summaryrefslogtreecommitdiff
path: root/usr/src/cmd/rpcsvc
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/rpcsvc
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/rpcsvc')
-rw-r--r--usr/src/cmd/rpcsvc/Makefile248
-rw-r--r--usr/src/cmd/rpcsvc/Makefile.rpc51
-rw-r--r--usr/src/cmd/rpcsvc/net_files/rpc94
-rw-r--r--usr/src/cmd/rpcsvc/nis/bin/Makefile66
-rw-r--r--usr/src/cmd/rpcsvc/nis/bin/nisadm.c1072
-rw-r--r--usr/src/cmd/rpcsvc/nis/bin/nisclient.sh1754
-rw-r--r--usr/src/cmd/rpcsvc/nis/bin/nisctl.c222
-rw-r--r--usr/src/cmd/rpcsvc/nis/bin/nisopaccess.sh298
-rw-r--r--usr/src/cmd/rpcsvc/nis/bin/nisping.c355
-rw-r--r--usr/src/cmd/rpcsvc/nis/bin/nispopulate.sh1576
-rw-r--r--usr/src/cmd/rpcsvc/nis/bin/nisserver.sh1728
-rw-r--r--usr/src/cmd/rpcsvc/nis/bin/nissetup.sh448
-rw-r--r--usr/src/cmd/rpcsvc/nis/bin/nisshowcache.c176
-rw-r--r--usr/src/cmd/rpcsvc/nis/bin/nisstat.c258
-rw-r--r--usr/src/cmd/rpcsvc/nis/bin/nisupdkeys.c548
-rw-r--r--usr/src/cmd/rpcsvc/nis/bin/req.flg28
-rw-r--r--usr/src/cmd/rpcsvc/nis/cachemgr/Makefile84
-rw-r--r--usr/src/cmd/rpcsvc/nis/cachemgr/cachemgr.c533
-rw-r--r--usr/src/cmd/rpcsvc/nis/cachemgr/cachemgr.h38
-rw-r--r--usr/src/cmd/rpcsvc/nis/cachemgr/req.flg28
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/Makefile133
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/configs/Makefile61
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/configs/NIS+LDAPmapping.template996
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/configs/rpc.nisd233
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/ldap_log.c146
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/log.h154
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_cleanup.c194
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_db.c3343
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_ib_proc.c1799
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_log_common.c1318
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_log_svc.c1076
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_main.c1385
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_mt.c159
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_mt.h162
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_multival.c857
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_ns_proc.c1038
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_opacc.c400
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_proc.h472
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_service.c656
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_subr_proc.c3347
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_thread_funcs.c619
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_xx_proc.c1936
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/nisinit.c746
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/nisldapmaptest.c389
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/nislog.c290
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/req.flg50
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv.c405
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_common.c74
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_common.h90
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/DNS_FWD84
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/Makefile74
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/main.c298
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nget_answer.c421
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/ngethostbyname.c536
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nres.h97
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nres_rcv.c154
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nres_search.c157
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nres_send.c166
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/prnt.h48
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/rpc_as.c327
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/rpc_as.h63
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/svc_run_as.c126
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/ypresolv_proc.c677
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/yp_ns_proc.c2312
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nisd/ypserv1.c153
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/Makefile80
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_cache.c215
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_cache.h69
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_svc.c1231
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_svcsubr.c1377
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_svcsubr.h79
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_ypfwd.c117
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/req.flg28
-rw-r--r--usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/rpc.nispasswdd.c553
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/Makefile173
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nis_bkrst.h89
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nis_util.c869
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nisaddcred/Makefile65
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makedescred.c288
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makedhextcred.c677
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makekerbcred.c47
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makelocalcred.c296
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makersacred.c47
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makesyscred.c191
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nisaddcred/nisaddcred.c742
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nisaddcred/nisaddcred.h44
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nisaddent.c5226
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nisauthconf.c223
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nisbackup.cc1159
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/niscat.c284
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nischmod.c339
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nisdefaults.c203
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/niserror.c63
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nisgrep.c376
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nisgrpadm.c442
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nisln.c181
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nisls.c269
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nismatch.c334
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nismkdir.c508
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nispath.c90
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nisprefadm.c1816
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nisrestore.cc1301
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nisrm.c166
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nisrmdir.c433
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nistbladm.c722
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nistest.c320
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/req.flg49
-rw-r--r--usr/src/cmd/rpcsvc/nisplus80
-rw-r--r--usr/src/cmd/rpcsvc/nisplus.xml85
-rw-r--r--usr/src/cmd/rpcsvc/rpc.bootparamd/Makefile63
-rw-r--r--usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_ip_route.c523
-rw-r--r--usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_private.h46
-rw-r--r--usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_prot_svc.c261
-rw-r--r--usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_prot_xdr.c151
-rw-r--r--usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_subr.c299
-rw-r--r--usr/src/cmd/rpcsvc/rpc.bootparamd/bootparams.xml113
-rw-r--r--usr/src/cmd/rpcsvc/rpc.rusersd.c484
-rw-r--r--usr/src/cmd/rpcsvc/rstat.xml112
-rw-r--r--usr/src/cmd/rpcsvc/rstat_main.c174
-rw-r--r--usr/src/cmd/rpcsvc/rstat_proc.c1095
-rw-r--r--usr/src/cmd/rpcsvc/rstat_v2.x86
-rw-r--r--usr/src/cmd/rpcsvc/rup.c537
-rw-r--r--usr/src/cmd/rpcsvc/rusers.c652
-rw-r--r--usr/src/cmd/rpcsvc/rusers.xml116
-rw-r--r--usr/src/cmd/rpcsvc/rwall.c265
-rw-r--r--usr/src/cmd/rpcsvc/rwall_subr.c119
-rw-r--r--usr/src/cmd/rpcsvc/spray.c180
-rw-r--r--usr/src/cmd/rpcsvc/spray.xml112
-rw-r--r--usr/src/cmd/rpcsvc/spray_subr.c80
-rw-r--r--usr/src/cmd/rpcsvc/wall.xml112
130 files changed, 65352 insertions, 0 deletions
diff --git a/usr/src/cmd/rpcsvc/Makefile b/usr/src/cmd/rpcsvc/Makefile
new file mode 100644
index 0000000000..34e3bf375b
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/Makefile
@@ -0,0 +1,248 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/rpcsvc/Makefile
+
+PROTOCOL_DIR= $(ROOT)/usr/include/rpcsvc
+
+BINPROG= rusers rup
+SBINPROG= rwall spray
+RPROG= rpc.rusersd
+SPROG= rpc.sprayd
+WPROG= rpc.rwalld
+TPROG= rpc.rstatd
+
+PROG= $(BINPROG) $(SBINPROG) $(RPROG) $(SPROG) $(WPROG) $(TPROG)
+
+MANIFEST= \
+ nisplus.xml \
+ rusers.xml \
+ spray.xml \
+ wall.xml \
+ rstat.xml
+
+SVCMETHOD= \
+ nisplus
+
+SCLNTOBJS= spray.o spray_clnt.o
+SSVCOBJS= spray_subr.o spray_svc.o
+WCLNTOBJS = rwall.o rwall_clnt.o
+WSVCOBJS= rwall_subr.o rwall_svc.o
+TSVCOBJS= rstat_main.o rstat_proc.o rstat_svc.o rstat_v2_svc.o rstat_v2_xdr.o
+
+DERIVED_FILES = rstat.x rstat.h rstat_svc.c rstat_v2.h rstat_v2_svc.c \
+ rstat_v2_xdr.c spray.x spray.h spray_clnt.c spray_svc.c \
+ rwall.x rwall.h rwall_svc.c rwall_clnt.c
+
+include ../Makefile.cmd
+
+ROOTMANIFESTDIR= $(ROOTSVCNETWORKRPC)
+$(ROOTMANIFEST) := FILEMODE= 444
+
+CPPFLAGS= -I. $(CPPFLAGS.master)
+LDLIBS += -lnsl
+rusers := CFLAGS += -v
+$(BINPROG) := LDLIBS += -lrpcsvc
+$(RPROG) := LDLIBS += -lrpcsvc
+$(SPROG) := LDLIBS += -lrpcsvc
+$(TPROG) := LDLIBS += -lrpcsvc
+spray := LDLIBS += -lrpcsvc
+
+CP= cp
+
+SUBDIRS= rpc.bootparamd nis/utils nis/bin nis/cachemgr nis/rpc.nisd \
+ nis/rpc.nispasswdd
+OBJS= $(SCLNTOBJS) $(SSVCOBJS) $(WCLNTOBJS) $(WSVCOBJS) $(TSVCOBJS)
+SRCS= $(OBJS:%.o=%.c) rusers.c rpc.rusersd.c rup.c
+
+ETCFILES= rpc
+
+TXTS= $(ETCFILES:%=net_files/%)
+
+NETSVC= $(ROOTLIB)/netsvc
+NIS = $(NETSVC)/nis
+RWALL= $(NETSVC)/rwall
+RUSERS= $(NETSVC)/rusers
+SPRAY= $(NETSVC)/spray
+RSTAT= $(NETSVC)/rstat
+ROOTVAR_NIS= $(ROOT)/var/nis
+
+ROOTDIRS= \
+ $(NETSVC) \
+ $(NIS) \
+ $(RWALL) \
+ $(RUSERS) \
+ $(SPRAY) \
+ $(RSTAT) \
+ $(ROOTVAR_NIS)
+
+IBINPROG= $(BINPROG:%=$(ROOTBIN)/%)
+ISBINPROG= $(SBINPROG:%=$(ROOTUSRSBIN)/%)
+IWPROG= $(WPROG:%=$(RWALL)/%)
+ISPROG= $(SPROG:%=$(SPRAY)/%)
+IRPROG= $(RPROG:%=$(RUSERS)/%)
+ITPROG= $(TPROG:%=$(RSTAT)/%)
+IETCFILES= $(ETCFILES:%=$(ROOTETC)/%)
+
+# non-default file attributes
+$(ROOTDIRS) := OWNER= root
+$(ROOTDIRS) := GROUP= sys
+
+$(ROOTETC)/rpc := FILEMODE= 0644
+$(ROOTETC)/rpc := OWNER= root
+$(ROOTETC)/rpc := GROUP= sys
+
+all:= TARGET= all
+install:= TARGET= install
+clean:= TARGET= clean
+clobber:= TARGET= clobber
+lint:= TARGET= lint
+
+rpc.rstatd:= LDLIBS += -lkstat
+rwall:= CPPFLAGS += -D_REENTRANT
+rpc.rwalld:= CPPFLAGS += -D_REENTRANT
+
+# install rules
+
+$(RWALL)/% $(SPRAY)/% $(RUSERS)/% $(RSTAT)/%: %
+ $(INS.file)
+
+$(ROOTETC)/%: ./net_files/%
+ $(INS.file)
+
+.KEEP_STATE:
+
+all: $(PROG) $(TXTS) $(SUBDIRS)
+
+# multi-object targets
+#
+spray: $(SCLNTOBJS)
+ $(LINK.c) $(SCLNTOBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+spray_svc.o spray_clnt.o: spray.h
+
+rpc.sprayd: $(SSVCOBJS)
+ $(LINK.c) $(SSVCOBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+rwall_svc.o rwall_clnt.o: rwall.h
+
+rwall: $(WCLNTOBJS)
+ $(LINK.c) $(WCLNTOBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+rpc.rwalld: $(WSVCOBJS)
+ $(LINK.c) $(WSVCOBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+rstat_main.o: rstat.h rstat_v2.h
+
+rstat_svc.o: rstat.h
+
+rstat_V2_svc.o rstat_V2_xdr.o: rstat_v2.h
+
+rpc.rstatd: $(TSVCOBJS)
+ $(LINK.c) $(TSVCOBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+install: $(PROG) $(TXTS) .WAIT $(ROOTDIRS) .WAIT $(IBINPROG) $(ISBINPROG) \
+ $(IWPROG) $(ISPROG) $(IRPROG) $(ITPROG) $(IETCFILES) $(SUBDIRS) \
+ $(ROOTMANIFEST) $(ROOTSVCMETHOD)
+
+$(ROOTDIRS):
+ $(INS.dir)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+#
+# Derived files
+#
+
+rstat.x: $(PROTOCOL_DIR)/rstat.x
+ $(RM) rstat.x
+ $(CP) $(PROTOCOL_DIR)/rstat.x rstat.x
+
+rstat.h: $(PROTOCOL_DIR)/rstat.h
+ $(RM) rstat.h
+ $(CP) $(PROTOCOL_DIR)/rstat.h rstat.h
+
+rstat_svc.c: rstat.x
+ $(RPCGEN) -C -m rstat.x > $@
+
+rstat_v2.h: rstat_v2.x
+ $(RPCGEN) -C -h rstat_v2.x > $@
+
+rstat_v2_svc.c: rstat_v2.x
+ $(RPCGEN) -C -m rstat_v2.x > $@
+
+rstat_v2_xdr.c: rstat_v2.x
+ $(RPCGEN) -c rstat_v2.x > $@
+
+spray.x: $(PROTOCOL_DIR)/spray.x
+ $(RM) spray.x
+ $(CP) $(PROTOCOL_DIR)/spray.x spray.x
+
+spray.h: $(PROTOCOL_DIR)/spray.h
+ $(RM) spray.h
+ $(CP) $(PROTOCOL_DIR)/spray.h spray.h
+
+spray_svc.c: spray.x
+ $(RPCGEN) -s datagram_v -s circuit_v spray.x > $@
+
+spray_clnt.c: spray.x
+ $(RPCGEN) -l spray.x > $@
+
+rwall.x: $(PROTOCOL_DIR)/rwall.x
+ $(RM) rwall.x
+ $(CP) $(PROTOCOL_DIR)/rwall.x rwall.x
+
+rwall.h: $(PROTOCOL_DIR)/rwall.h
+ $(RM) rwall.h
+ $(CP) $(PROTOCOL_DIR)/rwall.h rwall.h
+
+rwall_svc.c: rwall.x
+ $(RPCGEN) -A -s datagram_v rwall.x > $@
+
+rwall_clnt.c: rwall.x
+ $(RPCGEN) -l -M rwall.x > $@
+
+check: $(CHKMANIFEST)
+
+clean: $(SUBDIRS)
+ -$(RM) $(OBJS) $(DERIVED_FILES)
+
+clobber: clean $(SUBDIRS)
+ -$(RM) $(PROG) $(CLOBBERFILES)
+
+lint:
+ $(LINT.c) $(WCLNTOBJS:%.o=%.c) -lnsl -lrpcsvc
+ $(LINT.c) $(WSVCOBJS:%.o=%.c) -lnsl -lrpcsvc
+ $(LINT.c) rusers.c -lnsl -lrpcsvc
+
+FRC:
diff --git a/usr/src/cmd/rpcsvc/Makefile.rpc b/usr/src/cmd/rpcsvc/Makefile.rpc
new file mode 100644
index 0000000000..d1244aac94
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/Makefile.rpc
@@ -0,0 +1,51 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# cmd/rpcsvc/Makefile.rpc
+
+#
+# This Makefile is intended to be included by Makefiles describing
+# the build of server daemon services implementing RPC protocols.
+# It initializes the service manifest installation directory macro
+# ROOTMANISERVERRPC to the correct values for such services.
+#
+#
+
+include $(SRC)/cmd/Makefile.cmd
+
+ROOTMANIRPC= $(ROOTSVCNETWORK)/rpc
+
+ROOTMANIFESTDIR= $(ROOTMANIRPC)
+$(ROOTMANIFEST) := FILEMODE= 444
+
+$(ROOTMANIRPC):
+ $(INS.dir)
+
+$(ROOTMANIRPC)/%: %
+ $(INS.file)
+
+$(ROOTMANIFEST): $(ROOTMANIFESTDIR)
diff --git a/usr/src/cmd/rpcsvc/net_files/rpc b/usr/src/cmd/rpcsvc/net_files/rpc
new file mode 100644
index 0000000000..4fd1179b5a
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/net_files/rpc
@@ -0,0 +1,94 @@
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+# ident "%Z%%M% %I% %E% SMI" /* Svr4.0 1.2 */
+#
+# rpc
+#
+rpcbind 100000 portmap sunrpc rpcbind
+rstatd 100001 rstat rup perfmeter
+rusersd 100002 rusers
+nfs 100003 nfsprog
+ypserv 100004 ypprog
+mountd 100005 mount showmount
+ypbind 100007
+walld 100008 rwall shutdown
+yppasswdd 100009 yppasswd
+etherstatd 100010 etherstat
+rquotad 100011 rquotaprog quota rquota
+sprayd 100012 spray
+3270_mapper 100013
+rje_mapper 100014
+selection_svc 100015 selnsvc
+database_svc 100016
+rexd 100017 rex
+alis 100018
+sched 100019
+llockmgr 100020
+nlockmgr 100021
+x25.inr 100022
+statmon 100023
+status 100024
+ypupdated 100028 ypupdate
+keyserv 100029 keyserver
+bootparam 100026
+sunlink_mapper 100033
+tfsd 100037
+nsed 100038
+nsemntd 100039
+showfhd 100043 showfh
+ioadmd 100055 rpc.ioadmd
+NETlicense 100062
+sunisamd 100065
+debug_svc 100066 dbsrv
+bugtraqd 100071
+event 100101 na.event # SunNet Manager
+logger 100102 na.logger # SunNet Manager
+sync 100104 na.sync
+hostperf 100107 na.hostperf
+activity 100109 na.activity # SunNet Manager
+hostmem 100112 na.hostmem
+sample 100113 na.sample
+x25 100114 na.x25
+ping 100115 na.ping
+rpcnfs 100116 na.rpcnfs
+hostif 100117 na.hostif
+etherif 100118 na.etherif
+iproutes 100120 na.iproutes
+layers 100121 na.layers
+snmp 100122 na.snmp snmp-cmc snmp-synoptics snmp-unisys snmp-utk
+traffic 100123 na.traffic
+ktkt_warnd 100134
+nfs_acl 100227
+sadmind 100232
+gssd 100234
+nisd 100300 rpc.nisd
+nispasswd 100303 rpc.nispasswdd
+ufsd 100233 ufsd
+pcnfsd 150001
+ocfserv 100150
+metad 100229 metad
+metamhd 100230 metamhd
+metamedd 100242 metamedd
+smserverd 100155 smserverd
diff --git a/usr/src/cmd/rpcsvc/nis/bin/Makefile b/usr/src/cmd/rpcsvc/nis/bin/Makefile
new file mode 100644
index 0000000000..bbf74d862d
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/bin/Makefile
@@ -0,0 +1,66 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 1988-1992 Sun Microsystems Inc
+# All Rights Reserved.
+#
+# Makefile for NIS+ server utility commands
+#
+
+SCRIPTS= nissetup nisclient nisserver nispopulate nisopaccess
+PROG= nisstat nisshowcache nisupdkeys nisping nisctl $(SCRIPTS)
+# unsupported programs, which are built, but not installed
+UPROG= nisadm
+SRCS= nisadm.c nisctl.c nisstat.c nisshowcache.c nisupdkeys.c nisping.c
+OBJS= nisadm.o nisctl.o nisstat.o nisshowcache.o nisupdkeys.o nisping.o
+
+include ../../../Makefile.cmd
+
+ROOTNSLIB= $(ROOT)/usr/lib/nis
+ROOTNSPROG= $(PROG:%=$(ROOTNSLIB)/%)
+ROOTSCRIPTS= $(SCRIPTS:%=$(ROOTNSLIB)/%)
+
+CPPFLAGS += -I$(SRC)/lib/libnsl/include
+LDLIBS += -lnsl
+
+$(ROOTSCRIPTS) := FILEMODE = 755
+
+.KEEP_STATE:
+
+all: $(PROG) $(UPROG)
+
+install: all $(ROOTNSLIB) $(ROOTNSPROG)
+
+lint: lint_SRCS
+
+$(ROOTNSLIB):
+ $(INS.dir)
+
+$(ROOTNSLIB)/%: %
+ $(INS.file)
+
+clean:
+ -$(RM) $(OBJS)
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/rpcsvc/nis/bin/nisadm.c b/usr/src/cmd/rpcsvc/nis/bin/nisadm.c
new file mode 100644
index 0000000000..8c481d4a5f
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/bin/nisadm.c
@@ -0,0 +1,1072 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * nisadm.c
+ *
+ * Copyright (c) 1988-1992 Sun Microsystems Inc
+ * All Rights Reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Ported from SCCS version :
+ * "@(#)nisadm.c 1.14 90/12/12 Copyr 1990 Sun Micro";
+ *
+ * nisadm.c
+ *
+ * This utility is a shell interface to the NIS name service.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/nis.h>
+
+extern char *optarg;
+extern int optind, opterr;
+#define ZVAL zattr_val.zattr_val_val
+#define ZLEN zattr_val.zattr_val_len
+#define nilstring(s) ((s) ? (s) : "(nil)")
+
+#define LOWER(c) (isupper((c)) ? _tolower((c)) : (c))
+
+struct obj_defaults {
+ nis_name oname[512];
+ nis_attr attrs[NIS_MAXATTR];
+ table_col tcols[NIS_MAXCOLUMNS];
+ entry_col ecols[NIS_MAXCOLUMNS];
+ nis_server srvrs[NIS_MAXATTR];
+ nis_name grps[NIS_MAXATTR];
+ endpoint endp[NIS_MAXATTR];
+ char buffer[8192]; /* String space */
+};
+oar_mask masks[6] = {
+ {DEFAULT_RIGHTS, NIS_DIRECTORY_OBJ},
+ {DEFAULT_RIGHTS, NIS_GROUP_OBJ},
+ {DEFAULT_RIGHTS, NIS_LINK_OBJ},
+ {DEFAULT_RIGHTS, NIS_ENTRY_OBJ},
+ {DEFAULT_RIGHTS, NIS_TABLE_OBJ},
+ {0L, NIS_BOGUS_OBJ}
+};
+
+typedef struct obj_defaults obj_defaults;
+static obj_defaults *
+__get_obj_defaults()
+{
+ static obj_defaults *obd = NULL;
+
+ if (obd)
+ return (obd);
+
+ obd = (obj_defaults *)malloc(sizeof (obj_defaults));
+ if (obd == NULL)
+ return (obd);
+ memset((char *)obd, 0, sizeof (obj_defaults));
+
+ return (obd);
+}
+
+static char *
+ask(buf, buflen)
+ char **buf;
+ int *buflen;
+{
+ char *a, *s;
+
+ if (feof(stdin))
+ return (NULL);
+
+ a = *buf; /* Point a at the buffer */
+ fflush(stdin);
+ if (feof(stdin))
+ exit(0); /* Exit on EOF */
+ if (fgets(a, *buflen, stdin) == NULL)
+ return (NULL); /* return NULL on EOF */
+
+ s = a + strlen(a); /* Point s at the end of it */
+ *(s-1) = '\0'; /* Nul terminate the string */
+ *buf = s;
+ *buflen = *buflen - (strlen(a)+1);
+ return (a);
+}
+
+static char *
+getname(buf, len)
+ char **buf;
+ int *len;
+{
+ char *a, *s;
+ nis_name ldir = nis_local_directory();
+
+ if (feof(stdin))
+ return (NULL);
+
+ a = *buf; /* Point a at the buffer */
+ fflush(stdin);
+ if (fgets(a, *len, stdin) == NULL)
+ return (NULL); /* return NULL on EOF */
+
+ s = a + strlen(a); /* Point s at the end of it */
+ *(s-1) = '\0'; /* Nul terminate the string */
+ if (*(s-2) != '.') {
+ if (strlen(a)) /* don't prepend a dot on a null name */
+ strcat(a, ".");
+ strcat(a, ldir);
+ }
+ s = a + strlen(a) + 1;
+ *buf = s;
+ *len = *len - (strlen(a)+1);
+ return (a);
+}
+
+/*
+ * Convert two hex digits to one unsigned char byte.
+ */
+static u_char
+getbyte(bp)
+ u_char *bp;
+{
+ u_char nyb, val;
+
+ nyb = LOWER(*bp);
+ if (nyb >= 'a')
+ nyb = (nyb - 'a') + 10;
+ else
+ nyb = (nyb - '0');
+ if (nyb > 15)
+ fprintf(stderr, "Illegal character in binary data, '%c'\n",
+ *bp);
+ val = (nyb & 0xf) << 4;
+
+ nyb = LOWER(*(bp+1));
+ if (nyb >= 'a')
+ nyb = (nyb - 'a') + 10;
+ else
+ nyb = (nyb - '0');
+ if (nyb > 15)
+ fprintf(stderr, "Illegal character in binary data, '%c'\n",
+ *(bp+1));
+ val |= (nyb & 0xf);
+ return (val);
+}
+
+#define xask(x) { fflush(stdin); \
+ if (feof(stdin))\
+ return (NULL); \
+ fgets(x, 32, stdin); \
+ x[strlen(x)-1] = '\0'; }
+
+/*
+ * nis_get_object()
+ *
+ * This object will query the user for an object of the type specified
+ * in the 'type' variable. All of it's queries go to stdin.
+ */
+nis_object *
+nis_get_object(name, group, owner, rights, ttl, type)
+ char *name;
+ char *group;
+ char *owner;
+ u_long rights;
+ u_long ttl;
+ zotypes type;
+{
+ /*
+ * These variables define a string buffer array that is used
+ * while fetching the object, later the data is cloned so we
+ * can toss it. Yes, it's a giant hunk to put on the stack.
+ */
+#define BUFLENGTH 4096
+ char buffer[BUFLENGTH];
+ char *bp = &buffer[0]; /* For various uses */
+ int bl = BUFLENGTH;
+ char tmpval[32], *s; /* for fetching numbers and such */
+ char *t; /* temporary */
+ nis_object *obj, tmp;
+ int np, i, j;
+ u_long flags;
+ obj_defaults *ob_data = __get_obj_defaults();
+ link_obj *li;
+ directory_obj *di;
+ entry_obj *en;
+ group_obj *gr;
+ table_obj *ta;
+
+ memset((char *)&tmp, 0, sizeof (tmp));
+
+ printf("Creating NIS object... \n");
+ if (! name) {
+ printf("Enter objects name : ");
+ name = getname(&bp, &bl);
+ if (!name)
+ return (NULL);
+ }
+ s = (char *) nis_leaf_of(name);
+ if (s)
+ strcpy(bp, s);
+ else
+ strcpy(bp, "<none>");
+
+ tmp.zo_name = bp;
+ bl = bl - strlen(bp) - 1;
+ bp = bp + strlen(bp) + 1;
+
+ if (owner)
+ tmp.zo_owner = owner;
+ else
+ tmp.zo_owner = nis_local_principal();
+
+ if (group)
+ tmp.zo_group = group;
+ else
+ tmp.zo_group = nis_local_group();
+
+ if (rights)
+ tmp.zo_access = rights;
+ else
+ tmp.zo_access = DEFAULT_RIGHTS;
+
+ if (ttl)
+ tmp.zo_ttl = ttl;
+ else
+ tmp.zo_ttl = 24 * 3600;
+
+ /* name must be fully qualified */
+ tmp.zo_domain = nis_domain_of(name);
+ if (tmp.zo_domain == NULL)
+ tmp.zo_domain = ".";
+
+ if (type == NIS_BOGUS_OBJ) {
+ tmp.zo_data.zo_type = NIS_BOGUS_OBJ;
+ while (tmp.zo_data.zo_type == NIS_BOGUS_OBJ) {
+ printf("Enter the objects type : ");
+ xask(tmpval);
+ if (LOWER(tmpval[0]) == 'g')
+ tmp.zo_data.zo_type = NIS_GROUP_OBJ;
+ else if (LOWER(tmpval[0]) == 't')
+ tmp.zo_data.zo_type = NIS_TABLE_OBJ;
+ else if (LOWER(tmpval[0]) == 'd')
+ tmp.zo_data.zo_type = NIS_DIRECTORY_OBJ;
+ else if (LOWER(tmpval[0]) == 'e')
+ tmp.zo_data.zo_type = NIS_ENTRY_OBJ;
+ else if (LOWER(tmpval[0]) == 'l')
+ tmp.zo_data.zo_type = NIS_LINK_OBJ;
+ else if (LOWER(tmpval[0]) == 'p')
+ tmp.zo_data.zo_type = NIS_PRIVATE_OBJ;
+ }
+ } else
+ tmp.zo_data.zo_type = type;
+
+ switch (tmp.zo_data.zo_type) {
+ case NIS_GROUP_OBJ :
+ gr = &(tmp.GR_data);
+ printf("Enter flags value [IRN] ? ");
+ xask(tmpval);
+ gr->gr_flags = 0;
+ for (s = &tmpval[0]; *s; s++) {
+ if (LOWER(*s) == 'i')
+ gr->gr_flags |= IMPMEM_GROUPS;
+ else if (LOWER(*s) == 'r')
+ gr->gr_flags |= RECURS_GROUPS;
+ else if (LOWER(*s) == 'n')
+ gr->gr_flags |= NEGMEM_GROUPS;
+ else {
+ printf("Must be some combo of :\n");
+ printf("\tR = Recursive groups ok\n");
+ printf("\tN = Negative groups ok\n");
+ printf("\tI = Implicit groups ok\n");
+ exit(1);
+ }
+ }
+ do {
+ printf("Number of members in this group ? ");
+ xask(tmpval);
+ np = atoi(tmpval);
+ if ((np < 1) || (np > NIS_MAXATTR))
+ printf("Illegal number of members.\n");
+ } while ((np < 1) || (np > NIS_MAXATTR));
+ gr->gr_members.gr_members_len = np;
+ gr->gr_members.gr_members_val = &(ob_data->grps[0]);
+ for (i = 0; i < np; i++) {
+ printf("\tMember #%d name :\n", i);
+ ob_data->grps[i] = getname(&bp, &bl);
+ }
+ break;
+ case NIS_LINK_OBJ :
+
+ li = &(tmp.LI_data);
+ type = NIS_BOGUS_OBJ;
+ while (type == NIS_BOGUS_OBJ) {
+ printf(
+ "Linked object, enter real type [Group/Table/Entry] :");
+ xask(tmpval);
+ if (LOWER(tmpval[0]) == 'g')
+ type = NIS_GROUP_OBJ;
+ else if (LOWER(tmpval[0]) == 't')
+ type = NIS_TABLE_OBJ;
+ else if (LOWER(tmpval[0]) == 'd')
+ type = NIS_DIRECTORY_OBJ;
+ else if (LOWER(tmpval[0]) == 'e')
+ type = NIS_ENTRY_OBJ;
+ else
+ type = NIS_BOGUS_OBJ;
+ }
+ li->li_rtype = type;
+ do {
+ printf("Number of attributes in this name ? ");
+ xask(tmpval);
+ np = atoi(tmpval);
+ if ((np < 0) || (np > NIS_MAXATTR))
+ fprintf(stderr,
+ "Illegal number of attributes.\n");
+ } while ((np < 0) || (np > NIS_MAXATTR));
+ for (i = 0; i < np; i++) {
+ printf("Attribute #%d\n", i);
+ printf("\tAttribute Name : ");
+ ob_data->attrs[i].zattr_ndx = ask(&bp, &bl);
+ printf("\tAttribute Value : ");
+ ob_data->attrs[i].ZVAL = ask(&bp, &bl);
+ ob_data->attrs[i].ZLEN =
+ strlen(ob_data->attrs[i].ZVAL) + 1;
+ }
+ printf("Enter Linked name : ");
+ li->li_name = getname(&bp, &bl);
+ li->li_attrs.li_attrs_len = np;
+ li->li_attrs.li_attrs_val = &(ob_data->attrs[0]);
+ break;
+ case NIS_TABLE_OBJ :
+
+ ta = &(tmp.TA_data);
+ printf("Enter table type : ");
+ ta->ta_type = ask(&bp, &bl);
+ do {
+ printf("Number of columns in this table ? ");
+ xask(tmpval);
+ np = atoi(tmpval);
+ if ((np < 1) || (np > NIS_MAXCOLUMNS))
+ printf("Illegal number of columns.\n");
+ } while ((np < 1) || (np > NIS_MAXCOLUMNS));
+ ta->ta_maxcol = np;
+ printf("Enter separator character : ");
+ xask(tmpval);
+ ta->ta_sep = (tmpval[0] == '\0') ? ' ' : tmpval[0];
+ printf("Enter Search path : ");
+ ta->ta_path = ask(&bp, &bl);
+ for (i = 0; i < np; i++) {
+ int tmplen;
+
+ printf("Column #%d : \n", i+1);
+ printf("\tEnter Name : ");
+ ob_data->tcols[i].tc_name = ask(&bp, &bl);
+ ob_data->tcols[i].tc_rights = DEFAULT_RIGHTS;
+ printf("\tEnter Flags [S/C/B/X] : ");
+ xask(tmpval);
+ tmplen = strlen(tmpval);
+ for (j = 0, flags = 0; j < tmplen; j++) {
+ if (LOWER(tmpval[j]) == 'x')
+ flags |= TA_XDR;
+ else if (LOWER(tmpval[j]) == 'c')
+ flags |= TA_CASE;
+ else if (LOWER(tmpval[j]) == 'b')
+ flags |= TA_BINARY;
+ else if (LOWER(tmpval[j]) == 's')
+ flags |= TA_SEARCHABLE;
+ }
+ if ((flags & TA_SEARCHABLE) &&
+ (strlen(ob_data->tcols[i].tc_name) == 0)) {
+ fprintf(stderr,
+ "Can't have a searchable column with no name.\n");
+ return (NULL);
+
+ }
+ ob_data->tcols[i].tc_flags = flags;
+ }
+ ta->ta_cols.ta_cols_len = np;
+ ta->ta_cols.ta_cols_val = &(ob_data->tcols[0]);
+ break;
+ case NIS_DIRECTORY_OBJ :
+
+ di = &(tmp.DI_data);
+ printf("This new directory's name : ");
+ di->do_name = getname(&bp, &bl);
+ do {
+ printf(
+ "Number of servers for this directory? ");
+ xask(tmpval);
+ np = atoi(tmpval);
+ if ((np < 1) || (np > NIS_MAXATTR))
+ printf("Illegal number of servers.\n");
+ } while ((np < 1) || (np > NIS_MAXATTR));
+
+ for (i = 0; i < np; i++) {
+ if (i == 0)
+ printf("Enter Master server name :");
+ else
+ printf("Enter replicate server name :");
+ ob_data->srvrs[i].name = getname(&bp, &bl);
+ printf("Enter Universal Address : ");
+ ob_data->endp[i].uaddr = ask(&bp, &bl);
+ ob_data->endp[i].family = "INET";
+ ob_data->endp[i].proto = "TCP";
+ ob_data->srvrs[i].key_type = NIS_PK_NONE;
+ ob_data->srvrs[i].pkey.n_len = 0;
+ ob_data->srvrs[i].pkey.n_bytes = NULL;
+ ob_data->srvrs[i].ep.ep_val = &ob_data->endp[0];
+ ob_data->srvrs[i].ep.ep_len = 1;
+ }
+ di->do_servers.do_servers_len = np;
+ di->do_servers.do_servers_val = &ob_data->srvrs[0];
+ do {
+ printf("Enter ns type [Z/Y/D] :");
+ xask(tmpval);
+ if (LOWER(tmpval[0]) == 'z')
+ di->do_type = NIS;
+ else if (LOWER(tmpval[0]) == 'y')
+ di->do_type = SUNYP;
+ else if (LOWER(tmpval[0]) == 'd')
+ di->do_type = DNS;
+ else
+ di->do_type = IVY;
+ } while (di->do_type == IVY); /* XXX */
+ printf("Enter time to live value : ");
+ xask(tmpval);
+ di->do_ttl = atoi(tmpval);
+ if (! (di->do_ttl))
+ fprintf(stderr,
+ "Warning, ttl = 0 prohibits caching.\n");
+ di->do_armask.do_armask_len = 6;
+ di->do_armask.do_armask_val = &masks[0];
+ break;
+ case NIS_ENTRY_OBJ :
+ en = &(tmp.EN_data);
+ /* Presumed to be the same. */
+ printf("Enter entry type : ");
+ en->en_type = ask(&bp, &bl);
+ if (!en->en_type)
+ return (NULL);
+ do {
+ printf("Number of columns for this entry ? ");
+ xask(tmpval);
+ np = atoi(tmpval);
+ if ((np < 1) || (np > NIS_MAXCOLUMNS))
+ printf("Illegal number of columns.\n");
+ } while ((np < 1) || (np > NIS_MAXCOLUMNS));
+
+ for (i = 0; i < np; i++) {
+ entry_col *ec;
+ u_char *val;
+ int len;
+
+ ec = &(ob_data->ecols[i]);
+ printf("Column #%d :\n", i+1);
+ printf("\tEnter Flags [M/C/B/X] : ");
+ xask(tmpval);
+ len = strlen(tmpval);
+ for (j = 0, flags = 0; j < len; j++) {
+ if (LOWER(tmpval[j]) == 'x')
+ flags |= EN_XDR;
+ else if (LOWER(tmpval[j]) == 'c')
+ flags |= EN_CRYPT;
+ else if (LOWER(tmpval[j]) == 'b')
+ flags |= EN_BINARY;
+ else if (LOWER(tmpval[j]) == 'm')
+ flags |= EN_MODIFIED;
+ else
+ printf("** Unknown flag '%c'\n",
+ tmpval[j]);
+ }
+ ec->ec_flags = flags;
+ printf("\tValue : ");
+ val = (u_char *)ask(&bp, &bl);
+ len = strlen((char *) val)+1;
+ if (flags & EN_BINARY) {
+ for (j = 0; j < len; j += 2)
+ *(val + (j>>1)) =
+ getbyte(val+j);
+ len = len >> 1; /* Half the bytes */
+ }
+ ec->ec_value.ec_value_val = (char *)val;
+ ec->ec_value.ec_value_len = len;
+ }
+ en->en_cols.en_cols_len = np;
+ en->en_cols.en_cols_val = &(ob_data->ecols[0]);
+ break;
+ case NIS_PRIVATE_OBJ:
+ printf("Data length : ");
+ xask(tmpval);
+ i = atoi(tmpval);
+ tmp.zo_data.objdata_u.po_data.po_data_len = i;
+ tmp.zo_data.objdata_u.po_data.po_data_val = bp;
+ for (j = 0; j < i; j++) {
+ xask(tmpval);
+ *(bp+j) = getbyte(tmpval);
+ }
+ break;
+
+ }
+ obj = nis_clone_object(&tmp, NULL); /* Create a "clean" copy */
+
+ return (obj);
+}
+void
+usage(nm)
+ char *nm;
+{
+ fprintf(stderr,
+"usage : %s -C|a|A|r|R|m|p|l|L [-t G|E|D|L|T] [-S] [-o file] name\n", nm);
+}
+
+char *nis_errors[] = {
+ "SUCCESS",
+ "S_SUCCESS",
+ "NOTFOUND",
+ "S_NOTFOUND",
+ "CACHEEXPIRED",
+ "NAMEUNREACHABLE",
+ "UNKNOWNOBJ",
+ "TRYAGAIN",
+ "SYSTEMERROR",
+ "CHAINBROKEN",
+ "PERMISSION",
+ "NOTOWNER",
+ "NOT_ME",
+ "NOMEMORY",
+ "NAMEEXISTS",
+ "NOTMASTER",
+ "INVALIDOBJ",
+ "BADNAME",
+ "NOCALLBACK",
+ "CBRESULTS",
+ "NOSUCHNAME",
+ "NOTUNIQUE",
+ "IBMODERROR",
+ "NOSUCHTABLE",
+ "TYPEMISMATCH",
+ "LINKNAMEERROR",
+ "PARTIAL",
+ "TOOMANYATTRS",
+ "RPCERROR",
+ "BADATTRIBUTE",
+ "NOTSEARCHABLE",
+ "CBERROR",
+ "FOREIGNNS",
+ "BADOBJECT",
+ "NOTSAMEOBJ",
+ "MODFAIL",
+ "BADREQUEST",
+ "NOTEMPTY"
+};
+
+static char *
+text_of(s)
+ nis_error s;
+{
+ static char *bg = "BOGUS_ERROR";
+
+ if (s > NIS_NOTEMPTY)
+ return (bg);
+ else
+ return (nis_errors[s]);
+}
+
+
+void
+print_stats(r)
+ nis_result *r;
+{
+ fprintf(stderr, "%s:C=%d:A=%d:D=%d:S=%d:\n", text_of(r->status),
+ r->cticks, r->aticks, r->dticks, r->zticks);
+}
+
+enum op_todo {NOP, ADD, ADD_NS, REMOVE, REMOVE_OBJ, REM_DIR, MODIFY,
+ PRINT, LIST, CHECKPOINT, LOOKUP};
+XDR in_xdrs, out_xdrs;
+
+void
+put_obj(obj, f)
+ nis_object *obj;
+ int f;
+{
+ if (f)
+ nis_print_object(obj);
+ else
+ xdr_nis_object(&out_xdrs, obj);
+}
+
+nis_object *
+get_obj(name, type, f)
+ nis_name name;
+ zotypes type;
+ int f;
+{
+ nis_object *obj;
+ char *s;
+ int stat;
+
+ if (f) {
+ obj = nis_get_object(name, NULL, NULL, 0, 0, type);
+ stat = TRUE;
+ } else {
+ obj = (nis_object *)(calloc(1, sizeof (nis_object)));
+ stat = xdr_nis_object(&in_xdrs, obj);
+ if (! stat) {
+ free(obj);
+ obj = NULL;
+ } else if (name) {
+ /* We "rewrite" the name of the object. */
+ free(obj->zo_name);
+ obj->zo_name = strdup(nis_leaf_of(name));
+ free(obj->zo_domain);
+ obj->zo_domain = strdup(nis_domain_of(name));
+ }
+ }
+ /* FIXUP for some old objects */
+ if (obj) {
+ s = obj->zo_name;
+ if (s[strlen(s)-1] == '.')
+ s[strlen(s)-1] = '\0';
+ }
+
+ return (obj); /* will be NULL if undecodable */
+}
+
+/*
+ * Attempt to actually create the directories for a particular
+ * dir object.
+ */
+make_directory(obj)
+ nis_object *obj;
+{
+ directory_obj *da;
+ nis_server *srvs;
+ int i, ms;
+ nis_error status;
+
+ da = &(obj->DI_data);
+ ms = da->do_servers.do_servers_len;
+ srvs = da->do_servers.do_servers_val;
+ for (i = 0; i < ms; i++) {
+ printf("Attempting to create directory \"%s\"\n", da->do_name);
+ status = nis_mkdir(da->do_name, &(srvs[i]));
+ }
+}
+
+/*
+ * Attempt to actually remove the directories for a particular
+ * dir object.
+ */
+remove_dir(obj)
+ nis_object *obj;
+{
+ directory_obj *da;
+ nis_server *srvs;
+ int i, ms;
+ nis_error status;
+
+ da = &(obj->DI_data);
+ ms = da->do_servers.do_servers_len;
+ srvs = da->do_servers.do_servers_val;
+ for (i = 0; i < ms; i++) {
+ printf("Attempting to remove directory \"%s\"\n", da->do_name);
+ status = nis_rmdir(da->do_name, &(srvs[i]));
+ }
+}
+
+
+char name_buf[1024]; /* Temp buffer for names */
+/*
+ * Construct a legal NIS name from the object components.
+ * Uses static name buffer above.
+ */
+char *
+make_name(name, domain)
+ nis_name name;
+ nis_name domain;
+{
+
+ strcpy(name_buf, name);
+ if (domain[0] != '.')
+ strcat(name_buf, ".");
+ strcat(name_buf, domain);
+ if (name_buf[strlen(name_buf)-1] != '.')
+ strcat(name_buf, ".");
+ return (name_buf);
+}
+
+/*
+ * Main code for the nisadm command
+ */
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ enum op_todo op = PRINT; /* Operation to perform */
+ zotypes obj_type = 0; /* Object "type" to use */
+ ib_request req; /* Request to use */
+ int stats = FALSE; /* Print statistics */
+ u_long flags = 0, /* Lookup flags */
+ err = 0; /* errors encountered */
+ int interact = FALSE, /* Interactive input */
+ prettyprint = FALSE; /* Print in ASCII text */
+ char *s, /* Some temporaries */
+ *name, /* NIS name to look up */
+ *rname; /* name to look up */
+ int i, j; /* More temporaries */
+ nis_object *obj; /* Working object */
+ char real_name[1024]; /* discovered name */
+ nis_result *res; /* Operation result */
+ int c;
+
+ if (strcmp(argv[0], "zadd") == 0)
+ op = ADD;
+ else if (strcmp(argv[0], "zrem") == 0)
+ op = REMOVE;
+ else if (strcmp(argv[0], "zmod") == 0)
+ op = MODIFY;
+ else if (strcmp(argv[0], "zlist") == 0)
+ op = LIST;
+ else if (strcmp(argv[0], "zdump") == 0) {
+ op = LIST;
+ prettyprint = TRUE;
+ }
+
+
+ while ((c = getopt(argc, argv, "CARLarmpit:SlF:")) != -1) {
+ switch (c) {
+ case 'a' :
+ op = ADD;
+ break;
+ case 'A' :
+ op = ADD_NS;
+ break;
+ case 'R' :
+ op = REMOVE_OBJ;
+ break;
+ case 'r' :
+ op = REMOVE;
+ break;
+ case 'm' :
+ op = MODIFY;
+ break;
+ case 'L' :
+ op = LIST;
+ break;
+ case 'l' :
+ op = LOOKUP;
+ break;
+ case 'p' :
+ prettyprint = TRUE;
+ break;
+ case 'i' :
+ interact = TRUE;
+ break;
+ case 'S' :
+ stats = TRUE;
+ break;
+ case 'C' :
+ op = CHECKPOINT;
+ break;
+ case 'F' :
+ for (s = optarg; *s; s++) {
+ switch (*s) {
+ case 'h' :
+ flags |= HARD_LOOKUP;
+ break;
+ case 'f' :
+ flags |= FOLLOW_LINKS;
+ break;
+ case 'p' :
+ flags |= FOLLOW_PATH;
+ break;
+ case 'c' :
+ flags |= NO_CACHE;
+ break;
+ case 'a' :
+ flags |= ALL_RESULTS;
+ break;
+ case 'm' :
+ flags |= MASTER_ONLY;
+ break;
+ default :
+ fprintf(stderr, "bad flag\n");
+ err++;
+ }
+ }
+ break;
+ case 't':
+ switch (*optarg) {
+ case 'G' :
+ obj_type = NIS_GROUP_OBJ;
+ break;
+ case 'D' :
+ obj_type = NIS_DIRECTORY_OBJ;
+ break;
+ case 'T' :
+ obj_type = NIS_TABLE_OBJ;
+ break;
+ case 'E' :
+ obj_type = NIS_ENTRY_OBJ;
+ break;
+ case 'L' :
+ obj_type = NIS_LINK_OBJ;
+ break;
+ default :
+ fprintf(stderr,
+ "unknown object type\n");
+ err++;
+ break;
+ }
+ break;
+ case '?' :
+ usage(argv[0]);
+ exit(1);
+ default :
+ err++;
+ break;
+ }
+ }
+ for (name = NULL; optind < argc; optind++) {
+ if ((name) || (*argv[optind] == '-')) {
+ fprintf(stderr, "Extra input beyond name.\n");
+ err++;
+ } else
+ name = argv[optind];
+ }
+
+ if (err || name == NULL) {
+ usage(argv[0]);
+ exit(1);
+ }
+
+ if (name[strlen(name) - 1] != '.') {
+ res = nis_lookup(name, EXPAND_NAME);
+ if (res->status != NIS_SUCCESS) {
+ fprintf(stderr, "unable to locate \"%s\"\n", name);
+ exit(1);
+ }
+ sprintf(real_name, "%s.%s", res->objects.objects_val->zo_name,
+ res->objects.objects_val->zo_domain);
+ name = real_name;
+ nis_freeresult(res);
+ }
+
+ if (! interact)
+ xdrstdio_create(&in_xdrs, stdin, XDR_DECODE);
+
+ if (! prettyprint)
+ xdrstdio_create(&out_xdrs, stdout, XDR_ENCODE);
+
+ switch (op) {
+ case ADD :
+ case ADD_NS :
+ while ((obj = get_obj(name, obj_type, interact))) {
+
+ rname = make_name(obj->zo_name, obj->zo_domain);
+ if ((__type_of(obj) == NIS_ENTRY_OBJ) &&
+ (op == ADD))
+ res = nis_add_entry(rname, obj, 0);
+ else
+ res = nis_add(rname, obj);
+ if (stats)
+ print_stats(res);
+ if (res->status != NIS_SUCCESS) {
+ if (!stats)
+ print_stats(res);
+ err = res->status;
+ if (__type_of(obj) != NIS_ENTRY_OBJ)
+ break;
+ }
+ if (__type_of(obj) == NIS_DIRECTORY_OBJ)
+ make_directory(obj);
+
+ nis_destroy_object(obj);
+ }
+ break;
+
+ case REMOVE_OBJ :
+ if (interact) {
+ fprintf(stderr,
+ "This option cannot use user typed in objects.\n");
+ break;
+ }
+ while ((obj = get_obj(name, obj_type, 0))) {
+ rname = make_name(obj->zo_name, obj->zo_domain);
+ if (__type_of(obj) == NIS_ENTRY_OBJ)
+ res = nis_remove_entry(rname, obj, 0);
+ else
+ res = nis_remove(rname, obj);
+ if (stats)
+ print_stats(res);
+ if (res->status != NIS_SUCCESS) {
+ if (!stats)
+ print_stats(res);
+ nis_print_object(obj);
+ err = res->status;
+ }
+ nis_destroy_object(obj);
+ }
+ break;
+
+ case REMOVE :
+ if (name == NULL) {
+ fprintf(stderr,
+ "Name required for remove operation.\n");
+ exit(1);
+ }
+ strcpy(name_buf, name);
+ if (name_buf[strlen(name_buf)-1] != '.')
+ strcat(name_buf, ".");
+
+ if (obj_type == NIS_DIRECTORY_OBJ) {
+ res = nis_lookup(name_buf, 0);
+ if (NIS_RES_STATUS(res) != NIS_SUCCESS) {
+ err = res->status;
+ break;
+ }
+ obj = nis_clone_object(NIS_RES_OBJECT(res),
+ NULL);
+ nis_freeresult(res);
+ res = nis_remove(name_buf, obj);
+ if (NIS_RES_STATUS(res) == NIS_SUCCESS) {
+ remove_dir(obj);
+ }
+ nis_destroy_object(obj);
+ if ((stats) || (NIS_RES_STATUS(res)
+ != NIS_SUCCESS))
+ print_stats(res);
+ err = NIS_RES_STATUS(res);
+ nis_freeresult(res);
+ break;
+ }
+ nis_get_request(name_buf, NULL, NULL, &req);
+ if (req.ibr_srch.ibr_srch_len)
+ res = nis_remove_entry(name_buf, NULL, 0);
+ else
+ res = nis_remove(name_buf, NULL);
+ nis_free_request(&req);
+ if ((stats) || (NIS_RES_STATUS(res) != NIS_SUCCESS))
+ print_stats(res);
+ if (res->status != NIS_SUCCESS)
+ err = res->status;
+ break;
+
+ case MODIFY :
+ while ((obj = get_obj(name, obj_type, interact))) {
+ rname = make_name(obj->zo_name, obj->zo_domain);
+ if (__type_of(obj) == NIS_ENTRY_OBJ)
+ res = nis_modify_entry(rname, obj, 0);
+ else
+ res = nis_modify(rname, obj);
+ if (stats)
+ print_stats(res);
+ if (res->status != NIS_SUCCESS) {
+ if (!stats)
+ print_stats(res);
+ err = res->status;
+ }
+ nis_destroy_object(obj);
+ }
+ break;
+
+ case LOOKUP :
+ case LIST :
+ if (name == NULL) {
+ fprintf(stderr,
+ "Name required for List or Lookup operation.\n");
+ exit(1);
+ }
+ strcpy(name_buf, name);
+ if (name_buf[strlen(name_buf)-1] != '.')
+ strcat(name_buf, ".");
+
+ nis_get_request(name_buf, NULL, NULL, &req);
+ if ((op == LIST) || (req.ibr_srch.ibr_srch_len != 0))
+ res = nis_list(name_buf, flags, NULL, NULL);
+ else
+ res = nis_lookup(req.ibr_name, flags);
+ nis_free_request(&req);
+ if (res->status == NIS_SUCCESS) {
+ if (stats)
+ print_stats(res);
+ for (i = 0; i < res->objects.objects_len; i++) {
+ put_obj((res->objects.objects_val)+ i,
+ prettyprint);
+ }
+ }
+ if (stats)
+ print_stats(res);
+
+ if (res->status != NIS_SUCCESS) {
+ if (!stats)
+ print_stats(res);
+ err = res->status;
+ break;
+ }
+ break;
+
+ case PRINT :
+ while ((obj = get_obj(name, obj_type, interact))) {
+ put_obj(obj, prettyprint);
+ nis_destroy_object(obj);
+ }
+ break;
+
+ case CHECKPOINT :
+ if (! name) {
+ fprintf(stderr,
+ "Name required for Checkpoint operation.");
+ err++;
+ break;
+ }
+ strcpy(name_buf, name);
+ if (name_buf[strlen(name_buf)-1] != '.')
+ strcat(name_buf, ".");
+
+ res = nis_checkpoint(name_buf);
+ if (stats)
+ print_stats(res);
+ if (res->status != NIS_SUCCESS) {
+ if (!stats)
+ print_stats(res);
+ err = res->status;
+ }
+ break;
+
+ default :
+ fprintf(stderr, "Unknown operation requested.\n");
+ err = -1;
+ break;
+
+ }
+
+ if (! interact)
+ xdr_destroy(&in_xdrs);
+ else
+ printf("\n");
+
+ if (! prettyprint)
+ xdr_destroy(&out_xdrs);
+
+ exit(err);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/bin/nisclient.sh b/usr/src/cmd/rpcsvc/nis/bin/nisclient.sh
new file mode 100644
index 0000000000..273e107b5e
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/bin/nisclient.sh
@@ -0,0 +1,1754 @@
+#!/bin/sh
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# nisclient -- script to setup NIS+ clients
+
+#
+# print_usage(): ask user if they want to see detailed usage msg.
+#
+print_usage()
+{
+ echo
+ get_yesno " Do you want to see more information on this command? \\n\
+ (type 'y' to get a detailed description, 'n' to exit)"
+ if [ $ANS = "n" -o $ANS = "N" ]
+ then
+ echo
+ return 1
+ else
+ print_more
+ fi
+ exit 1
+}
+
+
+#
+# print_more(): print the usage message.
+#
+print_more()
+{
+ more << EOF
+USAGE:
+ o to create credentials for NIS+ client or NIS+ principals:
+ $PROG -c [-o] [-v] [-x] [-l <NIS+_password>] [-d <NIS+_domain>]
+ <name> ...
+
+ o to initialize NIS+ client machines:
+ $PROG -i [-S 0|2] [-v] [-x] -h <NIS+_server_host>
+ [-a <NIS+_server_addr>] [-d <NIS+_domain>]
+
+ o to initialize NIS+ users:
+ $PROG -u [-v] [-x]
+
+ o to restore the network service environment:
+ $PROG -r
+
+OPTIONS:
+ -a <NIS+_server_addr>
+ specifies the IP address for the NIS+ server. This option is
+ *ONLY* used with the "-i" option.
+
+ -c adds DES credentials for NIS+ principals.
+
+ -d <NIS+_domain>
+ specifies the NIS+ domain where the credential should be created
+ when used in conjuction with the -c option. It specifies the
+ name for the new NIS+ domain when used in conjuction with the
+ -i option. The default is your current domain name.
+
+ -h <NIS+_server_host>
+ specifies the NIS+ server's hostname. This option is *ONLY*
+ used with the "-i" option.
+
+ -i initializes a NIS+ client machine. Also see the -S option.
+
+ -l <network_password>
+ specifies the network password for the clients. This option is
+ *ONLY* used with the "-c" option. If this option is not specified,
+ this script will prompt you for the network password.
+
+ -o overwrite existing credential entries. The default is not
+ to overwrite. This is *ONLY* used with the "-c" option.
+
+ -r restores the network service environment.
+
+ -S 0|2
+ specifies the authentication level for the NIS+ client. Level 0 is
+ for unauthenticated clients and level 2 is for authenticated (DES)
+ clients. The default is to set up with level 2 authentication.
+ This option is *ONLY* used with -i option. nisclient always uses
+ level 2 authentication (DES) for both -c and -u options. There is
+ no need to run nisclient with -u and -c for level 0 authentication.
+
+ -u initializes a NIS+ user.
+
+ -v runs this script in verbose mode.
+
+ -x turns the "echo" mode on. This script just prints the commands
+ that it would have executed. The commands are printed with
+ leading "+++". Note that the commands are not actually executed.
+ The default is off.
+
+EOF
+}
+
+
+print_ciru_usage()
+{
+ if [ "$ERRciru_OPTION" = "$ciru_OPTION" ]
+ then
+ echo "**WARNING: You have specified the '$ciru_OPTION' option twice."
+ return 0
+ fi
+ echo
+ echo "**ERROR: You have specified the '$ERRciru_OPTION' option after"
+ echo " having selected the '$ciru_OPTION' option."
+ echo "Please select only one of these options: '-c', '-i', '-r' or '-u'."
+ print_usage
+ exit 1
+}
+
+
+#^L
+# Generic Routines:
+# -----------------
+#
+# This section contains general routines.
+# get_ans() - prompts the message and waits for an input
+# get_yesno() - prompts the message and waits for a Y or N answer
+# tolower(): converts upper to lower case.
+#
+
+#
+# get_ans(): gets an answer from the user.
+# $1 instruction/comment/description/question
+# $2 default value
+#
+get_ans()
+{
+ if [ -z "$2" ]
+ then
+ echo "$1 \c"
+ else
+ echo "$1 [$2] \c"
+ fi
+ read ANS
+ if [ -z "$ANS" ]
+ then
+ ANS=$2
+ fi
+}
+
+
+########## get_yesno constants:
+##
+## There are two very common phrases passed to get_yesno:
+## These have been parameterized to provide "ease of use".
+## Thus, there are three message "types" which are possible:
+## --$CONTINUE: "Do you want to continue? (type 'y' to continue, 'n' to exit this script)"
+## --$CONFIRM: "Is this information correct? (type 'y' to accept, 'n' to change)"
+## --actual string is passed.
+##
+## If the message is $CONTINUE, get_yesno will exit if the response is no.
+##
+###########
+CONTINUE=2
+CONFIRM=1
+#
+# get_yesno(): get the yes or no answer.
+# $1 message type or message.
+#
+#
+#
+get_yesno()
+{
+ ANS="X"
+
+ case $1 in
+ $CONTINUE )
+ INFOTEXT="Do you want to continue? (type 'y' to continue, 'n' to exit this script)"
+ ;;
+ $CONFIRM )
+ INFOTEXT="Is this information correct? (type 'y' to accept, 'n' to change)"
+ ;;
+ *) INFOTEXT="$1"
+ ;;
+ esac
+
+ while [ "$ANS" != "y" -a "$ANS" != "n" -a "$ANS" != "Y" -a "$ANS" != "N" ]
+ do
+ get_ans "$INFOTEXT" ""
+ done
+
+ if [ "$1" = "$CONTINUE" ]; then
+ if [ $ANS = "n" -o $ANS = "N" ]
+ then
+ exit
+ fi
+ fi
+
+}
+
+
+#
+# tolower(): converts upper to lower case.
+# $1 string to convert
+#
+tolower()
+{
+ echo "$1" | tr '[A-Z]' '[a-z]'
+}
+
+
+#
+# smf(5) routines
+# restart_instance() - restart instance or enable if not enabled
+#
+
+#
+# restart_instance [-t] instance_fmri
+#
+restart_instance() {
+ if [ "$1" = "-t" ]; then
+ flag=-t
+ shift
+ else
+ flag=
+ fi
+
+ if [ "`/usr/bin/svcprop -p restarter/state $1`" = "disabled" ];
+ then
+ /usr/sbin/svcadm enable $flag $1
+ else
+ /usr/sbin/svcadm restart $1
+ fi
+}
+
+
+#^L
+# Common Routines:
+# ---------------
+#
+# This section contains common routines for the script.
+# init() - initializes all the variables
+# parse_arg() - parses the command line arguments
+#
+
+#
+# init(): initializes variables and options
+#
+init()
+{
+
+ VERB='> /dev/null'
+ PROG=`basename $0`
+ ECHO="eval"
+ BACKUP=no_nisplus
+ DOM=`nisdefaults -d`
+ NODOT=`echo $DOM | sed -e "s/\.$//"`
+ SEC_DOM_NODOT=""
+ SEC_DOM_DOT=""
+ SEC=x
+ PAS=""
+ GEN=FALSE
+ DEFSEC=x
+ OWRITE="false"
+ HOST=
+ ADDR=
+
+ OS=`uname -r | cut -d. -f1`
+ OSVER=`uname -r | cut -d. -f2`
+
+ #
+ # The following files need special handling:
+ # - they must be backed up and removed before initializing the client.
+ # These files are then automatically generated by nisinit and chkey
+ # during the initialization process.
+ # - during the restore process, these files must be removed if the
+ # backup files are not found.
+ #
+ DEL_REST_FILES="/var/nis/NIS_COLD_START /var/nis/client_info /etc/.rootkey"
+
+ #
+ # The following file needs to be removed before the initialization
+ # and restoring process. There is no need to backup this file because
+ # this file is generated by the nis_cachemgr automatically.
+ #
+ DELETE_FILES="/var/nis/NIS_SHARED_DIRCACHE /var/nis/.pref_servers"
+
+ #
+ # RESTORE_FILES variable contains a list of files that need to
+ # be restored if the backup file exists. Otherwise, the current
+ # file should be left alone.
+ #
+ # May have to restore the /etc/inet/ipnodes file if the client is
+ # initialized with an IPv6 address for the NIS+ server.
+ #
+ RESTORE_FILES="/etc/defaultdomain $HOSTS_FILE"
+
+ #
+ # REST_DOM_DIR variable is the same as RESTORE_FILES except for
+ # directories. Specifically, this is for directories which are
+ # *not* generated by this script, rather just need renaming to
+ # avoid conflicts in a NIS+ client environment.
+ #
+ REST_DOM_DIR="/var/yp/binding/$NODOT"
+
+ #
+ # The following section is needed for 4.x support.
+ #
+ # Again, /etc/inet/ipnodes file may need to be added in the future.
+
+ HOSTS_FILE=/etc/inet/hosts
+ PATH=/usr/lib/nis:/usr/sbin:/usr/bin:/usr/lib/netsvc/yp:$PATH; export PATH
+ RESTORE_FILES="$RESTORE_FILES /etc/nsswitch.conf"
+ YPPROC="ypbind -broadcast"
+}
+
+
+#
+# parse_arg(): parses the input arguments.
+# It returns the number to be shift in the argument list.
+#
+parse_arg()
+{
+ while getopts "a:cd:g:h:ik:l:orS:uvx" ARG
+ do
+ case $ARG in
+ a) ADDR=$OPTARG;;
+ c) if [ -z "$ACTION" ]
+ then
+ ACTION="create"
+ ciru_OPTION="-c"
+ else
+ ERRciru_OPTION="-c"
+ print_ciru_usage
+ fi;;
+ d) if [ "`echo $OPTARG | sed -e 's/.*\(.\)$/\1/'`" != "." ]
+ then
+ NODOT=$OPTARG
+ DOM=${NODOT}.
+ else
+ DOM=$OPTARG
+ NODOT=`echo $DOM | sed -e "s/\.$//"`
+ fi
+ REST_DOM_DIR="/var/yp/binding/$NODOT";;
+ g) GEN="TRUE"
+ GENFILE=$OPTARG;;
+ h) HOST=$OPTARG;;
+ i) if [ -z "$ACTION" ]
+ then
+ ACTION="init"
+ ciru_OPTION="-i"
+ else
+ ERRciru_OPTION="-i"
+ print_ciru_usage
+ fi;;
+ k) if [ "`echo $OPTARG | sed -e 's/.*\(.\)$/\1/'`" != "." ]
+ then
+ SEC_DOM_NODOT=$OPTARG
+ SEC_DOM_DOT=${NODOT}.
+ else
+ SEC_DOM_DOT=$OPTARG
+ SEC_DOM_NODOT=`echo $SEC_DOM_DOT | sed -e "s/\.$//"`
+ fi;;
+ l) PAS=$OPTARG;;
+ o) OWRITE="";;
+ r) if [ -z "$ACTION" ]
+ then
+ ACTION="restore"
+ ciru_OPTION="-r"
+ else
+ ERRciru_OPTION="-r"
+ print_ciru_usage
+ fi;;
+ S) VALUE=`expr "$OPTARG" : "\([02]\)"`
+ if [ -z "$VALUE" ]
+ then
+ echo "**ERROR: invalid security level."
+ echo " It must be either 0 or 2."
+ exit 1
+ fi
+ SEC=$VALUE;;
+ v) VERB="";;
+ u) if [ -z "$ACTION" ]
+ then
+ ACTION="user"
+ ciru_OPTION="-u"
+ else
+ ERRciru_OPTION="-u"
+ print_ciru_usage
+ fi;;
+ x) ECHO="echo +++";;
+ \?) print_usage
+ exit 1;;
+ *) echo "**ERROR: Should never get to this point!!!!!"
+ print_usage
+ exit 1;;
+ esac
+ done
+ return `expr $OPTIND - 1`
+}
+
+
+#^L
+# Common Routines used by -c and -i:
+# ----------------------------------
+#
+# This section contains routines to setup a client.
+# setup_backup() - sets up the the backup files
+# setup_domain() - sets up the domain
+# setup_server() - sets up the server information
+# setup_switch() - sets up the switch
+# setup_security()- sets up the local keys
+# do_nisinit() - runs the nisinit command
+# rm_servdata() - deletes /var/nis/data, /var/nis/trans.log &
+# /var/nis/data.dict*
+#
+
+#
+# setup_backup(): backup all the files specfied in variable $RESTORE_FILES
+# to <file>.$BACKUP and save a temporary copy of previous backup.
+#
+setup_backup()
+{
+ eval "echo setting up backup files... $VERB"
+
+ trap restore_tmp 2
+
+ # ... save a copy of current backup copies
+ for FILE in $RESTORE_FILES $DEL_REST_FILES
+ do
+ if [ -f $FILE.$BACKUP ]
+ then
+ $ECHO mv $FILE.$BACKUP $FILE.$BACKUP.$$ > /dev/null &
+ fi
+ done
+
+ # ... save a copy of these special files: coldstart and rootkey
+ for FILE in $DEL_REST_FILES
+ do
+ if [ -f $FILE ]
+ then
+ eval "echo saving a copy of $FILE... $VERB"
+ $ECHO cp $FILE $FILE.$BACKUP
+ fi
+ done
+
+ trap restore_here 2
+}
+
+
+#
+# setup_domain(): sets up "domainname" and "/etc/defaultdomain" with the specified
+# domain information.
+#
+setup_domain()
+{
+ echo "setting up domain information \"$DOM\"..."
+ if [ `nisdefaults -d` != $DOM ]
+ then
+ # NODOT is used to support 4.x YP clients
+ $ECHO domainname $NODOT
+ fi
+
+ # ... we need to check the /etc/defaultdomain file separately.
+ # We cannot assume that the /etc/defaultdomain is set to the
+ # same value as the domainname is.
+ #
+ DEFDOM=`sed -n -e "1p" /etc/defaultdomain`
+ if [ "$DEFDOM" != $NODOT ]
+ then
+ $ECHO "cp /etc/defaultdomain /etc/defaultdomain.$BACKUP > /dev/null"
+ $ECHO "domainname > /etc/defaultdomain"
+ fi
+ echo ""
+
+ # ... save a copy of current backup copies just like for
+ # $RESTORE_FILES and $DEL_REST_FILES, but since this script
+ # does not generate a new version of the directories, we
+ # just rename it, and restore later if necessary.
+ for FILE in $REST_DOM_DIR
+ do
+ if [ -d $FILE ]
+ then
+ if [ -d $FILE.$BACKUP ]
+ then
+ $ECHO mv -f $FILE.$BACKUP $FILE.$BACKUP.$$ > /dev/null &
+ fi
+ $ECHO mv -f $FILE $FILE.$BACKUP > /dev/null &
+ fi
+ done
+}
+
+
+
+#
+# setup_server(): adds server information into /etc/hosts file.
+# Initializes $ADDR & $HOST (addr & name of place to get COLDSTART file)
+#
+# Will have to modify this function to account for IPV6 addresses being
+# passed as the server address (when the NIS+ servers start running over IPV6).
+
+setup_server()
+{
+ eval "echo setting up NIS+ server information... $VERB"
+ while [ -z "$HOST" ]
+ do
+ get_ans "Type server's hostname:" ""
+ HOST=$ANS
+ done
+ # Parse $HOSTS_FILE, first kill all comments,
+ # then check for $HOST as a word by itself,
+ # then take just the first such line.
+ # Characters within [] are <space> and <tab>.
+ ENTRY=`sed -e 's/#.*$//' $HOSTS_FILE | \
+ awk '/[ ]'$HOST'([ ]|$)/ \
+ { print $0; exit}'`
+ if [ "$ENTRY" ]
+ then
+ if [ -z "$ADDR" ]
+ then
+ eval "echo $HOST is already in the /etc/hosts file. $VERB"
+ echo ""
+ ADDR=`echo $ENTRY | awk '{print $1}'`
+ return
+ else
+ OLD_ADDR=`echo $ENTRY | awk '{print $1}'`
+ $ECHO "cp $HOSTS_FILE $HOSTS_FILE.$BACKUP"
+ $ECHO "sed -e \"/$ENTRY/s/^$OLD_ADDR/$ADDR/\" \
+ $HOSTS_FILE > /tmp/hosts.$$"
+ $ECHO mv /tmp/hosts.$$ $HOSTS_FILE > /dev/null
+ return
+ fi
+ elif [ -z "$ADDR" ]
+ then
+ DEFADDR=""
+ zone=`/usr/bin/zonename`
+ PROC=`/usr/bin/pgrep -z $zone ypbind`
+ if [ ! -z "$PROC" ]
+ then
+ # ... try yp maps
+ DEFADDR=`ypmatch $HOST hosts 2> /dev/null | cut -d' ' -f1`
+ else
+ # ... try nisplus tables
+ if [ -f /var/nis/NIS_COLD_START ]
+ then
+ DEFADDR=`nismatch -P $HOST hosts.org_dir 2> /dev/null | cut -d' ' -f3`
+ fi
+ fi
+ while [ -z "$ADDR" ]
+ do
+ get_ans "Type server ${HOST}'s IP address:" "$DEFADDR"
+ ADDR=$ANS
+ done
+ fi
+
+ $ECHO "cp $HOSTS_FILE $HOSTS_FILE.$BACKUP"
+ $ECHO "echo $ADDR $HOST >> $HOSTS_FILE"
+ eval "echo $VERB"
+}
+
+
+#
+# setup_switch(): copies the nisplus switch configuration file to
+# nsswitch.conf.
+#
+setup_switch()
+{
+ echo "setting up the name service switch information..."
+ $ECHO "/bin/rm -f /etc/.rootkey 2> /dev/null"
+ diff /etc/nsswitch.conf /etc/nsswitch.nisplus > /dev/null
+ if [ $? -eq 0 ]
+ then
+ eval "echo The switch configuration file is already set to use NISPLUS. $VERB"
+ echo ""
+ restart_instance network/rpc/keyserv:default
+ return
+ fi
+
+ $ECHO "cp /etc/nsswitch.conf /etc/nsswitch.conf.$BACKUP"
+ $ECHO "cp /etc/nsswitch.nisplus /etc/nsswitch.conf"
+
+ restart_instance network/rpc/keyserv:default
+ echo ""
+}
+
+
+#
+# print_passwd_request()
+# Utility print routine for setup_security & old_setup_security
+# Made a routine to provide consistency in outputting messages,
+# and so only 1 place has to be changed.
+# $1 is the same as $1 for the calling routine.
+#
+print_passwd_request()
+{
+ eval "echo setting up security information for $1... $VERB"
+ echo "At the prompt below, type the network password (also known"
+ echo "as the Secure-RPC password) that you obtained either"
+ echo "from your administrator or from running the nispopulate script."
+}
+
+
+#
+# setup_security(): runs chkey to change the network password same as the
+# login passwd.
+# $1 specifies root or user setup
+#
+setup_security()
+{
+ if [ $1 = "root" ]
+ then
+ MESS="root"
+ else
+ MESS="user"
+ fi
+ print_passwd_request $1
+ $ECHO "chkey -p > /dev/null"
+ if [ $? -ne 0 ]
+ then
+ echo "**ERROR: chkey failed."
+ echo ""
+ echo "The network password that you have entered is invalid."
+ echo "If this machine was initialized before as a NIS+ client,"
+ echo "please enter the $MESS login password as the network"
+ echo "password."
+ echo "Or re-type the network password that your administrator"
+ echo "gave you."
+ echo ""
+
+ $ECHO "chkey -p > /dev/null"
+ if [ $? -ne 0 ]
+ then
+ echo "**ERROR: chkey failed again."
+ echo "Please contact your network administrator to verify your network password."
+ if [ $1 = "root" ]
+ then
+ restore_here
+ fi
+ fi
+ fi
+ echo ""
+ echo "Your network password has been changed to your login one."
+ echo "Your network and login passwords are now the same."
+ echo ""
+}
+
+
+#
+# do_nisinit(): runs nisinit.
+# use $ADDR, as /etc/hosts isn't read if nis+ running
+#
+# Will have to modify this function for working with IPV6 NIS+ servers
+do_nisinit()
+{
+ eval "echo running nisinit command ... $VERB"
+
+ $ECHO "/bin/rm -f $DELETE_FILES 2> /dev/null"
+ $ECHO "mv -f /var/nis/NIS_COLD_START /var/nis/NIS_COLD_START.$BACKUP 2> /dev/null"
+ if [ -z "$SEC_DOM_DOT" ]
+ then
+ eval "echo nisinit -c -H $ADDR ... $VERB"
+ $ECHO "nisinit -c -H $ADDR > /dev/null"
+ else
+ eval "echo nisinit -c -k $SEC_DOM_DOT -H $ADDR ... $VERB"
+ $ECHO "nisinit -c -k $SEC_DOM_DOT -H $ADDR > /dev/null"
+ fi
+ if [ $? -ne 0 ]
+ then
+ echo "**ERROR: nisinit failed."
+ restore_here
+ fi
+ echo ""
+}
+
+
+
+#
+# rm_servdata(): removes the directory /var/nis/data, the trans.log file,
+# and the /var/nis/data.dict* file(s) if the client machine is a NIS+
+# server machine.
+#
+rm_servdata()
+{
+ $ECHO "rm -f /var/nis/trans.log > /dev/null"
+ $ECHO "rm -f /var/nis/data.dict* > /dev/null"
+ $ECHO "rm -rf /var/nis/data > /dev/null"
+ echo ""
+}
+
+
+
+#
+#
+# Client initialization Routine:
+# -------------------------------
+#
+# This section contains routine to initialize a client.
+# init_client() - the main module to initialize a client
+#
+
+#
+# init_client(): initializes client
+#
+init_client()
+{
+ eval "echo initializing client machine... $VERB"
+ WHO=`id | sed -e "s/uid=[0-9]*(\([^ )]*\)).*/\1/"`
+ if [ $WHO != "root" ]
+ then
+ echo "**ERROR: You must be root to use the -i option."
+ exit 1
+ fi
+
+ echo ""
+ if [ ! -z "$SEC_DOM_DOT" ]
+ then
+ eval "echo comparing the system domain and the secured RPC domain... $VERB"
+ check_rpc_domain
+ if [ "$SEC_DOM_DOT" != "$DOM" ]
+ then
+ echo "Initializing client `uname -n` for Secure RPC domain \"$SEC_DOM_NODOT\"."
+ echo "The system default domainname will be set to a different domain \"$DOM\"."
+ else
+ echo "Initializing client `uname -n` for domain \"$DOM\"."
+ fi
+ else
+ echo "Initializing client `uname -n` for domain \"$DOM\"."
+ fi
+
+
+ if [ -d /var/nis/data ]
+ then
+ echo ""
+ echo "WARNING: this machine serves NIS+ directories. Once this script is"
+ echo "executed, you will not be able to restore the existing NIS+ server"
+ echo "environment. Are you sure you want to proceed?"
+ get_yesno "(type 'y' to continue, 'n' to exit this script)"
+ if [ $ANS = "n" -o $ANS = "N" ]
+ then
+ exit
+ fi
+ echo "Once initialization is done, you will need to reboot your"
+ echo "machine."
+ echo ""
+ else
+ echo "Once initialization is done, you will need to reboot your"
+ echo "machine."
+ echo ""
+ get_yesno $CONTINUE
+ echo ""
+ fi
+
+ eval "echo killing NIS and/or NIS+ processes... $VERB"
+ svcadm disable network/nis/client
+ svcadm disable -t network/rpc/nisplus
+ eval "echo stopping nscd ... $VERB"
+ svcadm disable -t system/name-service-cache
+ eval "echo '' $VERB"
+
+ setup_backup
+
+ setup_server
+
+ setup_domain
+
+ setup_switch
+
+ do_nisinit
+
+ if [ "$SEC" = "2" ]
+ then
+ eval "echo -S 2 option specified, setting up security... $VERB"
+ setup_security root
+ elif [ "$SEC" = "x" ]
+ then
+ LOOKUP_DOM=${SEC_DOM_DOT:-$DOM}
+ if nistest '[cname='`nisdefaults -h`'],cred.org_dir'.$LOOKUP_DOM;
+ then
+ eval "echo credential exists for setting up security... $VERB"
+ setup_security root
+ fi
+ fi
+
+ svcadm enable network/rpc/nisplus
+ eval "echo starting nscd ... $VERB"
+ svcadm enable system/name-service-cache
+
+ remove_tmp
+
+ # As this operation is likely configuration changing, restart the
+ # name-services milestone (such that configuration-sensitive services
+ # are in turn restarted).
+ /usr/sbin/svcadm restart milestone/name-services
+
+ echo "Client initialization completed!!"
+ echo "Please reboot your machine for changes to take effect."
+}
+
+
+#
+#
+# User initialization Routine:
+# -----------------------------
+#
+# This section contains routine to initialize an user.
+# init_user() - the main module to initialize an user
+#
+
+#
+# init_user(): initializes user
+#
+init_user()
+{
+ if [ $OS -eq 4 ]
+ then
+ echo "**ERROR: NIS+ client libraries are not available in 4.x"
+ echo " You must access NIS+ tables through NIS (YP)."
+ exit 1
+ fi
+
+ eval "echo initializing user information... $VERB"
+ WHO=`id | sed -e "s/uid=[0-9]*(\([^ )]*\)).*/\1/"`
+ if [ $WHO = "root" ]
+ then
+ echo "**ERROR: You cannot use the -u option as a root user."
+ print_usage
+ exit 1
+ fi
+
+ if [ "$SEC" = "2" ]
+ then
+ eval "echo -S 2 option specified, setting up security... $VERB"
+ setup_security user
+ eval "echo User initialization completed!! $VERB"
+ elif [ "$SEC" = "0" ]
+ then
+ echo "**WARNING: it is not necessary to initialize NIS+ users"
+ echo " for unauthenticated users."
+ elif [ "$SEC" = "x" ]
+ then
+ WHO=`id | sed -e "s/uid=[0-9]*(\([^ )]*\)).*/\1/"`
+ if nistest '[cname='$WHO.$DOM'],cred.org_dir'.$DOM;
+ then
+ eval "echo credential exists for setting up security... $VERB"
+ setup_security user
+ eval "echo User initialization completed!! $VERB"
+ else
+ echo "**WARNING: you do not have NIS+ credentials."
+ echo " You can either use NIS+ service as an unauthenticated user"
+ echo " or ask your network administrator to create a credentital for you."
+ fi
+ fi
+}
+
+
+
+
+#
+#
+# Restore Routines:
+# -----------------
+#
+# This section contains routines to restore the environment.
+# restore_domain()- restores the domain
+# restore_switch()- restores the switch
+# mv_file() - moves file.$BACKUP file
+# restore_file() - restores all the files
+# restore_nis() - restores all NIS+ files
+# restore_service()
+# - restart either NIS or NIS+ daemons
+# restore() - the main module to restore the environment
+# restore_here() - same as restore() except it's called from init_client()
+# restore_tmp() - restores temporary backup files for previous backups
+# remove_tmp() - remove the backup files for the previous backups
+#
+
+#
+# restore_domain(): resets the "domainname" according to "/etc/defaultdomain".
+#
+restore_domain()
+{
+ DEFDOM=`sed -n -e "1p" /etc/defaultdomain`
+ NODOT=`echo $DEFDOM | sed -e "s/\.$//"`
+ echo "restoring domain to $DEFDOM..."
+ if [ "${NODOT}." != `nisdefaults -d` ]
+ then
+ $ECHO domainname ${NODOT}
+ fi
+ echo ""
+}
+
+
+#
+# restore_switch(): restarts the switch if necessary.
+# restarts nscd to ensure that it tracks the switch
+#
+restore_switch()
+{
+ eval "echo '' $VERB"
+ restart_instance network/rpc/keyserv:default
+ eval "echo restarting nscd ... $VERB"
+ restart_instance system/name-service-cache:default
+}
+
+
+#
+# mv_file(): moves file.$BACKUP to file.
+# $1 file name to be moved.
+#
+mv_file()
+{
+ $ECHO "mv -f $1.$BACKUP $1 > /dev/null"
+}
+
+
+#
+# restore_file(): restores the file specified.
+# $1 the file to be restored
+# This routine will restore any file that has the backup file <file>.$BACKUP.
+# The following table shows which backup files are created when the nisclient
+# script is run based on the previous name service setup.
+#
+# Files | NIS -> NIS+ | NIS+ -> NIS+
+# -----------+---------------+----------------
+# rootkey | no | yes
+# coldstart | no | yes
+# client_info| no | yes
+# switch | yes | maybe
+# hosts | maybe | maybe
+# domain | maybe | maybe
+#
+# Based on this table, those special files defined in $DEL_REST_FILES need to
+# be restored if any files from $RESTORE_FILES were restored (specially switch),
+# or if backup files for all the files in $DEL_REST_FILES were found.
+#
+restore_file()
+{
+ for FILE in $RESTORE_FILES
+ do
+ if [ -f $FILE.$BACKUP ]
+ then
+ if mv_file $FILE;
+ then
+ RESTORE=TRUE
+ echo " File $FILE restored!"
+ else
+ echo "**ERROR: could not restore file $FILE"
+ exit 1
+ fi
+ fi
+ done
+
+ for FILE in $REST_DOM_DIR
+ do
+ if [ -d $FILE.$BACKUP ]
+ then
+ if mv_file $FILE;
+ then
+ RESTORE=TRUE
+ echo " Directory $FILE restored!"
+ else
+ echo "**ERROR: could not restore directory $FILE"
+ exit 1
+ fi
+ fi
+ done
+
+ # ... check if the backup files exist for the special files in
+ # $DEL_REST_FILES
+ if [ "$RESTORE" != "TRUE" ]
+ then
+ for FILE in $DEL_REST_FILES
+ do
+ if [ ! -f $FILE.$BACKUP ]
+ then
+ return
+ fi
+ done
+ fi
+
+ # ... restore special files: coldstart and rootkey
+ # If the backup file exists, then restore. Otherwise, remove
+ # the file.
+ #
+ for FILE in $DEL_REST_FILES
+ do
+ if [ -f $FILE.$BACKUP ]
+ then
+ if mv_file $FILE;
+ then
+ RESTORE="TRUE"
+ echo " File $FILE restored!"
+ else
+ echo "**ERROR: could not restore file $FILE"
+ exit 1
+ fi
+ else
+ if [ -f $FILE ]
+ then
+ $ECHO "/bin/rm -f $FILE 2> /dev/null"
+ fi
+ fi
+ done
+}
+
+
+#
+# restore_nis(): removes the cache files: NIS_SHARED_DIRCACHE and
+# .pref_servers.
+#
+restore_nis()
+{
+ # ... remove the dircache file
+ for CACHE in $DELETE_FILES
+ do
+ if [ -f $CACHE ]
+ then
+ $ECHO "/bin/rm -f $CACHE 2> /dev/null"
+ fi
+ done
+}
+
+
+#
+# restore_service(): starts ypbind by checking the switch file.
+# If NIS+ service is to be restored, it will start nis_cachemgr.
+#
+restore_service()
+{
+ SWITCH=`grep "^[a-z]*:" /etc/nsswitch.conf`
+
+ if [ $OS -eq 5 ]
+ then
+ echo $SWITCH | grep -s "\<nisplus\>" >/dev/null
+ if [ $? -eq 0 ]
+ then
+ echo "Will not start up ypbind process because the nsswitch.conf"
+ echo "file shows that you are not using NIS."
+ echo "restarting nis_cachemgr process..."
+ restart_instance network/rpc/nisplus:default
+ echo ""
+ return
+ else
+ svcadm disable network/rpc/nisplus:default
+ fi
+ fi
+
+ echo $SWITCH | grep -s "\<nis\>" >/dev/null
+ if [ $? -eq 0 ]
+ then
+ echo "restarting NIS (YP) process..."
+ svcadm enable network/nis/client:default
+ echo ""
+ fi
+}
+
+
+#
+# restore(): restores the previous network information service.
+#
+restore()
+{
+ if [ $OS -eq 4 ]
+ then
+ echo "**ERROR: NIS+ client libraries are not available in 4.x"
+ echo " You must access NIS+ tables through NIS (YP)."
+ exit 1
+ fi
+
+ WHO=`id | sed -e "s/uid=[0-9]*(\([^ )]*\)).*/\1/"`
+ if [ $WHO != "root" ]
+ then
+ echo "This script must be run as root ..."
+ exit 1
+ fi
+
+ echo "This script will restore the previous network information"
+ echo "service. It recovers all the files with the no_nisplus "
+ echo "extension and restarts either NIS+ or NIS client processes"
+ echo "according to the /etc/nsswitch.conf configuration file."
+ echo ""
+ echo "Once restore is done, you will need to reboot your machine."
+ get_yesno $CONTINUE
+
+ svcadm disable -t network/rpc/nisplus
+ svcadm disable -t network/nis/client
+
+ restore_file
+
+ if [ "$RESTORE" = "TRUE" ]
+ then
+ restore_domain
+
+ restore_switch
+
+ restore_service
+
+ echo "Client files restored!"
+ echo "Please reboot your machine for changes to take effect."
+ else
+ echo "Nothing to restore from!"
+ echo "This script can only restore from NIS+ setup done with"
+ echo "nisclient command."
+ fi
+
+ # Again: a configuration changing operation, so restart the
+ # name-services milestone.
+ /usr/sbin/svcadm restart milestone/name-services
+}
+
+
+#
+# restore_here(): same as restore() except it's called from init_client()
+# when it fails to initialize a client.
+#
+restore_here()
+{
+ echo ""
+ echo "Restoring your network service..."
+ echo ""
+
+ svcadm disable -t network/rpc/nisplus
+ svcadm disable -t network/nis/client
+
+ restore_file
+
+ if [ "$RESTORE" = "TRUE" ]
+ then
+ restore_domain
+
+ restore_switch
+
+ restore_service
+
+ echo "Client files restored!"
+ fi
+ restore_tmp
+
+ /usr/sbin/svcadm restart milestone/name-services
+ exit 1
+}
+
+
+#
+# restore_tmp(): restores temporary backup files for previous backups.
+#
+restore_tmp()
+{
+ # ... restore from temporary backup files for previous backup
+ for FILE in $RESTORE_FILES $DEL_REST_FILES
+ do
+ if [ -f $FILE.$BACKUP.$$ ]
+ then
+ eval "echo restoring the temporary backup file for $FILE... $VERB"
+ $ECHO mv -f $FILE.$BACKUP.$$ $FILE.$BACKUP > /dev/null
+ fi
+ done
+ for FILE in $REST_DOM_DIR
+ do
+ if [ -d $FILE.$BACKUP.$$ ]
+ then
+ eval "echo restoring the temporary backup directory for $FILE... $VERB"
+ $ECHO mv -f $FILE.$BACKUP.$$ $FILE.$BACKUP > /dev/null
+ fi
+ done
+ exit 1
+}
+
+
+#
+# remove_tmp(): remove the backup files for the previous backups
+#
+remove_tmp()
+{
+ for FILE in $RESTORE_FILES $DEL_REST_FILES
+ do
+ if [ -f $FILE.$BACKUP.$$ ]
+ then
+ eval "echo removing the temporary backup file for $FILE... $VERB"
+ $ECHO "/bin/rm -f $FILE.$BACKUP.$$"
+ fi
+ done
+ for FILE in $REST_DOM_DIR
+ do
+ if [ -d $FILE.$BACKUP.$$ ]
+ then
+ eval "echo removing the temporary backup directory for $FILE... $VERB"
+ $ECHO "/bin/rm -rf $FILE.$BACKUP.$$"
+ fi
+ done
+}
+
+
+#
+#
+# Adding client Routines:
+# -----------------------
+#
+# This section contains routines to add a client or a principal into
+# credential table.
+# check_perm() - check for the write permission of a given object
+# check_domainname()
+# - check to see if the domainname has at least 2 components
+# check_rpc_domain()
+# - compares the system default domain and the secured
+# domain defines by the -k option
+# check_domain() - check a directory is valid and get the servers'
+# info
+# check_type() - check the type of name specified
+# add_LOCALcred() - adds the LOCAL credential into the credential table
+# add_DEScred() - adds the DES credential into the credential table
+# add_RSAcred() - adds the RSA credential into the credential table
+# add_cred() - adds credentials into the credential table
+# insert_cred() - the main module to add credentials
+#
+
+#
+# check_perm(): checks if we have write permission to the NIS+ table
+# This should be replaced with nisaccess command when it's available
+# $1 the table to be checked.
+#
+check_perm()
+{
+ if [ "$ECHO" = "echo" ]
+ then
+ return
+ fi
+
+ echo "checking $1 permission..."
+ MYPRINC=`nisdefaults -p`
+ if [ $MYPRINC = "nobody" ]
+ then
+ if nistest -a n=c $1;
+ then
+ return
+ else
+ return 1
+ fi
+ fi
+
+ DUMMY=`nisls -ld $1`
+ if [ $? -ne 0 ]
+ then
+ exit 1
+ fi
+ OWN=`echo $DUMMY | cut -d" " -f3`
+ if [ "$OWN" = $MYPRINC ]
+ then
+ if nistest -a o=c $1;
+ then
+ return
+ else
+ return 1
+ fi
+ fi
+
+ DUMMY=`nisls -ldg $1`
+ if [ $? -ne 0 ]
+ then
+ exit 1
+ fi
+ OWN=`echo $DUMMY | cut -d" " -f3`
+ if [ ! -z "$OWN" ]
+ then
+ if nisgrpadm -t -s "$OWN" $MYPRINC;
+ then
+ if nistest -a g=c $1;
+ then
+ return
+ else
+ return 1
+ fi
+ fi
+ fi
+
+ if nistest -a w=c $1;
+ then
+ return
+ else
+ return 1
+ fi
+}
+
+
+#
+# check_domainname(): check validity of a domain name. Currently we check
+# that it has at least two components.
+# $1 the domain name to be checked
+#
+check_domainname()
+{
+ if [ ! -z "$1" ]
+ then
+ t=`expr "$1" : '[^.]\{1,\}[.][^.]\{1,\}'`
+ if [ "$t" = 0 ]
+ then
+ echo '**ERROR: invalid domain name ('$1')'
+ echo ' It must have at least two components.'
+ echo ' For example, "company.com.".'
+ print_usage
+ exit 1
+ fi
+ fi
+}
+
+#
+# check_rpc_domain(): compares the secured domain against the system default
+# domain. We check to make sure that the local system domain (defined in
+# the /etc/defaultdomain or by the -d option if specified) is at the same
+# level or below the domain specified by the -k option. This is required
+# mainly to support server living in its own domain fix.
+check_rpc_domain()
+{
+ if [ -z "$SEC_DOM_DOT" ]
+ then
+ return
+ fi
+
+ if nistest -c $DOM le $SEC_DOM_DOT;
+ then
+ :
+ else
+ echo '***ERROR: invalid -k domain name ('$SEC_DOM_NODOT')'
+ echo ' The system default domain ('$NODOT') must be either the same'
+ echo ' as or a descendant of the Secure RPC domain ('$SEC_DOM_NODOT')'
+ echo ' where the key is stored.'
+ echo ' For example, if the system default domain is defined as'
+ echo ' (sub.company.com), then the -k domain can be defined as either'
+ echo ' sub.company.com or company.com.'
+ exit 1
+ fi
+}
+
+
+#
+# check_domain(): checks if it's a valid domain and get the server's info.
+#
+check_domain()
+{
+ if [ "$ECHO" = "echo" ]
+ then
+ return
+ fi
+
+ echo "checking $DOM domain..."
+ if nistest -t D $DOM;
+ then
+ :
+ else
+ echo "**ERROR: Don't know about the domain \"$DOM\"."
+ echo " Please check the domain name."
+ exit 1
+ fi
+
+ niscat -o $DOM > /tmp/$PROG.$$
+ if [ $? -ne 0 ]
+ then
+ rm -f /tmp/$PROG.$$ > /dev/null
+ exit 1
+ fi
+ SERVER=`sed -n -e "s/ Name *: //p" /tmp/$PROG.$$ | sed -n -e "1p"`
+ rm -f /tmp/$PROG.$$ > /dev/null
+ SER_NAME=`echo $SERVER | cut -d. -f1`
+ SER_DOM=`echo $SERVER | cut -d. -f2-`
+ if [ $SER_DOM = $SER_NAME ]
+ then
+ $SER_DOM=$DOM
+ fi
+
+ #
+ # Will have to check ipnodes.org_dir also when NIS+ server has IPv6
+ # interfaces.
+ #
+ DUMMY=`nismatch -P $SER_NAME hosts.org_dir.$SER_DOM`
+ if [ $? -eq 0 ]
+ then
+ SERVER=`echo $DUMMY | sed -n -e "1p"`
+ SER_ADDR=`echo $SERVER | cut -d" " -f3`
+ else
+ DUMMY=`grep -v "^#" $HOSTS_FILE | grep -i -s $SER_NAME`
+ if [ $? -eq 0 ]
+ then
+ SERVER=`echo $DUMMY | sed -e "s/ / /g"`
+ SER_ADDR=`echo $SERVER | cut -d" " -f1`
+ else
+ echo "**ERROR: Couldn't get the server ${SER_NAME}'s address."
+ exit 1
+ fi
+ fi
+ echo ""
+}
+
+
+
+#
+# check_type(): checks what type of credential to add into the credential
+# table. It tries to match the given name agains the passwd.org_dir and
+# hosts.org_dir tables. It assigns the value of "user" if the name is found
+# in the passwd table and "host" if it's found in the hosts table.
+# *** The name cannot be in both table ***
+# $1 name
+#
+check_type()
+{
+ eval "echo checking info type for $NAME... $VERB"
+ UTYPE="FALSE"
+ HTYPE="FALSE"
+ ARG=$1
+ LARG=$1
+ NEWNAME=""
+
+ # Checking both for IPV4 and IPV6 hosts
+ DUM1=`nismatch -P name=$ARG hosts.org_dir.$DOM`;
+
+ if [ $? -ne 0 ]
+ then
+ if nistest ipnodes.org_dir.$DOM;
+ then
+ DUM1=`nismatch -P name=$ARG ipnodes.org_dir.$DOM`;
+ fi
+ fi
+
+ if [ -n "$DUM1" ]
+ then
+ H_NAME=`echo $DUM1 | cut -d' ' -f1`
+ H_ALIAS=`echo $DUM1 | cut -d' ' -f2`
+ if [ "`tolower $H_NAME`" != "`tolower $H_ALIAS`" ]
+ then
+ echo "**WARNING: $H_ALIAS is an alias name for host $H_NAME."
+ echo "You cannot create credential for host alias."
+ get_yesno "Do you want to create the credential for $H_NAME?"
+ if [ $ANS = "y" -o $ANS = "Y" ]
+ then
+ NEWNAME=$H_NAME
+ LARG=$H_NAME
+ HTYPE="TRUE"
+ else
+ HTYPE="SKIP" # for error message handling
+ fi
+ else
+ HTYPE="TRUE"
+ fi
+ fi
+
+ if nistest '[name='$LARG'],passwd.org_dir.'$DOM;
+ then
+ if [ $HTYPE = "TRUE" ]
+ then
+ echo "**ERROR: this name \"$LARG\" is in both the passwd and hosts tables."
+ echo " You cannot have an username same as the hostname."
+ return 1
+ fi
+ UTYPE="TRUE"
+ fi
+
+ if [ $UTYPE = "TRUE" ]
+ then
+ TYPE="user"
+ elif [ $HTYPE = "TRUE" ]
+ then
+ TYPE="host"
+ else
+ if [ $HTYPE != "SKIP" ]
+ then
+ echo "**ERROR: invalid name \"$LARG\"."
+ echo " It is neither an host nor an user name."
+ fi
+ return 1
+ fi
+}
+
+
+
+
+#
+# add_LOCALcred(): adds the LOCAL credential into the credential table.
+#
+add_LOCALcred()
+{
+ echo "adding LOCAL credential for $1..."
+
+ DUMMY=`nismatch -P name=$1 passwd.org_dir.$DOM`
+ if [ $? -ne 0 ]
+ then
+ return 1
+ fi
+ UID=`echo $DUMMY | cut -d: -f3`
+
+ if [ $? -ne 0 ]
+ then
+ return 1
+ fi
+ $ECHO nisaddcred -p $UID -P $1.$DOM local $DOM
+ return $?
+}
+
+
+
+
+#
+# add_RSAcred(): adds the RSA credential into the credential table.
+# $1 either username or hostname
+# $2 password if specified
+#
+add_RSAcred()
+{
+ echo "adding RSA credential for $1..."
+
+ if [ $# -eq 1 ]
+ then
+ $ECHO nisaddcred -p $1.$DOM -P $1.$DOM rsa $DOM
+ return $?
+ fi
+
+ echo "nisaddcred -l $2 -p $1.$DOM -P $1.$DOM rsa $DOM"
+ return $?
+}
+
+#
+# add_DEScred(): adds the DES credential into the credential table.
+# $1 either username or hostname
+# $2 password if specified
+#
+add_DEScred()
+{
+ echo "adding DES credential for $1..."
+
+ if [ $# -eq 2 ]
+ then
+ LPASS="-l $2"
+ else
+ LPASS=""
+ fi
+ if [ $TYPE = "user" ]
+ then
+ $ECHO nisaddcred $LPASS -p unix.${UID}@$NODOT -P $1.$DOM des $DOM
+ else
+ $ECHO nisaddcred $LPASS -p unix.$1@$NODOT -P $1.$DOM des $DOM
+ fi
+ return $?
+}
+
+
+
+
+#
+# add_cred(): adds the credential.
+# $* names to be added.
+#
+add_cred()
+{
+ while [ $# -ne 0 ]
+ do
+ NAME=$1
+ check_type $NAME
+
+ if [ $? -eq 0 ]
+ then
+ if [ ! -z "$NEWNAME" ]
+ then
+ NAME=$NEWNAME
+ fi
+
+ DUMMY=`nismatch $NAME.$DOM cred.org_dir.$DOM > /dev/null`
+ if [ $? -eq 0 ]
+ then
+ if [ -z "$OWRITE" ]
+ then
+ echo ... overwriting the existing entry for principal $NAME!
+ else
+ echo ... principal $NAME already exist -- skipped!
+ shift
+ continue
+ fi
+ fi
+
+ if [ $TYPE = "user" ]
+ then
+ if add_LOCALcred $NAME;
+ then
+ eval echo ... added LOCAL credential for $NAME. $VERB
+ else
+ eval "echo ... could not add LOCAL credential for $NAME. $VERB"
+ TYPE="fail"
+ fi
+ fi
+
+ if [ $TYPE != "fail" ]
+ then
+ case $SEC in
+ 2) SECTYPE=DES;;
+ 3) SECTYPE=RSA;;
+ esac
+
+ if add_${SECTYPE}cred $NAME $PAS;
+ then
+ eval "echo ... added $SECTYPE credential for $NAME. $VERB"
+ else
+ eval "echo ... could not add $SECTYPE credential for $NAME. $VERB"
+ TYPE="fail"
+ fi
+ fi
+ echo ""
+
+ if [ $TYPE = "host" ]
+ then
+ DOIT=DOIT
+ elif [ $TYPE = "user" ]
+ then
+ USERDOIT=DOIT
+ fi
+
+ fi
+ shift
+ done
+}
+
+
+
+#
+# insert_cred(): adds NIS+ client or principals credentials
+#
+insert_cred()
+{
+ if [ $# -eq 0 ]
+ then
+ echo "**ERROR: missing hostnames or usernames."
+ print_usage
+ exit 1
+ fi
+
+ if [ "$SEC" = "0" ]
+ then
+ echo "**WARNING: it is not necessary to create NIS+ credentials"
+ echo " for unauthenticated users."
+ return
+ fi
+ if [ "$SEC" != "2" -a "$SEC" != "x" ]
+ then
+ echo "**ERROR: invalid security level $SEC."
+ fi
+ SEC=2
+
+ case $SEC in
+ 2) MESS=DES;;
+ 3) MESS=RSA;;
+ esac
+ echo ""
+ echo "You will be adding $MESS credentials in domain $DOM for"
+ echo $*
+ echo ""
+ if [ -z "$OWRITE" ]
+ then
+ echo "** nisclient will overwrite existing entries in the credential"
+ echo "** table for hosts and users specified above."
+ else
+ echo "** nisclient will not overwrite any existing entries in the"
+ echo "** credential table."
+ fi
+ echo ""
+ get_yesno $CONTINUE
+ echo ""
+
+ check_domain
+
+ check_perm cred.org_dir.$DOM
+ if [ $? -ne 0 ]
+ then
+ echo "Sorry, no permission to create credentials!"
+ exit 1
+ fi
+ echo ""
+
+ add_cred $*
+
+ if [ "$DOIT" = "DOIT" ]
+ then
+ if [ $GEN = "TRUE" ]
+ then
+ $ECHO "echo '#!/bin/sh' > $GENFILE"
+ $ECHO "echo '#' > $GENFILE"
+ $ECHO "echo '' > $GENFILE"
+ $ECHO "echo "nisclient -i -h $SER_NAME -a $SER_ADDR -d $DOM" > $GENFILE"
+ fi
+ echo "For all new NIS+ clients added, you will need to run the"
+ echo "following on the client's machine:"
+ echo "nisclient -i -h $SER_NAME -a $SER_ADDR -d $DOM"
+ fi
+
+ if [ "$USERDOIT" = "DOIT" ]
+ then
+ echo ""
+ echo "For all new NIS+ users added, you will need to update"
+ echo "their keys on all machines that they are currently logged"
+ echo "in by running keylogin(1), chkey(1), or nisclient(1M)."
+ fi
+}
+
+
+
+#
+#
+# * * * MAIN * * *
+#
+
+# Display the obsolescence message in all the cases
+echo ""
+echo "******** ******** WARNING ******** ********"
+echo "NIS+ might not be supported in a future release. Tools to aid"
+echo "the migration from NIS+ to LDAP are available in the Solaris 9"
+echo "operating environment. For more information, visit"
+echo "http://www.sun.com/directory/nisplus/transition.html"
+echo "******** ******** ******* ******** ********"
+echo ""
+
+init
+
+umask 22
+
+parse_arg $*
+shift $?
+
+check_domainname "$DOM"
+
+case $ACTION in
+"init")
+ init_client;;
+"user")
+ init_user;;
+"create")
+ insert_cred $*;;
+"restore")
+ restore;;
+*)
+ echo "**ERROR: you must specify one of the these options: -c, -i, -u, -r."
+ print_usage
+ exit 1
+esac
diff --git a/usr/src/cmd/rpcsvc/nis/bin/nisctl.c b/usr/src/cmd/rpcsvc/nis/bin/nisctl.c
new file mode 100644
index 0000000000..1c541a0437
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/bin/nisctl.c
@@ -0,0 +1,222 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * nisctl.c
+ *
+ * Copyright (c) 1988-1992 Sun Microsystems Inc
+ * All Rights Reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * nisctl.c
+ *
+ * This program is used to control the operation of NIS+ servers.
+ * It may be used to turn debugging on and off, and to flush
+ * various caches and print statistics.
+ */
+
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/nis.h>
+
+extern int optind;
+extern char *optarg;
+
+nis_tag mytags[64];
+
+usage(s)
+ char *s;
+{
+ fprintf(stderr,
+"usage: %s [-M] [-s] [-v on|off] [-f dgot [-n obj_name]] [-H host] [domain]\n",
+ s);
+ fprintf(stderr, "\t-M: heap memory\n");
+ fprintf(stderr, "\t-s: statistics\n");
+ fprintf(stderr, "\t-v: verbose mode\n");
+ fprintf(stderr,
+ "\t-f: flush cache (dir, group, object, table) [-n obj_name]\n");
+ fprintf(stderr, "At least one of the above options must be "
+ "specified\n");
+ exit(1);
+}
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+ int i;
+ nis_error error;
+ nis_tag *res;
+ nis_result *lres;
+ nis_object *obj;
+ nis_server **srvs, **x;
+ nis_name domain;
+ char dname[1024], *s;
+ char server[NIS_MAXNAMELEN];
+
+ server[0] = NULL;
+ i = 0;
+
+ /* nisctl cannot be used without any options */
+ if (argc == 1) {
+ usage(argv[0]);
+ exit(1);
+ }
+
+ while ((c = getopt(argc, argv, "Msv:n:f:H:")) != -1) {
+ switch (c) {
+ case 'M' :
+ mytags[0].tag_val = "";
+ mytags[0].tag_type = TAG_HEAP;
+ i = 1;
+ break;
+ case 'f' :
+ for (s = optarg; *s; s++) {
+ mytags[i].tag_val = "flush";
+ switch (*s) {
+ case 'g':
+ mytags[i].tag_type =
+ TAG_GCACHE_ALL;
+ break;
+ case 'd':
+ mytags[i].tag_type =
+ TAG_DCACHE_ALL;
+ break;
+ case 'o':
+ mytags[i].tag_type =
+ TAG_OCACHE;
+ break;
+ case 't':
+ mytags[i].tag_type =
+ TAG_TCACHE_ALL;
+ break;
+ default:
+ usage(argv[0]);
+ }
+ i++;
+ }
+ break;
+ case 'n':
+ /* specifies a particular entry to flush */
+ if (i == 0) {
+ fprintf(stderr, "-f option required\n");
+ usage(argv[0]);
+ }
+ if (i > 1) {
+ fprintf(stderr,
+ "only one option allowed\n");
+ usage(argv[0]);
+ }
+ i = 0; /* kludge - reset it */
+ mytags[i].tag_val = optarg;
+ switch (mytags[i].tag_type) {
+ case TAG_GCACHE_ALL:
+ mytags[i].tag_type = TAG_GCACHE_ONE;
+ break;
+ case TAG_TCACHE_ALL:
+ mytags[i].tag_type = TAG_TCACHE_ONE;
+ break;
+ case TAG_DCACHE_ALL:
+ mytags[i].tag_type = TAG_DCACHE_ONE;
+ break;
+ case TAG_OCACHE:
+ fprintf(stderr,
+ "Object flushing not supported\n");
+ usage(argv[0]);
+ default:
+ usage(argv[0]);
+ }
+ i = 1; /* reset it back */
+ break;
+ case 'v' :
+ if (strcmp(optarg, "1") == 0)
+ mytags[i].tag_val = "on";
+ else if (strcmp(optarg, "0") == 0)
+ mytags[i].tag_val = "off";
+ else
+ mytags[i].tag_val = optarg;
+ mytags[i++].tag_type = TAG_DEBUG;
+ break;
+ case 's' :
+ mytags[i].tag_val = optarg;
+ mytags[i++].tag_type = TAG_STATS;
+ break;
+ case 'H' :
+ if (!optarg)
+ usage(argv[0]);
+ if (optarg[strlen(optarg) -1] == '.')
+ strcpy(server, optarg);
+ else
+ sprintf(server, "%s.%s", optarg,
+ nis_local_directory());
+ break;
+ default :
+ usage(argv[0]);
+ exit(1);
+ }
+ }
+ if (optind < argc)
+ domain = argv[optind];
+ else
+ domain = nis_local_directory();
+
+ lres = nis_lookup(domain, EXPAND_NAME);
+ if (lres->status != NIS_SUCCESS) {
+ fprintf(stderr, "\"%s\" : %s\n", domain,
+ nis_sperrno(lres->status));
+ exit(1);
+ }
+ obj = lres->objects.objects_val;
+ sprintf(dname, "%s.%s", obj->zo_name, obj->zo_domain);
+ srvs = (nis_server **)nis_getservlist(dname);
+ if (! srvs) {
+ fprintf(stderr,
+ "Unable to get list of servers serving \"%s\"\n", dname);
+ exit(1);
+ }
+
+ for (x = srvs; *x; x++) {
+ if (server[0] && nis_dir_cmp(server, (*x)->name) != SAME_NAME)
+ continue;
+ printf("Setting State on : '%s'\n", (*x)->name);
+ error = nis_servstate(*x, mytags, i, &res);
+ if (error == NIS_SUCCESS) {
+ if (res == NULL) {
+ fprintf(stderr, "Set server state failed\n");
+ nis_freeservlist(srvs);
+ exit(1);
+ }
+ printf("%s\n", res->tag_val);
+ } else {
+ fprintf(stderr, "Set server state failed : %s\n",
+ nis_sperrno(error));
+ nis_freeservlist(srvs);
+ exit(1);
+ }
+ nis_freetags(res, i);
+ }
+ nis_freeservlist(srvs);
+ exit(0);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/bin/nisopaccess.sh b/usr/src/cmd/rpcsvc/nis/bin/nisopaccess.sh
new file mode 100644
index 0000000000..ca48dee411
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/bin/nisopaccess.sh
@@ -0,0 +1,298 @@
+#!/bin/sh
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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) 1999 by Sun Microsystems, Inc.
+# All rights reserved.
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+EXIT=255
+TBLNAME=proto_op_access
+TBLTYPE=proto_op_access_tbl
+VERBOSE=0
+REMOVE=0
+LIST=0
+
+print_usage()
+{
+ echo "Usage: $0 [-v] directory operation rights"
+ echo " $0 [-r] [-v] directory operation"
+ echo " $0 [-l] [-v] directory [operation]"
+}
+
+# no_dot(): check if arg has a trailing dot.
+no_dot()
+{
+ if [ "`echo $1 | sed -e 's/.*\(.\)$/\1/'`" != "." ]
+ then
+ return 0
+ fi
+ return 1
+}
+
+# parse_opt(): Parse options. Returns the number of arguments to shift
+# in order to get to the non-option arguments.
+parse_opt()
+{
+ while getopts "vrl" ARG
+ do
+ case $ARG in
+ v) VERBOSE=1;;
+ r) REMOVE=1;;
+ l) LIST=1;;
+ \?) print_usage
+ exit $EXIT;;
+ *) print_usage
+ exit $EXIT
+ esac
+ done
+ return `expr $OPTIND - 1`
+}
+
+# unknown_op(): Check that operation (NIS_PING etc.) is known
+unknown_op()
+{
+ case $OPERATION in
+ NIS_CHECKPOINT|nis_checkpoint|CHECKPOINT|checkpoint)
+ OPERATION=NIS_CHECKPOINT;;
+ NIS_CPTIME|nis_cptime|CPTIME|cptime)
+ OPERATION=NIS_CPTIME;;
+ NIS_MKDIR|nis_mkdir|MKDIR|mkdir)
+ OPERATION=NIS_MKDIR;;
+ NIS_PING|nis_ping|PING|ping)
+ OPERATION=NIS_PING;;
+ NIS_RMDIR|nis_rmdir|RMDIR|rmdir)
+ OPERATION=NIS_RMDIR;;
+ NIS_SERVSTATE|nis_servstate|SERVSTATE|servstate)
+ OPERATION=NIS_SERVSTATE;;
+ NIS_STATUS|nis_status|STATUS|status)
+ OPERATION=NIS_STATUS;;
+ *) return 0;;
+ esac
+ return 1
+}
+
+# print_rights(): parse and print access rights
+print_rights()
+{
+ op=$1
+ shift
+ rights="<unknown>"
+ owner="<none>"
+ group="<none>"
+ while [ $# -gt 0 ]; do
+ case $1 in
+ Owner)
+ shift
+ if [ $1 = ":" ]; then
+ shift
+ if [ $1 != "Group" ]; then
+ owner=$1
+ shift
+ fi
+ fi;;
+ Group)
+ shift
+ if [ $1 = ":" ]; then
+ shift
+ if [ $1 != "Access" ]; then
+ group=$1
+ shift
+ fi
+ fi;;
+ Access)
+ shift
+ if [ $1 = "Rights" ]; then
+ shift
+ if [ $1 = ":" ]; then
+ shift
+ rights=$1
+ while [ $# -gt 0 ]; do
+ shift
+ done
+ fi
+ fi;;
+ *)
+ shift;;
+ esac
+ done
+ echo "$op\t $rights\t $owner\t $group"
+ return $?
+}
+
+# print_op(): print access rights for the specified operation
+print_op()
+{
+ nismatch op=$1 $TBLNAME.$DIRECTORY > /dev/null 2>&1
+ if [ $? -eq 0 ]; then
+ print_rights $1 `niscat -oM \[op=$1\]$TBLNAME.$DIRECTORY`
+ fi
+ return $?
+}
+
+# Main
+
+PATH=/usr/lib/nis:/usr/sbin:/usr/bin
+
+# Parse the options, if any
+parse_opt $*
+shift $?
+
+# Check that we've got the correct number of arguments
+case $# in
+1) if [ $LIST -eq 0 ]; then
+ print_usage
+ exit $EXIT
+ fi
+ DIRECTORY=$1;;
+
+2) if [ $LIST -eq 0 -a $REMOVE -eq 0 ]; then
+ print_usage
+ exit $EXIT
+ elif [ $LIST -ne 0 -a $REMOVE -ne 0 ]; then
+ echo "The -l and -r options are mutually exclusive"
+ exit $EXIT
+ fi
+ if [ $LIST -ne 0 ]; then
+ LIST=2
+ fi
+ DIRECTORY=$1
+ OPERATION=$2
+ if unknown_op; then
+ echo "Unknown operation $OPERATION"
+ exit $EXIT
+ fi;;
+
+3) if [ $LIST -ne 0 -o $REMOVE -ne 0 ]; then
+ print_usage
+ exit $EXIT
+ fi
+ DIRECTORY=$1
+ OPERATION=$2
+ if unknown_op; then
+ echo "Unknown operation $OPERATION"
+ exit $EXIT
+ fi
+ RIGHTS=$3;;
+
+*) print_usage
+ exit $EXIT;;
+esac
+
+# If no trailing dot in directory name, add the domain name
+if no_dot $DIRECTORY;
+then
+ DIRECTORY=$DIRECTORY.`domainname`.
+fi
+
+# Does the directory exist ?
+niscat -o $DIRECTORY > /dev/null 2>&1
+STAT=$?
+if [ $STAT -ne 0 ]; then
+ echo "$DIRECTORY: no such NIS+ directory"
+ exit $STAT
+fi
+
+# Does the table exist ?
+niscat -o $TBLNAME.$DIRECTORY > /dev/null 2>&1
+TBLEXISTS=$?
+
+# List all or just one operation
+if [ $LIST -eq 1 ]; then
+ if [ $TBLEXISTS -ne 0 ]; then
+ echo "No operation access table for $DIRECTORY"
+ exit 0
+ fi
+ if [ $VERBOSE -eq 1 ]; then
+ echo "Listing access for all operations for $DIRECTORY"
+ fi
+ echo "Operation\t n---o---g---w---\t Owner\t\t\t Group"
+ echo ""
+ for OP in NIS_CHECKPOINT NIS_CPTIME NIS_MKDIR NIS_PING NIS_RMDIR \
+ NIS_SERVSTATE NIS_STATUS; do
+ print_op $OP
+ STAT=$?
+ done
+ exit $STAT
+elif [ $LIST -eq 2 ]; then
+ if [ $TBLEXISTS -ne 0 ]; then
+ echo "No operation access table for $DIRECTORY"
+ exit 0
+ fi
+ if [ $VERBOSE -eq 1 ]; then
+ echo "Listing access for $OPERATION for $DIRECTORY"
+ fi
+ print_op $OPERATION
+ exit $?
+fi
+
+# Remove an operation
+if [ $REMOVE -eq 1 ]; then
+ if [ $TBLEXISTS -ne 0 ]; then
+ echo "No operation access table for $DIRECTORY"
+ exit 0
+ fi
+ nismatch op=$OPERATION $TBLNAME.$DIRECTORY > /dev/null 2>&1
+ if [ $? -ne 0 ]; then
+ if [ $VERBOSE -eq 1 ]; then
+ echo "No $OPERATION access control for $DIRECTORY"
+ fi
+ exit 0
+ else
+ if [ $VERBOSE -eq 1 ]; then
+ echo "Removing $OPERATION access control for $DIRECTORY"
+ fi
+ nistbladm -R \[op=$OPERATION\]$TBLNAME.$DIRECTORY
+ exit $?
+ fi
+fi
+
+# Create the table if it doesn't exist already
+if [ $TBLEXISTS -ne 0 ]; then
+ if [ $VERBOSE -eq 1 ]; then
+ echo "Creating access control table in $DIRECTORY"
+ fi
+ nistbladm -c $TBLTYPE op=SI,o=rmcd,g=r,w=r,n=r subop=SI,o=rmcd,g=r,w=r,n=r $TBLNAME.$DIRECTORY
+ STAT=$?
+ if [ $STAT -ne 0 ]; then
+ exit $STAT
+ fi
+fi
+
+# If no entry for the operation, create it with requested rights
+nismatch op=$OPERATION $TBLNAME.$DIRECTORY > /dev/null 2>&1
+if [ $? -ne 0 ]; then
+ if [ $VERBOSE -eq 1 ]; then
+ echo "Creating $OPERATION access control entry for $DIRECTORY"
+ fi
+ nistbladm -a -D access=$RIGHTS op=$OPERATION $TBLNAME.$DIRECTORY
+ exit $?
+fi
+
+# Modify existing entry
+if [ $VERBOSE -eq 1 ]; then
+ echo "Changing $OPERATION access control entry for $DIRECTORY"
+fi
+nischmod $RIGHTS \[op=$OPERATION\]$TBLNAME.$DIRECTORY
+
+exit $?
diff --git a/usr/src/cmd/rpcsvc/nis/bin/nisping.c b/usr/src/cmd/rpcsvc/nis/bin/nisping.c
new file mode 100644
index 0000000000..dab6f7ff4b
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/bin/nisping.c
@@ -0,0 +1,355 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * nisping.c
+ *
+ * This program will ping a server in an attempt to get it to resynchronize
+ * with the master NIS+ server. It also performs database checkpoints.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <rpc/types.h>
+#include <rpc/rpc.h>
+#include <rpc/clnt.h>
+#include <rpcsvc/nis.h>
+#include <sys/time.h>
+
+extern void __nis_pingproc(nis_server *, nis_name, u_long);
+extern bool_t xdr_nis_name();
+extern bool_t xdr_cp_result();
+
+#define ROOT_OBJ "root.object"
+extern nis_name __nis_local_root();
+
+#define NIS_PROG 100300
+#define NIS_VERSION 3
+#define NIS_CPTIME 18
+#define NIS_CHECKPOINT 19
+
+/*
+ * Return values (from the man page):
+ * -1 No servers were contacted, or the server
+ * specified by the -H switch could not be con-
+ * tacted.
+ *
+ * 0 Success.
+ *
+ * 1 Some, but not all, servers were successfully
+ * contacted.
+ */
+#define PING_SUCCESS 0
+#define PING_SOME 1
+#define PING_NONE -1
+
+/*
+ * nis_checkpoint()
+ *
+ * This function will ask the indicated replicate to checkpoint itself
+ */
+static nis_error
+nis_checkpnt(srv, name)
+ nis_server *srv;
+ nis_name name;
+{
+ CLIENT *clnt;
+ enum clnt_stat status;
+ cp_result res;
+ struct timeval tv;
+
+ clnt = nis_make_rpchandle(srv, 0, NIS_PROG, NIS_VERSION,
+ ZMH_DG|ZMH_AUTH|ZMH_NOFALLBACK, 1024, 512);
+ /* If we can't contact it, return the safe answer */
+ if (! clnt) {
+ return (NIS_NAMEUNREACHABLE);
+ }
+
+ tv.tv_sec = 10;
+ tv.tv_usec = 0;
+ status = clnt_call(clnt, NIS_CHECKPOINT, xdr_nis_name, (char *) &name,
+ xdr_cp_result, (char *) &res, tv);
+ if (status != RPC_SUCCESS) {
+ printf("nisping: RPC error on server %s, error %s\n",
+ srv->name, clnt_sperrno(status));
+ res.cp_status = NIS_RPCERROR;
+ }
+ clnt_destroy(clnt);
+ return (res.cp_status);
+}
+
+/*
+ * nis_cptime()
+ *
+ * This function will ask the indicated replicate for the last
+ * update it has seen to the given directory.
+ */
+static nis_error
+nis_cptime(srv, name, utime)
+ nis_server *srv;
+ nis_name name;
+ u_long *utime;
+{
+ CLIENT *clnt;
+ enum clnt_stat status;
+ struct timeval tv;
+ nis_error res;
+
+ clnt = nis_make_rpchandle(srv, 0, NIS_PROG, NIS_VERSION,
+ ZMH_DG|ZMH_AUTH|ZMH_NOFALLBACK, 1024, 512);
+
+ /* If we can't contact it, return the safe answer */
+ if (! clnt) {
+ *utime = 0;
+ return (NIS_RPCERROR);
+ }
+ /* Only wait 10 seconds */
+ tv.tv_sec = 10;
+ tv.tv_usec = 0;
+ status = clnt_call(clnt, NIS_CPTIME, xdr_nis_name, (char *) &name,
+ xdr_u_long, (char *) utime, tv);
+ res = (status != RPC_SUCCESS) ? NIS_RPCERROR : NIS_SUCCESS;
+ clnt_destroy(clnt);
+ return (res);
+}
+
+static void
+usage(s)
+ char *s;
+{
+ fprintf(stderr, "usage: %s [-uf] [-H hostname] [-r|directory]\n", s);
+ fprintf(stderr, " %s -C [-a] [-H hostname] [directory]\n", s);
+
+ exit(PING_NONE);
+}
+
+static
+int
+match_host(char *host, char *target)
+{
+ int len = strlen(host);
+
+ if (strncasecmp(host, target, len) == 0 &&
+ (target[len] == '.' || target[len] == '\0'))
+ return (1);
+
+ return (0);
+}
+
+extern int optind;
+extern char *optarg;
+
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ nis_server *srvs;
+ nis_object *obj;
+ int c;
+ int i, ns, force = 0, uponly = 0;
+ int checkpoint_all = 0;
+ int chkpnt = 0;
+ u_long updtm, reptm;
+ nis_error status;
+ nis_name domain;
+ char dname[1024], obj_desc[1024];
+ char *host = NULL;
+ nis_result *res;
+ int root_object = 0;
+ int flag = EXPAND_NAME|USE_DGRAM;
+ int tries = 0;
+ int successes = 0;
+
+ while ((c = getopt(argc, argv, "CH:ufra")) != -1) {
+ switch (c) {
+ case 'f':
+ force = 1;
+ break;
+ case 'u':
+ uponly = 1;
+ break;
+ case 'C' :
+ chkpnt = 1;
+ break;
+ case 'H' :
+ host = optarg;
+ break;
+ case 'r' :
+ root_object = 1;
+ break;
+ case 'a' :
+ checkpoint_all = 1;
+ break;
+ default :
+ usage(argv[0]);
+ }
+ }
+
+ if (optind < argc) {
+ if (root_object)
+ usage(argv[0]);
+ domain = argv[optind];
+ } else
+ domain = nis_local_directory();
+
+ if (chkpnt == 0 && checkpoint_all)
+ usage(argv[0]);
+
+ if (root_object) {
+ if (chkpnt) {
+ fprintf(stderr,
+ "%s: no need to checkpoint root object.\n",
+ argv[0]);
+ usage(argv[0]);
+ }
+ domain = __nis_local_root();
+ if (domain == 0) {
+ fprintf(stderr,
+ "%s: cannot get name of root directory.\n",
+ argv[0]);
+ exit(PING_NONE);
+ }
+ }
+
+ if (!uponly)
+ flag += MASTER_ONLY;
+ res = nis_lookup(domain, flag);
+ if (res->status != NIS_SUCCESS) {
+ fprintf(stderr, "%s: %s\n", domain, nis_sperrno(res->status));
+ exit(PING_NONE);
+ }
+ obj = res->objects.objects_val;
+ sprintf(dname, "%s.%s", obj->zo_name, obj->zo_domain);
+ if (__type_of(obj) != NIS_DIRECTORY_OBJ) {
+ fprintf(stderr, "\"%s\" : not a directory.\n", dname);
+ exit(PING_NONE);
+ }
+ srvs = obj->DI_data.do_servers.do_servers_val;
+ ns = obj->DI_data.do_servers.do_servers_len;
+
+ if (root_object)
+ strcpy(obj_desc, "root object");
+ else
+ sprintf(obj_desc, "directory %s", dname);
+
+ if (host) { /* if a specific host has been specified */
+ for (i = 0; i < ns; ++i) {
+ if (match_host(host, srvs[i].name))
+ break;
+ }
+ if (i == ns) {
+ fprintf(stderr, "Host %s does not serve \"%s\".\n",
+ host, obj_desc);
+ exit(PING_NONE);
+ }
+ }
+
+ /* set real target name for root object. */
+ if (root_object)
+ strcpy(dname, ROOT_OBJ);
+
+ if (! chkpnt && (ns == 1) && ! uponly) {
+ printf("\"%s\" : no replicas\n", root_object? obj_desc : dname);
+ exit(PING_NONE);
+ }
+
+ if (chkpnt)
+ printf("Checkpointing %s serving directory \"%s\" :\n",
+ (host) ? "host" : "replicas", dname);
+ else if (uponly)
+ printf("Last updates for \"%s\" : \n", obj_desc);
+ else
+ printf("Pinging %s serving \"%s\" :\n",
+ (host) ? "host" : "replicas", obj_desc);
+
+ printf("Master server is \"%s\"\n", srvs[0].name);
+ status = nis_cptime(&srvs[0], dname, &updtm);
+ if (status != NIS_SUCCESS)
+ printf("\tUnable to fetch update time from master server.\n");
+ else if (!updtm)
+ printf("\tNo last update time available for \"%s\".\n",
+ obj_desc);
+ else
+ printf("\tLast update occurred at %s\n",
+ ctime((time_t *)&updtm));
+ /*
+ * Need to increment success count if -H <master> or only 1 server.
+ */
+ if ((!host || match_host(host, srvs[0].name)) &&
+ (status == NIS_SUCCESS)) {
+ tries++;
+ successes++;
+ }
+ for (i = 0; i < ns; i++) {
+ if (host && !match_host(host, srvs[i].name))
+ continue;
+ if ((i == 0) && ! chkpnt)
+ continue;
+ printf("%s server is \"%s\"\n", (i) ? "Replica" : "Master",
+ srvs[i].name);
+ if (chkpnt) {
+ if (checkpoint_all)
+ status = nis_checkpnt(&srvs[i], "");
+ else
+ status = nis_checkpnt(&srvs[i], dname);
+
+ if (status != NIS_SUCCESS) {
+ printf("checkpoint failed : %s\n",
+ nis_sperrno(status));
+ } else
+ printf("checkpoint scheduled on \"%s\".\n",
+ srvs[i].name);
+ } else {
+ status = nis_cptime(&srvs[i], dname, &reptm);
+ if (status == NIS_SUCCESS) {
+ if (!reptm)
+ printf(
+ "\tNo last update available for \"%s\".\n",
+ obj_desc);
+ else
+ printf("\tLast Update seen was %s\n",
+ ctime((time_t *)&reptm));
+ if (! uponly && updtm &&
+ ((reptm < updtm) || force)) {
+ printf("\tPinging ... \"%s\"\n",
+ srvs[i].name);
+ __nis_pingproc(&srvs[i], dname, updtm);
+ }
+ } else
+ printf("\tUnavailable.\n\n");
+ }
+ tries++;
+ if (status == NIS_SUCCESS) successes++;
+ }
+ if (successes == 0)
+ exit(PING_NONE);
+ if (successes < tries)
+ exit(PING_SOME);
+ exit(PING_SUCCESS);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/bin/nispopulate.sh b/usr/src/cmd/rpcsvc/nis/bin/nispopulate.sh
new file mode 100644
index 0000000000..360c4d3d1f
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/bin/nispopulate.sh
@@ -0,0 +1,1576 @@
+#!/bin/sh
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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) 1992-1997 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# nispopulate -- script to populate NIS+ tables
+
+
+#
+# print_usage(): ask user if they want to see detailed usage msg.
+#
+print_usage()
+{
+ echo
+ get_yesno " Do you want to see more information on this command? \\n\
+ (type 'y' to get a detailed description, 'n' to exit)"
+ if [ $ANS = "y" -o $ANS = "Y" ]
+ then
+ print_more
+ fi
+ echo ""
+ exit 1
+}
+
+print_more()
+{
+ more << EOF
+USAGE:
+ o to populate the table from NIS maps:
+ $PROG -Y [-f] [-n] [-S 0|2] [-u] [-v] [-x] -h <NIS_server_host>
+ [-a <NIS_server_addr>] [-l <network_passwd>]
+ [-d <NIS+_domain>] -y <NIS_domain> [table] ...
+
+ o to populate the table from files:
+ $PROG -F [-f] [-u] [-v] [-x] [-S 0|2] [-d <NIS+_domain>]
+ [-l <network_passwd>] [-p <directory_path>] [table] ...
+
+ o to populate the credential table from hosts/passwd tables:
+ $PROG -C [-f] [-v] [-x] [-d <NIS+_domain>]
+ [-l <network_passwd>] [hosts|passwd]
+OPTIONS:
+ -a <NIS_server_addr>
+ specifies the IP address for the NIS server. This option is
+ *ONLY* used with the "-Y" option.
+
+ -C populate the NIS+ credential table from passwd and hosts tables
+ using DES authentication (security level 2).
+
+ -d <NIS+_domain>
+ specifies the NIS+ domain. The default is the local domain.
+
+ -F populate NIS+ tables from files.
+
+ -f forces this script to populate the NIS+ tables without prompting
+ for confirmation.
+
+ -h <NIS_server_host>
+ specifies the NIS server hostname to copy the NIS map from. This
+ is *ONLY* used with the "-Y" option. This host must be already
+ exist in either the NIS+ hosts table or /etc/hosts file. If the
+ hostname is not defined, this script will prompt you for its IP
+ address.
+
+ -l <network_passwd>
+ specifies the network password for populating the NIS+ credential
+ table. This is *ONLY* used when you are populating the hosts and
+ passwd tables. The default passwd is "nisplus".
+
+ -n do not overwrite local NIS maps in /var/yp/<NISdomain>
+ directory if they already exist. The default is to overwrite
+ the existing NIS maps in the local /var/yp/<NISdomain>
+ directory. This is *ONLY* used with the "-Y" option.
+
+ -p <directory_path>
+ specifies the directory path where the files are stored.
+ This is *ONLY* used with the "-F" option. The default is the
+ current working directory.
+
+ -S 0|2
+ specifies the authentication level for the NIS+ client. Level 0 is
+ for unauthenticated clients and no credentials will be created for
+ users and hosts in the specified domain. Level 2 is for authenticated
+ (DES) clients and DES credentials will be created for users and hosts
+ in the specified domain. The default is to set up with level 2
+ authentication (DES). There is no need to run nispopulate with -C
+ for level 0 authentication.
+
+ -u updates the NIS+ tables (ie., adds, deletes, modifies) from either
+ files or NIS maps. This option should be used to bring an NIS+
+ table up to date when there are only a small number of changes.
+ The default is to add to the NIS+ tables without deleting any
+ existing entries. Also, see the -n option for updating NIS+
+ tables from existing maps in the /var/yp directory.
+
+ -v runs this script in verbose mode.
+
+ -x turns the "echo" mode on. This script just prints the commands
+ that it would have executed. The commands are printed with
+ leading "+++". Note that the commands are not actually executed.
+ The default is off.
+
+ -Y populate the NIS+ tables from NIS maps.
+
+ -y <NIS_domain>
+ specifies the NIS domain to copy the NIS maps from. This is
+ *ONLY* used with the "-Y" option. The default domain name is
+ the same as the local domain name.
+EOF
+}
+
+print_CFY_usage()
+{
+ if [ "$ERRCFY_OPTION" = "$CFY_OPTION" ]
+ then
+ echo "**WARNING: You have specified the '$CFY_OPTION' option twice."
+ return
+ fi
+ echo
+ echo "**ERROR: You have specified the '$ERRCFY_OPTION' option after"
+ echo " having selected the '$CFY_OPTION' option."
+ echo "Please select only one of these options: '-C', '-F' or '-Y'."
+ print_usage
+ exit 1
+}
+
+
+#NOTE:
+#Standard NIS+ table names are:
+#$MAPS shadow
+#(shadow map is only used when populating from files)
+
+
+#
+# Generic Routines:
+# -----------------
+#
+# This section contains general routines.
+# get_ans() - prompts the message and waits for an input
+# get_yesno() - prompts the message and waits for a Y or N answer
+# tolower() - converts upper to lower case.
+#
+
+#
+# get_ans(): gets an asnwer from the user.
+# $1 instruction/comment/description/question
+# $2 default value
+#
+get_ans()
+{
+ if [ -z "$2" ]
+ then
+ if [ $OS -eq 5 ]
+ then
+ echo "$1 \c"
+ else
+ echo -n "$1 "
+ fi
+ else
+ if [ $OS -eq 5 ]
+ then
+ echo "$1 [$2] \c"
+ else
+ echo -n "$1 [$2] "
+ fi
+ fi
+ read ANS
+ if [ -z "$ANS" ]
+ then
+ ANS=$2
+ fi
+}
+
+
+########## get_yesno constants:
+##
+## There are two very common phrases passed to get_yesno:
+## These have been parameterized to provide "ease of use".
+## Thus, there are three message "types" which are possible:
+## --$CONTINUE: "Do you want to continue? (type 'y' to continue, 'n' to exit this script)"
+## --$CONFIRM: "Is this information correct? (type 'y' to accept, 'n' to change)"
+## --actual string is passed.
+##
+## If the message is $CONTINUE, get_yesno will exit if the response is no.
+##
+###########
+CONTINUE=2
+CONFIRM=1
+#
+# get_yesno(): get the yes or no answer.
+# $1 message type or message.
+#
+#
+#
+get_yesno()
+{
+ ANS="X"
+
+ case $1 in
+ $CONTINUE )
+ INFOTEXT="Do you want to continue? (type 'y' to continue, 'n' to exit this script)"
+ ;;
+ $CONFIRM )
+ INFOTEXT="Is this information correct? (type 'y' to accept, 'n' to change)"
+ ;;
+ *) INFOTEXT="$1"
+ ;;
+ esac
+
+ while [ "$ANS" != "y" -a "$ANS" != "n" -a "$ANS" != "Y" -a "$ANS" != "N" ]
+ do
+ get_ans "$INFOTEXT" ""
+ done
+
+ if [ "$1" = "$CONTINUE" ]; then
+ if [ $ANS = "n" -o $ANS = "N" ]
+ then
+ exit
+ fi
+ fi
+
+}
+
+
+#
+# tolower(): converts upper to lower case.
+# $1 string to convert
+#
+tolower()
+{
+ echo "$1" | tr '[A-Z]' '[a-z]'
+}
+
+
+
+#
+# Common Routines:
+# ---------------
+#
+# This section contains common routines for the script.
+# init() - initializes all the variables
+# parse_arg() - parses the command line arguments
+# check_perm() - checks for the write permission for an object
+# get_security() - gets the security information
+# update_info() - updates the setup information
+# print_info() - prints the setup information
+# confirm() - prompts the user for confirmation
+# is_standard() - check if it's a member of the standard maps
+#
+
+#
+# init(): initializes variables and options
+#
+init()
+{
+
+ PROG=`basename $0`
+ CFY_OPTION="" # Keep track of Y, F or C option for error msgs.
+ ERRCFY_OPTION="" # 2nd C, F or Y error option.
+ VERB='> /dev/null' # NULL or "> /dev/null"
+ VERB_OPT='' # verbose option for nisaddent
+ ECHO="eval" # eval or echo
+ BACKUP="no_nisplus"
+ DOM=`nisdefaults -d` # domainname with DOT
+ NODOT=`echo $DOM | sed -e "s/\.$//"`
+ YPDOM="$NODOT" # YPdomain
+
+ ACTION="" # master or replica
+ FORCE="" # NULL or TRUE
+ YPHOST="" # NULL or <YPhostname>
+ DIRPATH="" # directory path where the files are
+ PASSWD="nisplus" # credential password
+ UPDATE="" # nisaddent update option
+ NEW="overwrite" # overwrite local NIS maps
+ NSSWITCH="/etc/nsswitch.conf"
+
+ STANDARD="" # prints either standard or non-standard
+
+ OS=`uname -r | cut -d. -f1`
+ SEC=2 # security level
+ TMPDIR=${TMPDIR:-/tmp} # temporary directory
+ MAPS="auto_master auto_home ethers group hosts ipnodes networks passwd protocols services rpc netmasks bootparams netgroup aliases timezone"
+ MAPS="$MAPS auth_attr exec_attr prof_attr user_attr audit_user"
+
+ if [ $OS -eq 5 ]
+ then
+ HOSTS_FILE=/etc/inet/hosts
+ PATH=/usr/lib/nis:/usr/sbin:/usr/bin:/usr/lib/netsvc/yp:$PATH; export PATH
+ else
+ HOSTS_FILE=/etc/hosts
+ PATH=/usr/etc/nis:/usr/etc:/usr/bin:/usr/etc/yp:$PATH; export PATH
+ fi
+
+}
+
+
+
+
+#
+# parse_arg(): parses the input arguments.
+# It returns the number to be shift in the argument list.
+#
+parse_arg()
+{
+ while getopts "a:Cd:Ffh:l:np:S:uvxYy:" ARG
+ do
+ case $ARG in
+ a) ADDR=$OPTARG;;
+ C) if [ -z "$ACTION" ]
+ then
+ ACTION="cred"
+ CFY_OPTION="-C"
+ else
+ ERRCFY_OPTION="-C"
+ print_CFY_usage
+ fi;;
+
+ d) if [ "`echo $OPTARG | sed -e 's/.*\(.\)$/\1/'`" != "." ]
+ then
+ NODOT=$OPTARG
+ DOM=${NODOT}.
+ else
+ DOM=$OPTARG
+ NODOT=`echo $DOM | sed -e "s/\.$//"`
+ fi ;;
+ F) if [ -z "$ACTION" ]
+ then
+ ACTION="file"
+ CFY_OPTION="-F"
+ else
+ ERRCFY_OPTION="-F"
+ print_CFY_usage
+ fi;;
+ f) FORCE="TRUE";;
+ h) YPHOST=$OPTARG;;
+ l) PASSWD=$OPTARG;;
+ n) NEW="";;
+ p) if [ -d $OPTARG ]
+ then
+ DIRPATH=$OPTARG
+ else
+ echo "**ERROR: directory $OPTARG does not exist."
+ exit 1
+ fi;;
+ S) VALUE=`expr "$OPTARG" : "\([02]\)"`
+ if [ -z "$VALUE" ]
+ then
+ echo "**ERROR: invalid security level."
+ echo " It must be either 0 or 2."
+ echo " This can only be used with -F and -Y options."
+ exit 1
+ fi
+ SEC=$VALUE;;
+ u) UPDATE="-m";;
+ v) VERB=""
+ VERB_OPT="-v";;
+ x) ECHO="echo +++";;
+ Y) if [ -z "$ACTION" ]
+ then
+ ACTION="yp"
+ CFY_OPTION="-Y"
+ else
+ ERRCFY_OPTION="-Y"
+ print_CFY_usage
+ fi;;
+ y) if [ "`echo $OPTARG | sed -e 's/.*\(.\)$/\1/'`" != "." ]
+ then
+ YPDOM=$OPTARG
+ else
+ YPDOM=`echo $OPTARG | sed -e "s/\.$//"`
+ fi ;;
+ \?) print_usage ;;
+ *) echo "**ERROR: Should never get to this point!!!!!"
+ print_usage ;;
+ esac
+ done
+ return `expr $OPTIND - 1`
+}
+
+
+
+
+#
+# get_security(): gets the security information
+#
+get_security()
+{
+ while [ /bin/true ]
+ do
+ get_ans "Security level (2=DES, 3=RSA):" $SEC
+ VALUE=`expr "$ANS" : "\([23]\)"`
+ if [ -z "$VALUE" -o "$VALUE" -lt 2 -o "$VALUE" -gt 3 ]
+ then
+ echo "**ERROR: invalid security level."
+ echo " It must be either 2 or 3."
+ else
+ SEC=$VALUE
+ break
+ fi
+ done
+ SEC=$ANS
+}
+
+
+
+#
+# update_info(): updates the information.
+#
+update_info()
+{
+ echo ""
+ # ...domainname
+ get_ans "NIS+ domain name:" $DOM
+ if [ "`echo $ANS | sed -e 's/.*\(.\)$/\1/'`" != "." ]
+ then
+ NODOT=$ANS
+ DOM=${ANS}.
+ else
+ DOM=${ANS}
+ NODOT=`echo $ANS | sed -e "s/\.$//"`
+ fi
+
+ case $ACTION in
+ "yp") # ...YP domainname
+ while [ /bin/true ]
+ do
+ get_ans "NIS domain name:" $YPDOM
+ if [ ! -z "$ANS" ]
+ then
+ if [ "`echo $ANS | sed -e 's/.*\(.\)$/\1/'`" != "." ]
+ then
+ YPDOM=${ANS}
+ else
+ YPDOM=`echo $ANS | sed -e "s/\.$//"`
+ fi
+ break
+ fi
+ echo "**WARNING: you must specify the NIS domain name."
+ done
+
+ # ...YP host name
+ while [ /bin/true ]
+ do
+ get_ans "NIS Hostname:" $YPHOST
+ if [ ! -z "$ANS" ]
+ then
+ YPHOST=$ANS
+ break
+ fi
+ echo "**WARNING: you must specify the NIS server hostname."
+ done
+ ;;
+ "file") # ...directory path for the files
+ DEFAULT=${DIRPATH:-"(current directory)"}
+ while [ /bin/true ]
+ do
+ get_ans "Directory Path:" "$DEFAULT"
+ if [ "$ANS" != "$DEFAULT" ]
+ then
+ if [ -d $ANS ]
+ then
+ DIRPATH=$ANS
+ break
+ else
+ echo "**ERROR: directory $ANS does not exist."
+ echo " Please try again."
+ fi
+ else
+ break
+ fi
+ done
+ ;;
+ "cred") # ... security level
+ get_security
+
+ # ... credential password
+ get_ans "Credential password:" "$PASSWD"
+ PASSWD=$ANS
+ ;;
+ esac
+}
+
+
+#
+# print_info(): prints the information on the screen.
+#
+print_info()
+{
+ # ...domainname
+ echo "NIS+ domain name : $DOM"
+
+ # ...YP info
+ case $ACTION in
+ "yp") echo "NIS (YP) domain : ${YPDOM:-(not available)}"
+ echo "NIS (YP) server hostname : ${YPHOST:-(not available)}"
+ ;;
+ "file") echo "Directory Path : ${DIRPATH:-(current directory)}"
+ ;;
+ "cred") case $SEC in
+ 0) MESS="0=NO_SEC";;
+ 1) MESS="1=SYS";;
+ 2) MESS="2=DES";;
+ 3) MESS="3=RSA";;
+ *) MESS="INVALID";;
+ esac
+ echo "Security Level : $MESS"
+ echo "Credential Password : $PASSWD"
+ ;;
+ esac
+}
+
+
+#
+# confirm(): asks for user confirmation. If declined, then it will step
+# the user through a question answer session.
+#
+confirm()
+{
+ while [ /bin/true ]
+ do
+ echo ""
+ print_info
+ echo ""
+
+ get_yesno $CONFIRM
+ if [ $ANS = "y" -o $ANS = "Y" ]
+ then
+ return
+ fi
+
+ update_info
+ done
+}
+
+
+
+#
+# check_perm(): checks if we have write permission to the NIS+ object
+# This should be replaced with nisaccess command when it's available
+# $1 the table to be checked.
+#
+check_perm()
+{
+ eval "echo checking $1 permission... $VERB"
+ MYPRINC=`nisdefaults -p`
+ if [ $MYPRINC = "nobody" ]
+ then
+ if nistest -a n=c $1;
+ then
+ return
+ else
+ return 1
+ fi
+ fi
+
+ DUMMY=`nisls -ld $1`
+ if [ $? -ne 0 ]
+ then
+ exit 1
+ fi
+ OWN=`echo $DUMMY | cut -d" " -f3`
+ if [ "$OWN" = $MYPRINC ]
+ then
+ if nistest -a o=c $1;
+ then
+ return
+ else
+ return 1
+ fi
+ fi
+
+ DUMMY=`nisls -ldg $1`
+ if [ $? -ne 0 ]
+ then
+ exit 1
+ fi
+ OWN=`echo $DUMMY | cut -d" " -f3`
+ if [ ! -z "$OWN" ]
+ then
+ if nisgrpadm -t -s "$OWN" $MYPRINC;
+ then
+ if nistest -a g=c $1;
+ then
+ return
+ else
+ return 1
+ fi
+ fi
+ fi
+
+ if nistest -a w=c $1;
+ then
+ return
+ else
+ return 1
+ fi
+}
+
+
+#
+# is_standard(): checks if the argument passed is a member of the standard
+# maps. It returns standard if the argument is a member, else it returns
+# non-standard.
+# $1 the table name to be checked.
+#
+is_standard()
+{
+ for EACHT in $MAPS
+ do
+ V1=`echo "$1" | tr '[A-Z]' '[a-z]'`
+ if [ $V1 = $EACHT ]
+ then
+ echo "standard"
+ return
+ fi
+ done
+ echo "non-standard"
+ return
+}
+
+
+
+#
+# Populate from YP Routines:
+# --------------------------
+#
+# This section contains the routine to populate the table from YP maps.
+# It will copy the maps from the YP server if not already exists in the
+# /var/yp/<YPdomain>.
+# from_yp() - populates the NIS+ tables from YP map.
+#
+
+#
+# yp_trans(): translate a name type to NIS map name for ypxfr.
+# $1 type name
+# NOTE: netid.byname map is not supported.
+#
+yp_trans()
+{
+ if [ $# -eq 0 ]
+ then
+ return
+ fi
+
+ case $1 in
+ "aliases") echo "mail.aliases";;
+ "bootparams") echo "bootparams";;
+ "ethers") echo "ethers.byaddr";;
+ "group") echo "group.byname";;
+ "hosts") echo "hosts.byaddr";;
+ "ipnodes") echo "ipnodes.byaddr";;
+ "netmasks") echo "netmasks.byaddr";;
+ "networks") echo "networks.byname";;
+ "passwd") echo "passwd.byname";;
+ "shadow") echo "passwd.byname";;
+ "protocols") echo "protocols.byname";;
+ "rpc") echo "rpc.bynumber";;
+ "services") echo "services.byname";;
+ "netgroup") echo "netgroup";;
+ "timezone") echo "timezone.byname";;
+
+ auto.*) echo $1;;
+ auto_*) echo `echo $1 | sed -e 's/\([^_]*\)_\(.*\)/\1.\2/'`;;
+ *) echo $1;;
+ esac
+}
+
+
+
+#
+# nisplus_trans(): translate a name type to NIS+ map name for ypxfr.
+# $1 name type
+# NOTE: netid.byname map is not supported.
+#
+nisplus_trans()
+{
+ if [ $# -eq 0 ]
+ then
+ return
+ fi
+
+ case $1 in
+ "shadow") echo "passwd";;
+ "aliases") echo "mail_aliases";;
+ *.*) echo $1 | sed -e 's/\./_/';;
+ *) echo $1;;
+ esac
+}
+
+#
+# print_interrupt_warning
+#
+# used by from_yp & from_files to caution user.
+#
+print_interrupt_warning()
+{
+ echo "**WARNING: Interrupting this script after choosing to continue"
+ echo "may leave the tables only partially populated. This script does"
+ echo "not do any automatic recovery or cleanup."
+ echo ""
+}
+
+#
+# from_yp(): populates the NIS+ tables from YP map.
+#
+from_yp()
+{
+ ERRFOUND=""
+ if [ -z "$FORCE" ]
+ then
+ confirm
+ else
+ echo ""
+ print_info
+ fi
+
+ if [ -z "$YPDOM" -o -z "$YPHOST" ]
+ then
+ echo ""
+ echo "**ERROR: you must specify both the NIS domain name (-y)"
+ echo " and the NIS server hostname (-h)."
+ print_usage
+ fi
+
+ #
+ # Try to determine IP address. If it was specified on the
+ # command line, we use that. If that fails, we try going through
+ # the switch (with the getent command). If that fails, we
+ # look in the /etc/hosts file. If we still don't get it,
+ # we ask. If we have to ask, then we add the IP address
+ # to the hosts file so we won't have to bug them if this
+ # script is run again (adding the entry is a little incorrect,
+ # but quite helpful).
+ #
+
+ # May have to modify at this point to support IPV6
+
+ if [ -z "$ADDR" ]
+ then
+ ADDR=`getent hosts "$YPHOST" | awk '{print $1}'`
+ fi
+
+ if [ -z "$ADDR" ]
+ then
+ ADDR=`grep -s -i "\<$YPHOST\>" $HOSTS_FILE | grep -s -v "^#" | awk '{print $1}'`
+ fi
+
+ if [ -z "$ADDR" ]
+ then
+ get_ans "Type the IP address for NIS (YP) server ${YPHOST}:" ""
+ ADDR=$ANS
+ if [ ! -f $HOSTS_FILE.$BACKUP ]
+ then
+ $ECHO cp $HOSTS_FILE $HOSTS_FILE.$BACKUP
+ fi
+ $ECHO "echo $ADDR $YPHOST >> $HOSTS_FILE"
+ fi
+
+ if [ $# -eq 0 ]
+ then
+ TABLES=$MAPS
+ STANDARD=standard
+ else
+ TABLES=$*
+ STANDARD=following
+ fi
+
+ # ...remove the "." from the domainname
+ YPDOM=`echo $YPDOM | sed -e "s/\.$//"`
+
+
+ echo ""
+ echo "This script will populate the $STANDARD NIS+ tables for domain "
+ echo "$DOM from the NIS (YP) maps in domain ${YPDOM}:"
+ echo $TABLES
+ echo ""
+
+ print_interrupt_warning
+
+ if [ -z "$FORCE" ]
+ then
+ get_yesno $CONTINUE
+ echo ""
+ fi
+
+ if [ "$SEC" != "0" ]
+ then
+ check_nsswitch
+ fi
+
+ if [ ! -d /var/yp/$YPDOM ]
+ then
+ eval "echo creating /var/yp/$YPDOM... $VERB"
+ $ECHO mkdir -p /var/yp/$YPDOM
+ fi
+
+ # ... populating standard files
+ for EACH in $TABLES
+ do
+ # ... check if table exits
+ NISTAB=`nisplus_trans $EACH`
+ if nistest -t T $NISTAB.org_dir.$DOM;
+ then
+ eval "echo $NISTAB.org_dir.$DOM OK... $VERB"
+ else
+ echo "**ERROR($?): table $NISTAB.org_dir.$DOM does not exist."
+ echo " $NISTAB table will not be loaded."
+ ERRFOUND="$ERRFOUND $NISTAB"
+ if [ "$ECHO" = "eval" ]
+ then
+ continue
+ fi
+ fi
+
+ # ...transfer the YP map from YP server
+ YPNAME=`yp_trans $EACH`
+ YPXFR="ypxfr"
+ TMPYPF=$TMPDIR/ypxfr.$$
+ if [ ! -z "$NEW" ]
+ then
+ if [ -f /var/yp/$YPDOM/${YPNAME}.dir ]
+ then
+ eval "echo removing existing local NIS \(YP\) map... $VERB"
+ $ECHO /bin/rm -f /var/yp/$YPDOM/${YPNAME}.*
+ fi
+ eval "echo copying NIS \(YP\) map from server... $VERB"
+ $ECHO "$YPXFR -c -d $YPDOM -h $ADDR $YPNAME > $TMPYPF"
+ if [ $? -ne 0 ]
+ then
+ cat $TMPYPF
+ /bin/rm -f $TMPYPF > /dev/null
+ echo "**ERROR: NIS map transfer failed."
+ echo " $NISTAB table will not be loaded."
+ echo ""
+ ERRFOUND="$ERRFOUND $NISTAB"
+ continue
+ fi
+ /bin/rm -f $TMPYPF > /dev/null
+ else
+ if [ -f /var/yp/$YPDOM/${YPNAME}.dir ]
+ then
+ eval "echo using the existing NIS \(YP\) map... $VERB"
+ else
+ eval "echo copying NIS \(YP\) map from server... $VERB"
+ $ECHO "$YPXFR -c -d $YPDOM -h $ADDR $YPNAME > $TMPYPF"
+ if [ $? -ne 0 ]
+ then
+ cat $TMPYPF
+ /bin/rm -f $TMPYPF > /dev/null
+ echo "**ERROR: NIS map transfer failed."
+ echo " $NISTAB table will not be loaded."
+ echo ""
+ ERRFOUND="$ERRFOUND $NISTAB"
+ continue
+ fi
+ /bin/rm -f $TMPYPF > /dev/null
+ fi
+ fi
+
+ # ...special conversion for netgroup, timezone and auto.master
+ # maps
+ OK="yes"
+ if [ $EACH = "netgroup" -a `tolower $YPDOM` != `tolower $NODOT` ]
+ then
+ eval "echo converting $EACH map... $VERB"
+ TMPFILE=$TMPDIR/netgroup.$$
+ $ECHO "makedbm -u /var/yp/$YPDOM/netgroup \
+ | sed -e s/$YPDOM/$NODOT/g > $TMPFILE"
+ if [ $? -eq 0 ]
+ then
+ eval "echo makedbm -u OK... $VERB"
+ $ECHO makedbm $TMPFILE \
+ /var/yp/$YPDOM/netgroup
+ else
+ OK=""
+ fi
+ if [ $? -ne 0 -o -z "$OK" ]
+ then
+ echo "**WARNING: NIS netgroup map conversion failed."
+ echo " netgroup table will not be loaded."
+ ERRFOUND="$ERRFOUND $NISTAB"
+ $ECHO "/bin/rm -f $TMPFILE > /dev/null"
+ continue
+ fi
+ $ECHO "/bin/rm -f $TMPFILE > /dev/null"
+ elif [ $EACH = "timezone" ]
+ then
+ eval "echo converting $EACH map... $VERB"
+ TMPFILE=$TMPDIR/timezone.$$
+ $ECHO "makedbm -u /var/yp/$YPDOM/timezone.byname \
+ > $TMPFILE"
+ if [ $? -eq 0 ]
+ then
+ eval "echo makedbm -u OK... $VERB"
+ $ECHO "grep -i '[^\.]$NODOT' $TMPFILE > /dev/null"
+ if [ $? -eq 1 ]
+ then
+ LOCAL_TZ=`grep -i "^$YPDOM" $TMPFILE | \
+ cut -d' ' -f2`
+ if [ -z "$LOCAL_TZ" ]
+ then
+ echo "**WARNING: couldn't convert timezone!"
+ echo "Please manually add timezone for $NODOT.\n"
+ else
+ LOCAL_TZ="$LOCAL_TZ $NODOT"
+ fi
+ fi
+ else
+ echo "**WARNING: NIS timezone map conversion failed."
+ echo " timezone table will not be loaded."
+ ERRFOUND="$ERRFOUND $NISTAB"
+ $ECHO "/bin/rm -f $TMPFILE > /dev/null"
+ continue
+ fi
+ $ECHO "/bin/rm -f $TMPFILE > /dev/null"
+ elif [ $NISTAB = "auto_master" ]
+ then
+ eval "echo converting $EACH map... $VERB"
+ TMPFILE=$TMPDIR/auto.master.$$
+ $ECHO "makedbm -u /var/yp/$YPDOM/auto.master \
+ | sed "s/auto./auto_/g" > $TMPFILE"
+ if [ $? -eq 0 ]
+ then
+ eval "echo makedbm -u OK... $VERB"
+ $ECHO makedbm $TMPFILE \
+ /var/yp/$YPDOM/auto.master
+ else
+ OK=""
+ fi
+ if [ $? -ne 0 -o -z "$OK" ]
+ then
+ echo "**WARNING: NIS auto.master map conversion failed."
+ echo " auto.master table will not be loaded."
+ ERRFOUND="$ERRFOUND $NISTAB"
+ $ECHO "/bin/rm -f $TMPFILE > /dev/null"
+ continue
+ fi
+ $ECHO "/bin/rm -f $TMPFILE > /dev/null"
+ fi
+
+ STAND=`is_standard $EACH`
+ echo "populating $NISTAB table from $YPDOM NIS (YP) domain..."
+ if [ `expr "$YPNAME" : 'auto\.'` -eq 5 -o \
+ $STAND = "non-standard" ]
+ then
+ eval "echo adding $STAND key-value table $NISTAB... $VERB"
+ $ECHO nisaddent $VERB_OPT $UPDATE -y $YPDOM \
+ -Y $YPNAME -t $NISTAB.org_dir key-value $DOM
+ else
+ eval "echo adding standard table $NISTAB... $VERB"
+ if [ "$EACH" = "aliases" ]
+ then
+ $ECHO nisaddent $VERB_OPT $UPDATE -y $YPDOM $EACH $DOM
+ else
+ $ECHO nisaddent $VERB_OPT $UPDATE -y $YPDOM $NISTAB $DOM
+ fi
+ fi
+ if [ $? -eq 1 ]
+ then
+ echo "**WARNING: failed to populate $NISTAB table."
+ ERRFOUND="$ERRFOUND $NISTAB"
+ else
+ if [ $NISTAB = "timezone" -a ! -z "$LOCAL_TZ" ]
+ then
+ echo "$LOCAL_TZ" | \
+ nisaddent $VERB_OPT -a $NISTAB $DOM
+ fi
+ if [ $? -eq 1 ]
+ then
+ echo "**WARNING: failed to populate $NISTAB table."
+ ERRFOUND="$ERRFOUND $NISTAB"
+ else
+ echo "$NISTAB table done."
+ if [ $EACH = "hosts" -o $EACH = "ipnodes" -o $EACH = "passwd" ]
+ then
+ if [ "$SEC" != "0" ]
+ then
+ add_cred_auto $EACH
+ fi
+ fi
+ fi
+ fi
+ echo ""
+ done
+
+ do_password_print
+
+ if [ -z "$ERRFOUND" ]
+ then
+ echo ""
+ echo "Done!"
+ else
+ echo ""
+ echo "nispopulate failed to populate the following tables:"
+ echo "$ERRFOUND"
+ exit 1
+ fi
+}
+
+
+
+#
+# Populate from files Routines:
+# -----------------------------
+#
+# This section contains the routine to populate the table from files.
+# from_files() - populates the NIS+ tables from files.
+# $* table types to be added, defaults to all standard tables
+#
+
+#
+# from_files(): populates the NIS+ tables from files.
+#
+from_files()
+{
+ ERRFOUND=""
+ if [ -z "$FORCE" ]
+ then
+ confirm
+ else
+ echo ""
+ print_info
+ fi
+
+ # shadow file is only supported in 5.x
+ if [ $OS -eq 5 ]
+ then
+ MAPS="$MAPS shadow"
+ fi
+
+ if [ $# -eq 0 ]
+ then
+ TABLES=$MAPS
+ STANDARD="standard"
+ else
+ TABLES=$*
+ STANDARD="following"
+ fi
+ echo ""
+ echo "This script will populate the $STANDARD NIS+ tables for domain "
+ echo "$DOM from the files in ${DIRPATH:-current directory}:"
+ echo $TABLES
+ echo ""
+
+ print_interrupt_warning
+
+ if [ -z "$FORCE" ]
+ then
+ get_yesno $CONTINUE
+ echo ""
+ fi
+
+ if [ "$SEC" != "0" ]
+ then
+ check_nsswitch
+ fi
+
+ # ... populating standard files
+ DIRPATH=${DIRPATH:-'.'}
+ for EACH in $TABLES
+ do
+ # ... check if table exits
+ NISTAB=`nisplus_trans $EACH`
+ if nistest -t T $NISTAB.org_dir.$DOM;
+ then
+ eval "echo $NISTAB.org_dir.$DOM OK... $VERB"
+ else
+ echo "**ERROR($?): table $NISTAB.org_dir.$DOM does not exist."
+ echo " $NISTAB table will not be loaded."
+ ERRFOUND="$ERRFOUND $NISTAB"
+ if [ "$ECHO" = "eval" ]
+ then
+ continue
+ fi
+ fi
+
+ if [ -f ${DIRPATH}/$EACH ]
+ then
+ STAND=`is_standard $EACH`
+ echo populating $NISTAB table from file ${DIRPATH}/$EACH...
+ if [ `expr "$EACH" : 'auto[._]'` -eq 5 -o \
+ $STAND = "non-standard" ]
+ then
+ eval "echo adding $STAND key-value table $NISTAB... $VERB"
+ $ECHO nisaddent $VERB_OPT $UPDATE -f \
+ ${DIRPATH}/$EACH -t \
+ $NISTAB.org_dir key-value $DOM
+ else
+ eval "echo adding standard table $NISTAB... $VERB"
+ if [ $EACH = "aliases" -o $EACH = "shadow" ]
+ then
+ $ECHO nisaddent $VERB_OPT $UPDATE -f ${DIRPATH}/$EACH $EACH $DOM
+ else
+ $ECHO nisaddent $VERB_OPT $UPDATE -f ${DIRPATH}/$EACH $NISTAB $DOM
+ fi
+ fi
+ if [ $? -eq 1 ]
+ then
+ echo "**WARNING: failed to populate $NISTAB table."
+ ERRFOUND="$ERRFOUND $NISTAB"
+ else
+ echo "$NISTAB table done."
+ if [ $EACH = "hosts" -o $EACH = "ipnodes" -o $EACH = "passwd" ]
+ then
+ if [ "$SEC" != "0" ]
+ then
+ add_cred_auto $EACH
+ fi
+ fi
+ fi
+ else
+ echo "**WARNING: file ${DIRPATH}/$EACH does not exist!"
+ echo " $NISTAB table will not be loaded."
+ ERRFOUND="$ERRFOUND $NISTAB"
+ fi
+ echo ""
+ done
+
+ do_password_print
+
+ if [ -z "$ERRFOUND" ]
+ then
+ echo ""
+ echo "Done!"
+ else
+ echo ""
+ echo "nispopulate failed to populate the following tables:"
+ echo "$ERRFOUND"
+ exit 1
+ fi
+}
+
+
+
+
+#
+# Populate the credential table Routines:
+# --------------------------------------
+#
+# This section contains the routine to populate the credential table from
+# either passwd or hosts tabls. Default is both passwd and hosts.
+#
+# add_cred() - routine to populate credential
+# add_cred_auto() - routine to populate credential automatically
+# after populating the passwd or hosts table.
+#
+# print_passwd__add_cred
+# print_host__add_cred
+# These routines are used by do_cred to create a shell
+# commands file that contains a routine (add_cred) which
+# is called by each entry in the respective table.
+# do_cred subsequently runs this shell script.
+#
+# do_cred() - populates the NIS+ credential table.
+# $* tables to populate from, defaults to both passwd and hosts
+# tables.
+# do_print_password() - this routine is used to print the
+# password used for the credential entries at the END of
+# populating all tables (per request of tech pubs).
+#
+
+#
+# Check that "publickey: nisplus" appears in nsswitch.conf. We
+# Do this by stripping comments and the running an awk script.
+# The awk script searches for the publickey line and then looks
+# for "nisplus" within the line. It prints:
+#
+# 0 - no publickey entry in nswitch.conf
+# 1 - no "nisplus" in publickey entry
+# 2 - publickey entry is okay (has "nisplus")
+#
+
+check_nsswitch() {
+
+ cat > /tmp/t.$$ <<'EOF'
+ /^[ ]*publickey[ ]*:/ {
+ for (i=2; i<=NF; i++)
+ if ($(i) == "nisplus") {
+ found = 2;
+ exit;
+ }
+ found = 1;
+ exit;
+ }
+ END {
+ if (found)
+ print found
+ else
+ print 0
+ }
+EOF
+
+ if [ ! -f $NSSWITCH ]
+ then
+ echo "**ERROR: the $NSSWITCH file does not exist."
+ exit 1
+ fi
+
+ t=`cat $NSSWITCH | sed 's/#.*//' | awk -f /tmp/t.$$`
+
+ /bin/rm -f /tmp/t.$$
+
+ if [ "$t" -eq 0 ]
+ then
+ echo "**ERROR: there is no publickey entry in $NSSWITCH."
+ echo "It should be:"
+ echo " publickey: nisplus"
+ exit 1
+ elif [ "$t" -eq 1 ]
+ then
+ echo "**ERROR: the publickey entry in $NSSWITCH is:"
+ grep '^[ ]*publickey[ ]*:' $NSSWITCH > /tmp/t.$$
+ echo " `cat /tmp/t.$$`"
+ echo "It should be:"
+ echo " publickey: nisplus"
+ /bin/rm -f /tmp/t.$$
+ exit 1
+ fi
+}
+
+#
+# add_cred(): populate the NIS+ credential from hosts, ipnodes or passwd
+# tables. This is for -C option:
+# $* tables to populate from.
+#
+add_cred()
+{
+ if [ -z "$FORCE" ]
+ then
+ confirm
+ else
+ echo ""
+ print_info
+ fi
+ if [ "$SEC" = "0" ]
+ then
+ echo "***WARNING: no credential will be created for level 0 security."
+ exit
+ fi
+
+ if [ $# -eq 0 ]
+ then
+ TABS="passwd hosts ipnodes"
+ else
+ TABS=$*
+ fi
+
+ echo ""
+ echo "This script will populate the NIS+ credential tables for domain "
+ echo "$DOM from the following table(s): $TABS"
+ echo ""
+
+ if [ -z "$FORCE" ]
+ then
+ get_yesno $CONTINUE
+ echo ""
+ fi
+ do_cred $TABS
+}
+
+#
+# add_cred_auto(): populate the NIS+ credential tables automatically
+# after populating the passwd or hosts tables.
+# $* table to populate from
+#
+add_cred_auto()
+{
+ echo ""
+ echo "Populating the NIS+ credential table for domain $DOM"
+ echo "from $1 table."
+ echo ""
+ do_cred $1
+}
+
+#
+##################################
+# SHELL FILE CREATION ROUTINES
+##################################
+#
+# These routines create the actual cmd files that
+# are used in do_cred to load the passwd, hosts and ipnodes tables.
+#
+##################################
+print_passwd__add_cred()
+{
+### 3 strings are dependent upon user options.
+## as listed under "1." "2. & 3."
+####
+# 1. the form of the nisaddcred command:
+####
+case $SEC in
+2) NISADDCRED="nisaddcred -l $PASSWD -p unix.\$2@$NODOT -P \$1.$DOM des $DOM $VERB"
+ ;;
+3) NISADDCRED="nisaddcred -l $PASSWD -p \$1.$DOM -P \$1.$DOM rsa $DOM $VERB"
+ ;;
+esac
+# 2. & 3. the lines output if verbose has been selected:
+if [ -z "$VERB" ]; then
+ VERB1='echo " ...$1 already exists"'
+ VERB2='echo " ...added $1"'
+else
+ VERB1=""
+ VERB2=""
+fi
+
+cat << EOF > $TMPFILE
+
+###################
+# WHAT THE PASSWORD FILE LOOKS LIKE
+###################
+#! /bin/sh
+
+# \$1 user name
+# \$2 user id
+
+ERR=2
+add_cred()
+{
+ DUMMY=\`nismatch \$1.'$DOM' cred.org_dir.'$DOM' > /dev/null\`
+ if [ \$? -eq 0 ]; then
+ $VERB1
+ return
+ fi
+ nisaddcred -p \$2 -P \$1.$DOM local $DOM $VERB
+ if [ \$? -eq 0 ]; then
+ $NISADDCRED
+ if [ \$? -eq 0 ]; then
+ $VERB2
+ ERR=0
+ else
+ ERR=1
+ fi
+ else
+ ERR=1
+ fi
+}
+
+EOF
+##################
+# END OF PASSWORD FILE
+##################
+}
+
+
+print_host__add_cred()
+{
+### 3 strings are dependent upon user options.
+## as listed under "1." "2. & 3."
+####
+# 1. the form of the nisaddcred command:
+####
+case $SEC in
+2) NISADDCRED="nisaddcred -l $PASSWD -p unix.\$1@$NODOT -P \$1.$DOM des $DOM $VERB"
+ ;;
+3) NISADDCRED="nisaddcred -l $PASSWD -p \$1.$DOM -P \$1.$DOM rsa $DOM $VERB"
+ ;;
+esac
+# 2. & 3. the lines output if verbose has been selected:
+if [ -z "$VERB" ]; then
+ VERB1='echo " ...$1 already exists"'
+ VERB2='echo " ...added $1"'
+else
+ VERB1=""
+ VERB2=""
+fi
+
+cat << EOF > $TMPFILE
+
+###################
+# WHAT THE HOSTS FILE LOOKS LIKE
+###################
+#! /bin/sh
+
+# \$1 host name
+
+ERR=2
+add_cred()
+{
+ DUMMY=\`nismatch \$1.$DOM cred.org_dir.$DOM > /dev/null\`
+ if [ \$? -eq 0 ]; then
+ $VERB1
+ return
+ fi
+ $NISADDCRED
+ if [ \$? -eq 0 ]; then
+ ERR=0
+ $VERB2
+ else
+ ERR=1
+ fi
+}
+
+EOF
+##################
+# END OF HOSTS FILE
+##################
+}
+
+
+#
+#
+# do_cred(): populates the NIS+ credential table.
+#
+do_cred()
+{
+ CRED_ERRFOUND=99
+
+ if [ $# -eq 0 ]
+ then
+ TABLES="passwd hosts ipnodes"
+ else
+ TABLES=$*
+ fi
+
+ # ... populating the credential table
+ for EACH in $TABLES
+ do
+ # ... check if table exits
+ case $EACH in
+ "passwd")
+ TMPFILE=$TMPDIR/passwd_$$
+ print_passwd__add_cred
+
+ echo dumping passwd table...
+ $ECHO "niscat -M passwd.org_dir.$DOM | \
+ awk -F: '{ printf (\"add_cred %s %s\n\", \$1, \$3) }' \
+ >> $TMPFILE"
+ if [ $? -ne 0 ]
+ then
+ DUMP_ERR=1
+ else
+ echo 'exit' >> $TMPFILE
+ DUMP_ERR=0
+ fi
+ ;;
+ "hosts")
+ TMPFILE=$TMPDIR/hosts_$$
+ print_host__add_cred
+
+ echo dumping hosts table...
+ $ECHO "niscat -M hosts.org_dir.$DOM | \
+ awk '{ printf (\"add_cred %s\n\", \$1) }' | \
+ sort | uniq >> $TMPFILE"
+ if [ $? -ne 0 ]
+ then
+ DUMP_ERR=1
+ else
+ echo 'exit' >> $TMPFILE
+ DUMP_ERR=0
+ fi
+ ;;
+ "ipnodes")
+ TMPFILE=$TMPDIR/ipnodes_$$
+ print_host__add_cred
+
+ echo dumping ipnodes table...
+ $ECHO "niscat -M ipnodes.org_dir.$DOM | \
+ awk '{ printf (\"add_cred %s\n\", \$1) }' | \
+ sort | uniq >> $TMPFILE"
+ if [ $? -ne 0 ]
+ then
+ DUMP_ERR=1
+ else
+ echo 'exit' >> $TMPFILE
+ DUMP_ERR=0
+ fi
+ ;;
+ *) echo "Don't know how to do >>$EACH<<"
+ exit;;
+ esac
+
+ $ECHO chmod +x $TMPFILE
+ if [ $DUMP_ERR -eq 0 ]
+ then
+ echo "loading credential table..."
+ $ECHO $TMPFILE
+ CRED_ERRFOUND=$?
+ if [ $CRED_ERRFOUND -eq 0 ]
+ then
+ if [ -z "$CREDTABLESADDED" ]
+ then
+ CREDTABLESADDED=$EACH
+ else
+ CREDTABLESADDED="$CREDTABLESADDED and $EACH"
+ fi
+ fi
+ else
+ echo "**ERROR: failed dumping $EACH table."
+ CRED_ERRFOUND=1
+ fi
+ echo
+ $ECHO "/bin/rm $TMPFILE > /dev/null"
+ done
+
+ if [ $CRED_ERRFOUND -eq 0 ]
+ then
+ echo ""
+ echo "The credential table for domain $DOM has been populated."
+ echo
+ echo "The password used will be $PASSWD."
+ echo
+ else
+ echo ""
+ echo "nispopulate failed to populate the credential table."
+ return 1
+ fi
+}
+
+#
+# Routine to print password for hosts, ipnodes and password table,
+# at END of populating all of the tables.
+#
+do_password_print()
+{
+ if [ ! -z "$CREDTABLESADDED" ]
+ then
+ echo
+ echo
+ echo "Credentials have been added for the entries in the"
+ echo "$CREDTABLESADDED table(s). Each entry was given a default"
+ echo "network password (also known as a Secure-RPC password)."
+ echo "This password is:"
+ echo
+ echo " $PASSWD"
+ echo
+ echo "Use this password when the nisclient script requests the"
+ echo "network password."
+ echo
+ fi
+}
+
+#
+#
+# * * * MAIN * * *
+#
+
+init
+
+parse_arg $*
+shift $?
+
+case $ACTION in
+"yp")
+ from_yp $*;;
+"file")
+ from_files $*;;
+"cred")
+ add_cred $*
+ do_password_print;;
+*)
+ echo
+ echo "**ERROR: you must specify one of these options: -C, -F, or -Y"
+ print_usage
+esac
diff --git a/usr/src/cmd/rpcsvc/nis/bin/nisserver.sh b/usr/src/cmd/rpcsvc/nis/bin/nisserver.sh
new file mode 100644
index 0000000000..9c1cdf6225
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/bin/nisserver.sh
@@ -0,0 +1,1728 @@
+#!/bin/sh
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# nisserver -- script to setup NIS+ servers
+
+nisplus_fmri=network/rpc/nisplus:default
+
+#
+# print_usage(): ask user if they want to see detailed usage msg.
+#
+print_usage()
+{
+ echo
+ get_yesno " Do you want to see more information on this command? \\n\
+ (type 'y' to get a detailed description, 'n' to exit)"
+ if [ $ANS = "n" -o $ANS = "N" ]
+ then
+ echo
+ return 1
+ else
+ print_more
+ fi
+ exit 1
+}
+
+
+
+#
+# print_more(): print the usage message.
+#
+print_more()
+{
+ more << EOF
+USAGE:
+ o to set up root master server:
+ $PROG -r [-f] [-v] [-x] [-Y] [-d <NIS+_domain>]
+ [-g <NIS+_groupname>] [-l <NIS+_passwd>]
+
+ o to set up non-root master server:
+ $PROG -M [-f] [-v] [-x] [-Y] [-d <NIS+_domain>]
+ [-g <NIS+_groupname>] [-h <NIS+_server_host>]
+
+ o to set up replica server:
+ $PROG -R [-f] [-v] [-x] [-Y] [-d <NIS+_domain>]
+ [-h <NIS+_server_host>]
+
+OPTIONS:
+ -d <NIS+_domain>
+ specifies the name for the NIS+ domain. The default is your
+ local domain.
+
+ -f forces the NIS+ server setup without prompting for confirmation.
+
+ -g <NIS+_groupname>
+ specifies the NIS+ group name for the new domain. This option is
+ not valid with "-R" option. The default group is admin.<domain>.
+
+ -h <NIS+_server_host>
+ specifies the hostname for the NIS+ server. It must be a
+ valid host in the local domain. Use a fully qualified
+ hostname (for example, hostx.xyz.sun.com.) to specify a host
+ outside of your local domain. The default for non-root master
+ server setup is to use the same list of servers as the parent
+ domain. The default for replica server setup is the local
+ hostname. This option is *ONLY* used for non-root master or
+ replica servers setup.
+
+ -l <network_password>
+ specifies the network password with which to create the
+ credentials for the root master server. This option is *ONLY*
+ used for master root server setup (-r). If this option is not
+ specified, this script will prompt you for the login password.
+
+ -M sets up the specified host as a non-root master server.
+ Make sure that rpc.nisd(1M) is running on the new master server
+ before this command is executed.
+
+ -R sets up the specified host as a replica server. Make sure that
+ rpc.nisd(1M) is running on the new replica server.
+
+ -r sets up the server as a root master server. Use the -R option
+ to set up a root replica server.
+
+ -v runs this script in verbose mode.
+
+ -x turns the "echo" mode on. This script just prints the commands
+ that it would have executed. The commands are printed with
+ leading "+++". Note that the commands are not actually executed.
+ The default is off.
+
+ -Y sets up an NIS+ server with NIS-compatibility mode. The default
+ is to set up the server without NIS-compatibility mode.
+EOF
+}
+
+
+
+#
+# print MR usage
+#
+print_MRr_usage()
+{
+ if [ "$ERRMRr_OPTION" = "$MRr_OPTION" ]
+ then
+ echo "**WARNING: You have specified the '$MRr_OPTION' option twice."
+ return 0
+ fi
+ echo
+ echo "**ERROR: You have specified the '$ERRMRr_OPTION' option after"
+ echo " having selected the '$MRr_OPTION' option."
+ echo "Please select only one of these options: '-M', '-R', or '-r'."
+ print_usage
+ exit 1
+}
+
+
+
+#
+# Generic Routines:
+# -----------------
+#
+# This section contains general routines.
+# get_ans() - prompts the message and waits for an input
+# get_yesno() - prompts the message and waits for a y or n answer
+# restart_process()
+# - kills and starts a specified process
+# kill_process() - kills a specified process
+# check_dot() - check if the argument ends with a dot
+# check_host() - check if the hostname specified is a valid one
+# tolower(): converts upper to lower case.
+#
+
+#
+# get_ans(): gets an asnwer from the user.
+# $1 instruction/comment/description/question
+# $2 default value
+#
+get_ans()
+{
+ if [ -z "$2" ]
+ then
+ echo "$1 \c"
+ else
+ echo "$1 [$2] \c"
+ fi
+ read ANS
+ if [ -z "$ANS" ]
+ then
+ ANS=$2
+ fi
+}
+
+
+
+########## get_yesno constants:
+##
+## There are two very common phrases passed to get_yesno:
+## These have been parameterized to provide "ease of use".
+## Thus, there are three message "types" which are possible:
+## --$CONTINUE: "Do you want to continue? (type 'y' to continue, 'n' to exit this script)"
+## --$CONFIRM: "Is this information correct? (type 'y' to accept, 'n' to change)"
+## --actual string is passed.
+##
+## If the message is $CONTINUE, get_yesno will exit if the response is no.
+##
+###########
+CONTINUE=2
+CONFIRM=1
+#
+# get_yesno(): get the yes or no answer.
+# $1 message type or message.
+#
+#
+#
+get_yesno()
+{
+ ANS="X"
+
+ case $1 in
+ $CONTINUE )
+ INFOTEXT="Do you want to continue? (type 'y' to continue, 'n' to exit this script)"
+ ;;
+ $CONFIRM )
+ INFOTEXT="Is this information correct? (type 'y' to accept, 'n' to change)"
+ ;;
+ *) INFOTEXT="$1"
+ ;;
+ esac
+
+ while [ "$ANS" != "y" -a "$ANS" != "n" -a "$ANS" != "Y" -a "$ANS" != "N" ]
+ do
+ get_ans "$INFOTEXT" ""
+ done
+
+ if [ "$1" = "$CONTINUE" ]; then
+ if [ $ANS = "n" -o $ANS = "N" ]
+ then
+ exit
+ fi
+ fi
+
+}
+
+
+
+#
+# check_dot(): checks if the argument specified ends with a dot.
+# $1 argument to be checked
+#
+check_dot()
+{
+ if [ "`echo $1 | sed -e 's/.*\(.\)$/\1/'`" != "." ]
+ then
+ return 1
+ fi
+ return 0
+}
+
+
+
+#
+# check_host(): checks if the host specified is in the credential table of
+# the its domain. If it's a valid host, then it'll assign the host
+# principal name to HOSTPRINC.
+# $1 host name (this can be a fully qualified name)
+#
+check_host()
+{
+ if [ -z "$1" ]
+ then
+ return 1
+ fi
+
+ if check_dot $1;
+ then
+ HOSTPRINC=$1
+ MESS="principal"
+ LDOM=`echo $1 | sed -e 's/[^\.]*\.\(.*\)$/\1/'`
+ else
+ LDOM=`nisdefaults -d`
+ HOSTPRINC=$1.$LDOM
+ MESS="host"
+ fi
+
+ nismatch $HOSTPRINC cred.org_dir.$LDOM > /dev/null
+ if [ $? -eq 0 ]
+ then
+ return 1
+ fi
+ echo "**ERROR: the principal name for host $1 is not defined in domain"
+ echo "\"$LDOM\". You must either add the credential for host $1"
+ echo "in domain \"$LDOM\" or specify a fully qualified hostname (with"
+ echo "the ending dot \".\") if the principal name is defined in a"
+ echo "different domain. Use nisclient -c to create the host credential."
+ return 0
+}
+
+
+
+#
+# tolower_single(): converts upper to lower case and single token.
+# Single token means the first token if the argument contains "."
+# dots as in the fully qualified hostname.
+# $1 string to convert
+#
+tolower_single()
+{
+ echo "$1" | tr '[A-Z]' '[a-z]' | cut -d. -f1
+}
+
+
+
+#
+# tolower(): converts upper to lower case.
+# $1 string to convert
+#
+tolower()
+{
+ echo "$1" | tr '[A-Z]' '[a-z]'
+}
+
+
+#
+# smf(5) routines
+# restart_instance() - restart instance or enable if not enabled
+#
+
+#
+# restart_instance [-t] instance_fmri
+#
+restart_instance() {
+ if [ "$1" = "-t" ]; then
+ flag=-t
+ shift
+ else
+ flag=
+ fi
+
+ if [ "`/usr/bin/svcprop -p restarter/state $1`" = "disabled" ];
+ then
+ /usr/sbin/svcadm enable $flag $1
+ else
+ /usr/sbin/svcadm restart $1
+ fi
+}
+
+#
+# Common Routines:
+# ---------------
+#
+# This section contains common routines for master and replica setups for
+# root and non-root domains.
+# init() - initializes all the variables
+# parse_arg() - parses the command line arguments
+# get_security() - gets the security information
+# update_info() - updates the setup information
+# print_info() - prints the setup information
+# confirm() - confirms the setup information
+# setup_domain() - sets up the domain
+# setup_switch() - sets up the switch
+# nis_server() - get the server's name for a domain
+# is_server() - checks if the specified host is already a server
+# for current domain $DOM.
+# check_perm() - checks for the write permission for an object
+# nis_chown() - changes the owner for a domain
+
+
+#
+# init(): initializes variables and options
+#
+init()
+{
+
+ PROG=`basename $0`
+ VERB='> /dev/null' # NULL or "> /dev/null"
+ ECHO="eval" # eval or echo
+ BACKUP=no_nisplus # backup suffix
+ DOM=`nisdefaults -d` # domainname with DOT
+ NODOT=`echo $DOM | sed -e "s/\.$//"`
+ # domainname without DOT
+ SEC=2 # 2=DES or 3=RSA
+ ACTION="" # master or replica
+ ROOT="nonroot" # nonroot or root
+ FORCE="" # NULL or TRUE
+ GROUP="" # NULL or <group-name>
+ HOST="" # NULL or <hostname>
+ YP="" # NULL or -Y
+ DEFSEC=2 # default security
+## The following variable allows for variation in a specific user message:
+ WITHOUT="without" # without YP compatibility
+
+ OS=`uname -r | cut -d. -f1`
+ OSVER=`uname -r | cut -d. -f2`
+ LOCALHOST=`uname -n`
+ PATH=/usr/lib/nis:/usr/sbin:/usr/bin:/usr/lib/netsvc/yp:$PATH; export PATH
+ LOCALHOST=`tolower_single $LOCALHOST`
+}
+
+
+
+#
+# parse_arg(): parses the input arguments.
+# It returns the number to be shift in the argument list.
+#
+parse_arg()
+{
+ while getopts "d:fg:h:l:MRrvxY" ARG
+ do
+ case $ARG in
+ d) if [ "`echo $OPTARG | sed -e 's/.*\(.\)$/\1/'`" != "." ]
+ then
+ NODOT=$OPTARG
+ DOM=${NODOT}.
+ else
+ DOM=$OPTARG
+ NODOT=`echo $DOM | sed -e "s/\.$//"`
+ fi ;;
+ f) FORCE="TRUE";;
+ g) if [ "`echo $OPTARG | sed -e 's/.*\(.\)$/\1/'`" != "." ]
+ then
+ echo "**ERROR: you must specify a fully qualified groupname."
+ exit 1
+ fi
+ GROUP=$OPTARG;;
+ h) HOST=$OPTARG;;
+ l) PASSWD="-l $OPTARG";;
+
+ M) if [ -z "$ACTION" ]
+ then
+ ACTION="master"
+ MRr_OPTION="-M"
+ else
+ ERRMRr_OPTION="-M"
+ print_MRr_usage
+ fi;;
+ R) if [ -z "$ACTION" ]
+ then
+ ACTION="replica"
+ MRr_OPTION="-R"
+ else
+ ERRMRr_OPTION="-R"
+ print_MRr_usage
+ fi;;
+ r) ROOT="root"
+ if [ -z "$ACTION" ]
+ then
+ ACTION="master"
+ MRr_OPTION="-r"
+ else
+ ERRMRr_OPTION="-r"
+ print_MRr_usage
+ fi;;
+ v) VERB="";;
+ x) ECHO="echo +++";;
+ Y) YP="-Y"
+ WITHOUT="with";;
+ \?) print_usage
+ exit 1;;
+ *) echo "**ERROR: Should never get to this point!!!!!"
+ print_usage
+ exit 1;;
+ esac
+ done
+ return `expr $OPTIND - 1`
+}
+
+
+
+#
+# check_rootgrp(): check if the group name specified is an valid group for
+# root master server setup.
+# $1 the groupname to be checked
+#
+check_rootgrp()
+{
+ if [ ! -z "$1" ]
+ then
+ if [ $ROOT = "root" ]
+ then
+ GDOM=`expr "$1" : '[^\.]*\.\(.*\)'`
+ if [ "`tolower $GDOM`" != "`tolower $DOM`" ]
+ then
+ return 1
+ fi
+ fi
+ fi
+}
+
+
+
+#
+# check_domainname(): check validity of a domain name. Currently we check
+# that it has at least two components.
+# $1 the domain name to be checked
+#
+check_domainname()
+{
+ if [ ! -z "$1" ]
+ then
+ t=`expr "$1" : '[^.]\{1,\}[.][^.]\{1,\}'`
+ if [ "$t" = 0 ]
+ then
+ echo '**ERROR: invalid domain name ('$1')'
+ echo ' It must have at least two components.'
+ echo ' For example, "company.com.".'
+ print_usage
+ exit 1
+ fi
+ fi
+}
+
+
+
+#
+# get_security(): gets the security information
+#
+get_security()
+{
+ while [ /bin/true ]
+ do
+ get_ans "Security level (2=DES, 3=RSA):" $SEC
+ VALUE=`expr "$ANS" : "\([23]\)"`
+ if [ -z "$VALUE" -o "$VALUE" -lt 2 -o "$VALUE" -gt 3 ]
+ then
+ echo "**ERROR: invalid security level."
+ echo " It must be either 2 or 3."
+ else
+ SEC=$VALUE
+ break
+ fi
+ done
+ SEC=$ANS
+}
+
+
+
+#
+# update_info(): updates the information.
+#
+update_info()
+{
+ echo ""
+ # ...domainname
+ get_ans "Domain name:" $DOM
+ if [ "`echo $ANS | sed -e 's/.*\(.\)$/\1/'`" != "." ]
+ then
+ NODOT=$ANS
+ DOM=${NODOT}.
+ else
+ DOM=$ANS
+ NODOT=`echo $DOM | sed -e "s/\.$//"`
+ fi
+
+ # ...host name
+ if [ $ACTION = "replica" ]
+ then
+ while [ /bin/true ]
+ do
+ get_ans "NIS+ Hostname:" $HOST
+ if [ "$HOST" = "$ANS" ]
+ then
+ break
+ fi
+ if check_host $ANS;
+ then
+ HOST=$ANS
+ break
+ fi
+ done
+ elif [ $ROOT = "nonroot" ]
+ then
+ DEFAULT=${HOST:-"(use ${PAR_DOM} servers)"}
+ while [ /bin/true ]
+ do
+ get_ans "NIS+ Hostname:" "$DEFAULT"
+ if [ "$ANS" = "$DEFAULT" ]
+ then
+ break
+ fi
+ if check_host $ANS;
+ then
+ HOST=$ANS
+ break
+ fi
+ done
+ fi
+
+ # ...group name
+ if [ $ACTION = "master" ]
+ then
+ DEFAULT=${GROUP:-admin.$DOM}
+ while [ /bin/true ]
+ do
+ get_ans "NIS+ group:" $DEFAULT
+ if [ "$ANS" != "$DEFAULT" ]
+ then
+ if check_dot $ANS;
+ then
+ if check_rootgrp $ANS;
+ then
+ GROUP=$ANS
+ break
+ fi
+ echo "**ERROR: invalid group name."
+ echo " It must be a group in the $DOM domain."
+ continue
+ fi
+ echo "**ERROR: NIS+ group name must end with a \".\"."
+ else
+ break
+ fi
+ done
+ fi
+
+ # ...YP compatibility
+ if [ $ACTION = "master" ]
+ then
+ DEFAULT=1
+ if [ -z "$YP" ]
+ then
+ DEFAULT=0
+ fi
+ ANS=2
+ while [ $ANS -ne 0 -a $ANS -ne 1 ]
+ do
+ get_ans "NIS (YP) compatibility (0=off, 1=on):" $DEFAULT
+ done
+ if [ $ANS -eq 1 ]
+ then
+ YP="-Y"
+ echo ""
+ else
+ YP=""
+ fi
+ fi
+}
+
+#
+# print_info(): prints the information on the screen.
+#
+print_info()
+{
+ # ...domainname
+ echo "Domain name : $DOM"
+
+ # ...hostname
+ if [ $ACTION = "replica" ]
+ then
+ USE_HOST=TRUE
+ echo "NIS+ server : $HOST"
+ elif [ $ROOT = "nonroot" ]
+ then
+ USE_HOST=TRUE
+ echo "NIS+ server : "${HOST:-"(use $PAR_DOM servers)"}
+ fi
+
+ # ...admin group
+ if [ $ACTION = "master" ]
+ then
+ MESS="admin.$DOM"
+ echo "NIS+ group : ${GROUP:-$MESS}"
+ fi
+
+ # ...YP compatibility
+ if [ $ACTION = "master" ]
+ then
+ MESS=ON
+ if [ -z "$YP" ]
+ then
+ MESS="OFF"
+ fi
+ echo "NIS (YP) compatibility : $MESS"
+ fi
+
+ # ...security level
+ if [ $ACTION = "master" ]
+ then
+ case $SEC in
+ 0) MESS="0=NO_SEC";;
+ 1) MESS="1=SYS";;
+ 2) MESS="2=DES";;
+ 3) MESS="3=RSA";;
+ *) MESS="INVALID";;
+ esac
+ echo "Security level : $MESS"
+ fi
+}
+
+
+
+#
+# confirm(): asks for user confirmation. If declined, then it will step
+# the user through a question answer session.
+#
+confirm()
+{
+ while [ /bin/true ]
+ do
+ echo ""
+ print_info
+ echo ""
+
+ get_yesno $CONFIRM
+ if [ $ANS = "y" -o $ANS = "Y" ]
+ then
+ return
+ fi
+
+ update_info
+ done
+}
+
+
+
+#
+# setup_domain(): sets up "domainname" and "/etc/defaultdomain" with the
+# specified domain information.
+#
+setup_domain()
+{
+ echo "setting up domain information \"$DOM\" ..."
+ if [ `nisdefaults -d` != $DOM ]
+ then
+ # NODOT is used to support 4.x YP clients
+ $ECHO domainname $NODOT
+ if [ ! -f /etc/defaultdomain.$BACKUP ]
+ then
+ $ECHO mv /etc/defaultdomain /etc/defaultdomain.$BACKUP
+ fi
+ $ECHO "domainname > /etc/defaultdomain"
+ fi
+ echo ""
+}
+
+
+
+#
+# setup_switch(): copies the nisplus switch configuration file to
+# nsswitch.conf.
+#
+setup_switch()
+{
+ if [ $OS -eq 5 ]
+ then
+ echo "setting up switch information ..."
+ diff /etc/nsswitch.conf /etc/nsswitch.nisplus > /dev/null
+ if [ $? -eq 0 ]
+ then
+ eval "echo switch configuration file already set to use NIS+. $VERB"
+ restart_instance network/rpc/keyserv:default
+ echo ""
+ return
+ fi
+
+ if [ ! -f /etc/nsswitch.conf.$BACKUP ]
+ then
+ $ECHO mv /etc/nsswitch.conf /etc/nsswitch.conf.$BACKUP
+ fi
+ $ECHO cp /etc/nsswitch.nisplus /etc/nsswitch.conf
+ echo ""
+ fi
+
+ $ECHO "rm -f /etc/.rootkey > /dev/null"
+ restart_instance network/rpc/keyserv:default
+}
+
+
+
+#
+# nis_server(): returns the master server for specified domain.
+# $1 domain
+#
+nis_server()
+{
+ niscat -M -o $1 > /tmp/$PROG.$$
+ if [ $? -ne 0 ]
+ then
+ rm -f /tmp/$PROG.$$ > /dev/null
+ exit 1
+ fi
+ ALLSERVERS=""
+ exec < /tmp/$PROG.$$
+ while read LINE
+ do
+ EA=`echo $LINE | sed -n -e "s/^Name : \([^\'].*\)/\1/p"`
+ if [ ! -z "$EA" ]
+ then
+ ALLSERVERS="${ALLSERVERS} $EA"
+ fi
+ done
+ exec < /dev/tty
+ GROUPSERVER=`sed -n -e "s/Group[ ]*: //p" /tmp/$PROG.$$`
+ MASTER_SERVER=`echo $ALLSERVERS | cut -d' ' -f1`
+ rm -f /tmp/$PROG.$$ > /dev/null
+ eval "echo nis_servers: ALLSERVERS=$ALLSERVERS ... $VERB"
+}
+
+
+
+#
+# is_server(): checks if the specified host is already a server for
+# current domain $DOM.
+# $1 server principal
+#
+is_server()
+{
+ V1=`tolower $1`
+ for EA in $ALLSERVERS
+ do
+ if [ "$V1" = "`tolower $EA`" ]
+ then
+ return 0
+ fi
+ done
+ return 1
+}
+
+
+
+#
+# check_perm(): checks if we have write permission to the NIS+ object
+# This should be replaced with nisaccess command when it's available
+# $1 the table to be checked.
+#
+check_perm()
+{
+ if [ "$ECHO" = "echo" ]
+ then
+ return
+ fi
+
+ eval "echo checking $1 permission ... $VERB"
+ MYPRINC=`nisdefaults -p`
+ if [ $MYPRINC = "nobody" ]
+ then
+ if nistest -a n=c $1;
+ then
+ return
+ else
+ return 1
+ fi
+ fi
+
+ DUMMY=`nisls -ld $1`
+ if [ $? -ne 0 ]
+ then
+ exit 1
+ fi
+ OWN=`echo $DUMMY | cut -d" " -f3`
+ if [ "$OWN" = $MYPRINC ]
+ then
+ if nistest -a o=c $1;
+ then
+ return
+ else
+ return 1
+ fi
+ fi
+
+ DUMMY=`nisls -ldg $1`
+ if [ $? -ne 0 ]
+ then
+ exit 1
+ fi
+ OWN=`echo $DUMMY | cut -d" " -f3`
+ if [ ! -z "$OWN" ]
+ then
+ if nisgrpadm -t -s "$OWN" $MYPRINC;
+ then
+ if nistest -a g=c $1;
+ then
+ return
+ else
+ return 1
+ fi
+ fi
+ fi
+
+ if nistest -a w=c $1;
+ then
+ return
+ else
+ return 1
+ fi
+}
+
+
+
+#
+# nis_chown(): changes the owner for the entire domain specified.
+# $1 the new owner's principal
+# $2 the domain
+#
+nis_chown()
+{
+ if [ "$ECHO" = "echo" ]
+ then
+ return
+ fi
+
+ nisls org_dir.$2 > /tmp/${PROG}_nisls.$$
+ sed -e "1d" -e "s/^\(.*\)/nischown $1 \1.org_dir.$2/" \
+ /tmp/${PROG}_nisls.$$ > /tmp/${PROG}_chown.$$
+ chmod +x /tmp/${PROG}_chown.$$
+ /tmp/${PROG}_chown.$$
+ rm -f /tmp/${PROG}_chown.$$ /tmp/${PROG}_nisls.$$ > /dev/null
+ $ECHO nischown $1 org_dir.$2 groups_dir.$2 $2
+}
+
+
+
+#
+# setup_properties(): modifies repository properties such that we run
+# with the proper options.
+# $* list of servers
+#
+setup_properties()
+{
+ if [ ! -z "$YP" -o "$SEC" -ne $DEFSEC ]; then
+ if [ -z "$YP" ]; then
+ eval "echo YP emulation disabled ... $VERB"
+ emulyp_active=`/usr/bin/svcprop -p \
+ application/emulate_yp $nisplus_fmri`
+ [ $? != 0 ] || \
+ if [ "$emulyp_active" = "true" ]; then
+ /usr/sbin/svccfg -s $nisplus_fmri \
+ setprop application/emulate_yp = \
+ boolean: false
+ fi
+ else
+ eval "echo YP emulation enabled ... $VERB"
+ echo ""
+ if /usr/bin/svcprop -q -p application $nisplus_fmri;
+ then :; else
+ /usr/sbin/svccfg -s $nisplus_fmri \
+ addpg application application
+ fi
+ /usr/sbin/svccfg -s $nisplus_fmri \
+ setprop application/emulate_yp = boolean: true
+ fi
+
+ if [ "$SEC" -ne $DEFSEC ]; then
+ eval "echo setting security to $SEC ... $VERB"
+ if /usr/bin/svcprop -q -p application $nisplus_fmri;
+ then :; else
+ /usr/sbin/svccfg -s $nisplus_fmri \
+ addpg application application
+ fi
+ /usr/sbin/svccfg -s $nisplus_fmri \
+ setprop application/security = count: $SEC
+ else
+ eval "echo using default security ... $VERB"
+ /usr/sbin/svccfg -s $nisplus_fmri \
+ delprop application/security
+ fi
+ fi
+
+ # Finally, make nis_cachemgr run with -i for the duration of the
+ # current OS instantiation.
+ #
+ # Create application_ovr property group. Suppress
+ # errors as it may already exist.
+ #
+ if /usr/bin/svcprop -q -p application_ovr $nisplus_fmri; then :; else
+ /usr/sbin/svccfg -s $nisplus_fmri \
+ addpg application_ovr application P
+ fi
+ /usr/sbin/svccfg -s $nisplus_fmri \
+ setprop application_ovr/clear_cache = boolean: true
+
+ /usr/sbin/svcadm refresh network/rpc/nisplus:default
+}
+
+
+#
+# root master setup Routine:
+# -------------------------
+#
+# This section contains the routine to setup a ROOT master server.
+# NOTE: you can only configure your local machine as a root master. You
+# cannot configure other machines across the net.
+# root_master() - sets up the local machine as a root master server
+#
+
+#
+# root_master(): sets up a the local machine as the root master server.
+#
+root_master()
+{
+ WHO=`id | sed -e "s/uid=[0-9]*(\([^ )]*\)).*/\1/"`
+ if [ $WHO != "root" ]
+ then
+ echo "This script must be run as root ..."
+ exit 1
+ fi
+
+ if [ ! -z "$HOST" -a "`tolower_single $HOST`" != "$LOCALHOST" ]
+ then
+ echo "**ERROR: you cannot set up $HOST remotely."
+ echo " To set up $HOST as an NIS+ root master server, run"
+ echo " nisserver on $HOST."
+ exit 1
+ fi
+ HOST=$LOCALHOST
+
+ if [ -z "$SEC" ] # NOTE: This conditional currently always false
+ then
+ echo ""
+ echo "You must specify the security level:"
+ eval "echo get security info ... $VERB"
+ get_security
+ echo ""
+ fi
+
+ echo "This script sets up this machine \"$LOCALHOST\" as an NIS+"
+ echo "root master server for domain $DOM."
+
+ if [ -z "$FORCE" ]
+ then
+ confirm
+ else
+ echo ""
+ print_info
+ fi
+
+ echo ""
+ echo "This script will set up your machine as a root master server for"
+ echo "domain $DOM $WITHOUT NIS compatibility at security level 2."
+ echo ""
+ if [ -f /var/nis/NIS_COLD_START ]
+ then
+ echo "WARNING: this script removes directories and files"
+ echo "related to NIS+ under /var/nis directory with the"
+ echo "exception of the client_info NIS_COLD_START file which"
+ echo "will be renamed to <file>.${BACKUP}. If you want to save"
+ echo "these files, you should abort from this script now to"
+ echo "save these files first."
+ echo ""
+ if [ -d /var/nis/data ]
+ then
+ echo "WARNING: once this script is executed, you will not be able to"
+ echo "restore the existing NIS+ server environment. However, you can"
+ echo "restore your NIS+ client environment using \"nisclient -r\""
+ echo "with the proper domain name and server information."
+ else
+ echo "Use \"nisclient -r\" to restore your NIS+ client environment."
+ fi
+ echo ""
+ else
+ echo "Use \"nisclient -r\" to restore your current network service environment."
+ echo ""
+ fi
+
+ if [ -z "$FORCE" ]
+ then
+ get_yesno $CONTINUE
+ echo ""
+ fi
+
+ setup_domain
+
+ setup_switch
+
+ eval "echo killing NIS and NIS+ processes ... $VERB"
+ /usr/sbin/svcadm disable network/nis/client
+ /usr/sbin/svcadm disable -t network/rpc/nisplus
+ eval "echo stopping nscd ... $VERB"
+ /usr/sbin/svcadm disable -t system/name-service-cache
+ eval "echo '' $VERB"
+
+ eval "echo setup NIS_GROUP environment variable ... $VERB"
+ GROUP=${GROUP:-admin.$DOM}
+ $ECHO "NIS_GROUP=$GROUP; export NIS_GROUP"
+ eval "echo '' $VERB"
+
+ # Save NIS_COLD_START file
+ if [ -f /var/nis/NIS_COLD_START ]
+ then
+ $ECHO cp /var/nis/NIS_COLD_START /var/nis/NIS_COLD_START.$BACKUP
+ fi
+
+ eval "echo rm /var/nis files ... $VERB"
+ $ECHO "rm -f /var/nis/NIS_COLD_START > /dev/null"
+ $ECHO "rm -f /var/nis/NIS_SHARED_DIRCACHE > /dev/null"
+ $ECHO "rm -f /var/nis/.NIS_PRIVATE_DIRCACHE > /dev/null"
+ $ECHO "rm -f /var/nis/client_info > /dev/null"
+ $ECHO "rm -f /var/nis/.pref_servers > /dev/null"
+ $ECHO "rm -f /var/nis/trans.log > /dev/null"
+ $ECHO "rm -f /var/nis/data.dict* > /dev/null"
+ $ECHO "rm -rf /var/nis/data > /dev/null"
+ eval "echo '' $VERB"
+
+ echo "running nisinit ..."
+ $ECHO "nisinit -r"
+ if [ $? -ne 0 ]
+ then
+ echo "**ERROR: it failed to initialize the root server."
+ exit 1
+ fi
+ echo ""
+
+ echo "starting root server at security level 0 to create credentials..."
+ $ECHO "rpc.nisd -S 0"
+ echo ""
+ sleep 2
+
+ echo "running nissetup to create standard directories and tables ..."
+ $ECHO "nissetup $YP"
+ if [ $? -ne 0 ]
+ then
+ echo "**ERROR: it failed to create the tables."
+ exit 1
+ fi
+ if [ $OS -ne 5 -o $OSVER -lt 3 ]
+ then
+ $ECHO "nischmod n+r cred.org_dir.$DOM"
+ fi
+ echo ""
+
+ echo "adding credential for $HOST.$DOM.."
+ case $SEC in
+ 2) $ECHO "nisaddcred $PASSWD des > /dev/null";;
+ 3) $ECHO "nisaddcred $PASSWD rsa > /dev/null";;
+ *) ;;
+ esac
+ if [ $? -ne 0 ]
+ then
+ echo "**ERROR: it failed to add the credential for root."
+ exit 1
+ fi
+ sleep 1
+
+ echo ""
+ echo "creating NIS+ administration group: ${GROUP} ..."
+ $ECHO "nisgrpadm -c $GROUP > /dev/null"
+ if [ $? -ne 0 ]
+ then
+ echo "**WARNING: failed to create the $GROUP group."
+ echo " You will need to create this group manually:"
+ echo " 1. /usr/bin/nisgrpadm -c $GROUP"
+ echo " 2. /usr/bin/nisgrpadm -a $GROUP $HOST.$DOM"
+ else
+ echo "adding principal ${HOST}.${DOM} to $GROUP ..."
+ $ECHO "nisgrpadm -a $GROUP ${HOST}.${DOM} > /dev/null"
+ if [ $? -ne 0 ]
+ then
+ echo "**WARNING: failed to add new member $HOST.$DOM into"
+ echo "the $GROUP group."
+ echo " You will need to add this member manually:"
+ echo " 1. /usr/bin/nisgrpadm -a $GROUP $HOST.$DOM"
+ fi
+ fi
+ echo ""
+
+ eval "echo updating the keys for directories ... $VERB"
+ $ECHO "nisupdkeys $DOM > /dev/null"
+ if [ $? -ne 0 ]
+ then
+ echo "WARNING: nisupdkeys failed on directory $DOM"
+ echo " You will need to run nisupdkeys manually:"
+ echo " 1. /usr/lib/nis/nisupdkeys $DOM"
+ echo ""
+ fi
+ $ECHO "nisupdkeys org_dir.$DOM > /dev/null"
+ if [ $? -ne 0 ]
+ then
+ echo "WARNING: nisupdkeys failed on directory org_dir.$DOM"
+ echo " You will need to run nisupdkeys manually:"
+ echo " 1. /usr/lib/nis/nisupdkeys org_dir.$DOM"
+ echo ""
+ fi
+ $ECHO "nisupdkeys groups_dir.$DOM > /dev/null"
+ if [ $? -ne 0 ]
+ then
+ echo "WARNING: nisupdkeys failed on directory groups_dir.$DOM"
+ echo " You will need to run nisupdkeys manually:"
+ echo " 1. /usr/lib/nis/nisupdkeys groups_dir.$DOM"
+ echo ""
+ fi
+ eval "echo $VERB"
+
+ if [ $OS -ne 5 -o $OSVER -lt 3 ]
+ then
+ eval "echo change group owner for $DOM.. $VERB"
+ $ECHO nischgrp $GROUP $DOM
+ # ... g=rmcd is just a temporary fix for nisinit bug
+ eval "echo add read access for nobody ... $VERB"
+ $ECHO nischmod n+r,g=rmcd $DOM
+ fi
+
+ pkill -z `/sbin/zonename` -x rpc.nisd
+
+ setup_properties
+
+ echo "restarting NIS+ root master server at security level $SEC ..."
+ $ECHO /usr/sbin/svcadm enable network/rpc/nisplus
+
+ eval "echo starting Name Service Cache Daemon nscd ... $VERB"
+ $ECHO /usr/sbin/svcadm enable system/name-service-cache
+
+ echo ""
+ echo "This system is now configured as a root server for domain $DOM"
+ echo "You can now populate the standard NIS+ tables by using the"
+ echo "nispopulate script or /usr/lib/nis/nisaddent command."
+}
+
+
+
+#
+# Non-root master setup Routine:
+# ------------------------------
+#
+# This section contains the routine to setup a non-ROOT master server.
+# NOTE: If the -h <hostname> is specified, then it will configure the
+# specified host as the master of the new domain. Otherwise, it will
+# use the same servers information as in the parent domain.
+# nonroot_master()
+# - sets up a non-root master server.
+#
+
+#
+# nonroot_master(): sets up a nonroot master server.
+#
+nonroot_master()
+{
+ # ... local variables
+ PROMOTE="make"
+
+ # ...check parent domain
+ PAR_DOM=`expr "$DOM" : '[^\.]*\.\(.*\)'`
+ if nistest -t D $PAR_DOM;
+ then
+ :
+ else
+ echo "**ERROR: $PAR_DOM does not exist."
+ exit 1
+ fi
+
+ if [ -z "$SEC" ] # NOTE: This conditional currently always false
+ then
+ echo ""
+ echo "You must specify the security level:"
+ eval "echo get security info ... $VERB"
+ get_security
+ echo ""
+ fi
+
+ if check_host "$HOST";
+ then
+ exit 1
+ fi
+
+ # ...check permission
+ check_perm $PAR_DOM
+ if [ $? -ne 0 ]
+ then
+ echo "**ERROR: no permission to create directory $DOM"
+ exit 1
+ fi
+
+ echo "This script sets up a non-root NIS+ master server for domain"
+ echo "$DOM"
+ if [ -z "$FORCE" ]
+ then
+ confirm
+ else
+ echo ""
+ print_info
+ fi
+
+ echo ""
+ if [ -z "$HOST" ]
+ then
+ HOSTDEF=""
+ echo "This script will set up an NIS+ non-root master for domain"
+ echo "$DOM $WITHOUT NIS compatibility, using the same servers for"
+ echo "domain $PAR_DOM."
+ nis_server $PAR_DOM
+ echo "servers: $ALLSERVERS"
+ else
+ echo "This script sets up machine \"$HOST\" as an NIS+"
+ HOSTDEF="yes"
+ echo "non-root master server for domain $DOM $WITHOUT NIS compatibility."
+ MASTER_SERVER=$HOST
+ ALLSERVERS=$HOST
+ eval "echo $VERB"
+ eval "echo checking rpc.nisd process on $HOST ... $VERB"
+ rpcinfo -u $HOST 100300 3 > /dev/null
+ if [ $? -ne 0 ]
+ then
+ echo "**ERROR: NIS+ server is not running on $HOST."
+ echo " You must do the following before becoming an NIS+ server:"
+ echo " 1. become an NIS+ client of the parent domain or any domain"
+ echo " above the domain which you plan to serve. (nisclient)"
+ echo " 2. start the NIS+ server. (rpc.nisd)"
+ exit 1
+ fi
+ fi
+ echo ""
+
+ if [ -z "$FORCE" ]
+ then
+ get_yesno $CONTINUE
+ echo ""
+ fi
+
+ # ...check domain
+ if nistest -t D $DOM;
+ then
+ echo "**WARNING: domain $DOM already exists."
+ if [ ! -z "$HOST" ]
+ then
+ nis_server $DOM
+ if [ "`tolower $HOSTPRINC`" = "`tolower $MASTER_SERVER`" ]
+ then
+ echo "$HOSTPRINC is already a master server for thisdomain."
+ echo "If you choose to continue with this script, it will"
+ echo "try to create the groups_dir and org_dir directories"
+ echo "for this domain."
+ IGNORE="yes"
+ else
+ is_server $HOSTPRINC
+ if [ $? -eq 0 ]
+ then
+ echo "$HOSTPRINC is already a replica server for this domain."
+ fi
+ echo "If you choose to continue with this script, it will"
+ echo "promote $HOSTPRINC to be the new master for $DOM"
+ PROMOTE="promote new master for"
+ fi
+ else
+ echo "If you choose to continue with this script, it will"
+ echo "try to create the groups_dir and org_dir directories"
+ echo "for this domain."
+ IGNORE="yes"
+ fi
+
+ echo ""
+ if [ -z "$FORCE" ]
+ then
+ get_yesno $CONTINUE
+ echo ""
+ fi
+ fi
+
+
+ eval "echo setup NIS_GROUP environment variable ... $VERB"
+ GROUP=${GROUP:-admin.$DOM}
+ $ECHO "NIS_GROUP=$GROUP; export NIS_GROUP"
+ eval "echo '' $VERB"
+
+ eval "echo running nismkdir ... $VERB"
+ DEF_PERM="-D access=g=rmcd,n=r"
+ if [ -z "$HOST" ]
+ then
+ $ECHO nismkdir $DEF_PERM $DOM
+ else
+ $ECHO nismkdir $DEF_PERM -m $HOSTPRINC $DOM
+ fi
+
+ if [ -z "$IGNORE" -a $? -ne 0 ]
+ then
+ echo "**ERROR: it failed to $PROMOTE the $DOM directory."
+ exit 1
+ fi
+
+ $ECHO "nisupdkeys $DOM > /dev/null"
+ if [ $? -ne 0 ]
+ then
+ echo "**WARNING: nisupdkeys failed on directory $DOM"
+ echo " This script will not be able to continue."
+ echo " Please remove the $DOM directory using 'nisrmdir'."
+ exit 1
+ fi
+
+ $ECHO "nisping $PAR_DOM > /dev/null"
+ sleep 4
+
+ if [ $OS -ne 5 -o $OSVER -lt 3 ]
+ then
+ DEF_PERM="-D access=g=rmcd,n=r"
+ else
+ DEF_PERM=""
+ fi
+ if [ "$PROMOTE" != "make" ]
+ then
+ $ECHO nismkdir $DEF_PERM -m $HOSTPRINC org_dir.$DOM
+ if [ $? -ne 0 ]
+ then
+ echo "**ERROR: it failed to $PROMOTE the org_dir.$DOM directory."
+ exit 1
+ fi
+ $ECHO nismkdir $DEF_PERM -m $HOSTPRINC groups_dir.$DOM
+ if [ $? -ne 0 ]
+ then
+ echo "**ERROR: it failed to $PROMOTE the groups_dir.$DOM directory."
+ exit 1
+ fi
+ fi
+
+ echo "running nissetup ..."
+ $ECHO "nissetup $YP $DOM"
+ if [ -z "$IGNORE" -a $? -ne 0 ]
+ then
+ echo "**ERROR: it failed to create the tables."
+ exit 1
+ fi
+ if [ $OS -ne 5 -o $OSVER -lt 3 ]
+ then
+ $ECHO "nischmod n+r cred.org_dir.$DOM"
+ fi
+ echo ""
+
+ if [ `echo $GROUP | cut -d. -f2-` = $DOM ]
+ then
+ echo "setting NIS+ group to ${GROUP} ..."
+ $ECHO "nisgrpadm -c $GROUP > /dev/null"
+ if [ $? -ne 0 ]
+ then
+ echo "**WARNING: failed to create the $GROUP group."
+ echo " You will need to create this group manually:"
+ echo " 1. /usr/bin/nisgrpadm -c $GROUP"
+ echo " 2. /usr/bin/nisgrpadm -a $GROUP $ALLSERVERS"
+ else
+ $ECHO "nisgrpadm -a $GROUP $ALLSERVERS > /dev/null"
+ if [ $? -ne 0 ]
+ then
+ echo "**WARNING: failed to add the following members into"
+ echo "the $GROUP group:"
+ echo $ALLSERVERS
+ echo ""
+ echo " You will need to add this member manually:"
+ echo " 1. /usr/bin/nisgrpadm -a $GROUP $ALLSERVERS"
+ else
+ $ECHO "nisctl -f g $DOM > /dev/null"
+ fi
+ fi
+ fi
+ echo ""
+
+ eval "echo updating the keys for directories ... $VERB"
+ $ECHO "nisupdkeys org_dir.$DOM > /dev/null"
+ if [ $? -ne 0 ]
+ then
+ echo "WARNING: nisupdkeys failed on directory org_dir.$DOM"
+ echo " You will need to run nisupdkeys manually:"
+ echo " 1. /usr/lib/nis/nisupdkeys org_dir.$DOM"
+ echo ""
+ fi
+ $ECHO "nisupdkeys groups_dir.$DOM > /dev/null"
+ if [ $? -ne 0 ]
+ then
+ echo "WARNING: nisupdkeys failed on directory groups_dir.$DOM"
+ echo " You will need to run nisupdkeys manually:"
+ echo " 1. /usr/lib/nis/nisupdkeys groups_dir.$DOM"
+ echo ""
+ fi
+ eval "echo $VERB"
+
+ if [ ! -z "$HOST" -a "`tolower $HOST`" != "$LOCALHOST" ]
+ then
+ eval "echo changing the owner on the directory ... $VERB"
+ nis_chown $MASTER_SERVER $DOM
+ eval "echo $VERB"
+ fi
+
+ if [ ! -z "$HOSTDEF" ]
+ then
+ setup_properties
+ echo ""
+ fi
+
+ # start rpc.nispasswdd if setting up on localhost
+ if [ ! -z "$HOST" -a "`tolower $HOST`" = "$LOCALHOST" ]
+ then
+ # check to see if already running...
+ zone=`/usr/bin/zonename`
+ PROC=`pgrep -z $zone rpc.nispasswdd`
+ if [ -z "$PROC" ]
+ then
+ # We are displaying this message for
+ # compatibility; at present, rpc.nispasswdd(1M)
+ # was started by our enabling of
+ # network/rpc/nisplus. It may or may not have
+ # exited by this point.
+ eval "echo starting NIS+ password daemon ... $VERB"
+ else
+ eval "echo NIS+ password daemon already running ... $VERB"
+ fi
+ else
+ # else need to print message saying start it up
+ echo ""
+ echo "**IMPORTANT:"
+ echo " Be sure to start the NIS+ password daemon (rpc.nispasswdd) on the"
+ if [ -z "$HOST" ]
+ then
+ echo " new NIS+ non-root (subdomain) master server IF NOT ALREADY."
+ else
+ echo " new NIS+ non-root (subdomain) master server $HOST IF NOT ALREADY."
+ fi
+ echo ""
+ fi
+
+ echo ""
+ echo "The server(s) for the non-root domain $DOM is(are) now"
+ echo "configured. You can now populate the standard NIS+ tables by"
+ echo "using the nispopulate or /usr/lib/nis/nisaddent commands."
+}
+
+
+
+#
+# replica setup Routine:
+# ----------------------
+#
+# This section contains the routine to setup a replica server.
+# NOTE: If the -h <hostname> is specified, then it will configure the
+# specified host as a replica of the domain. Otherwise, it will
+# configure the local machine as a replica of the domain.
+# replica() - sets up replica server.
+#
+#
+# replica(): sets up a replica server.
+#
+replica()
+{
+ # ...check domain
+ if nistest -t D $DOM;
+ then
+ :
+ else
+ echo "**ERROR: $DOM does not exist."
+ exit 1
+ fi
+
+ if check_host "$HOST";
+ then
+ exit 1
+ fi
+
+ # ...check permission
+ check_perm $DOM
+ if [ $? -ne 0 ]
+ then
+ echo "**ERROR: no permission to replicate directory $DOM"
+ exit 1
+ fi
+
+ echo "This script sets up an NIS+ replica server for domain"
+ echo "$DOM"
+
+ if [ -z "$HOST" ]
+ then
+ HOST=$LOCALHOST
+ HOSTPRINC=$HOST.`nisdefaults -d`
+ fi
+
+ if [ -z "$FORCE" ]
+ then
+ confirm
+ else
+ echo ""
+ print_info
+ fi
+
+ echo ""
+ nis_server $DOM
+ if [ "`tolower $HOSTPRINC`" = "`tolower $MASTER_SERVER`" ]
+ then
+ echo "ERROR: $HOST is a master server for this domain."
+ echo "You cannot demote a master server to replica."
+ echo "If you really want to demote this master, you should"
+ echo "promote a replica server to master using nisserver"
+ echo "with the -M option."
+ exit 1
+ fi
+
+ is_server $HOSTPRINC
+ if [ $? -eq 0 ]
+ then
+ echo "WARNING: $HOST is already a server for this domain."
+ echo "If you choose to continue with this script, it will"
+ echo "try to replicate the groups_dir and org_dir directories"
+ echo "for this domain."
+ IGNORE="yes"
+ else
+ echo "This script will set up machine \"$HOST\" as an NIS+"
+ echo "replica server for domain $DOM $WITHOUT NIS compatibility."
+ echo "The NIS+ server daemon, rpc.nisd, must be running on $HOST"
+ echo "with the proper options to serve this domain."
+ fi
+ echo ""
+
+ if [ -z "$FORCE" ]
+ then
+ get_yesno $CONTINUE
+ echo ""
+ fi
+
+ $ECHO "rpcinfo -u $HOST 100300 3 > /dev/null"
+ if [ $? -ne 0 ]
+ then
+ echo "**ERROR: NIS+ server is not running on $HOST."
+ echo " You must do the following before becoming an NIS+ server:"
+ echo " 1. become an NIS+ client of the parent domain or any domain"
+ echo " above the domain which you plan to serve. (nisclient)"
+ echo " 2. start the NIS+ server. (rpc.nisd)"
+ exit 1
+ fi
+
+ eval "echo running nismkdir ... $VERB"
+ $ECHO nismkdir -s $HOSTPRINC $DOM
+ if [ -z "$IGNORE" -a $? -ne 0 ]
+ then
+ echo "**ERROR: it failed to replicate the directory."
+ exit 1
+ fi
+ sleep 3
+ $ECHO "nisupdkeys $DOM > /dev/null"
+ if [ $? -ne 0 ]
+ then
+ echo "**WARNING: nisupdkeys failed on directory $DOM"
+ echo " This script will not be able to continue."
+ echo " Please remove the $DOM directory using 'nisrmdir'."
+ exit 1
+ fi
+
+ $ECHO nismkdir -s $HOSTPRINC org_dir.$DOM
+ if [ -z "$IGNORE" -a $? -ne 0 ]
+ then
+ echo "**ERROR: it failed to replicate the org_dir directory."
+ exit 1
+ fi
+ sleep 3
+ $ECHO nismkdir -s $HOSTPRINC groups_dir.$DOM
+ if [ -z "$IGNORE" -a $? -ne 0 ]
+ then
+ echo "**ERROR: it failed to replicate the groups_dir directory."
+ exit 1
+ fi
+ sleep 3
+
+ eval "echo updating the keys for directories ... $VERB"
+ $ECHO "nisupdkeys org_dir.$DOM > /dev/null"
+ if [ $? -ne 0 ]
+ then
+ echo "WARNING: nisupdkeys failed on directory org_dir.$DOM"
+ echo " You will need to run nisupdkeys manually:"
+ echo " 1. /usr/lib/nis/nisupdkeys org_dir.$DOM"
+ echo ""
+ fi
+ $ECHO "nisupdkeys groups_dir.$DOM > /dev/null"
+ if [ $? -ne 0 ]
+ then
+ echo "WARNING: nisupdkeys failed on directory groups_dir.$DOM"
+ echo " You will need to run nisupdkeys manually:"
+ echo " 1. /usr/lib/nis/nisupdkeys groups_dir.$DOM"
+ echo ""
+ fi
+ eval "echo $VERB"
+
+ if [ ! -z "$GROUPSERVER" ]
+ then
+ if nisgrpadm -s -t $GROUPSERVER $HOSTPRINC;
+ then
+ :
+ else
+ eval "echo adding replica principal into group owner ...$VERB"
+ $ECHO nisgrpadm -a $GROUPSERVER $HOSTPRINC
+ $ECHO "nisctl -f g $DOM > /dev/null"
+ fi
+ fi
+
+ eval "echo pinging $DOM directory object on new replica ... $VERB"
+ $ECHO "nisping -H ${HOST} $DOM > /dev/null"
+ sleep 10
+ eval "echo pinging $DOM groups_dir object on new replica ... $VERB"
+ $ECHO "nisping -H ${HOST} groups_dir.$DOM > /dev/null"
+ sleep 10
+ eval "echo pinging $DOM org_dir object on new replica ... $VERB"
+ $ECHO "nisping -H ${HOST} org_dir.$DOM > /dev/null"
+
+ echo ""
+ echo "The system ${HOST} is now configured as a replica server for"
+ echo "domain $DOM."
+ echo "The NIS+ server daemon, rpc.nisd, must be running on $HOST"
+ echo "with the proper options to serve this domain."
+ echo ""
+ echo "If you want to run this replica in NIS (YP) compatibility"
+ echo "mode, you must ensure that rpc.nisd on $HOST will boot in"
+ echo "NIS-compatibility mode. Then, restart rpc.nisd with the"
+ echo "-Y' option. These actions should be taken after this"
+ echo "script completes."
+}
+
+
+
+#
+#
+# * * * MAIN * * *
+#
+
+# Display the obsolescence message in all the cases
+echo ""
+echo "******** ******** WARNING ******** ********"
+echo "NIS+ might not be supported in a future release. Tools to aid"
+echo "the migration from NIS+ to LDAP are available in the Solaris 9"
+echo "operating environment. For more information, visit"
+echo "http://www.sun.com/directory/nisplus/transition.html"
+echo "******** ******** ******* ******** ********"
+echo ""
+
+init
+
+parse_arg $*
+shift $?
+
+check_domainname "$DOM"
+
+check_rootgrp "$GROUP"
+if [ $? -ne 0 ]
+then
+ echo "**ERROR: invalid group name."
+ echo " It must be a group in the $DOM domain."
+ exit 1
+fi
+
+
+case $ACTION in
+"master")
+ ${ROOT}_master;;
+"replica")
+ replica;;
+*)
+ echo "**ERROR: you must specify one of these options: -r, -M or -R"
+ print_usage
+ exit 1
+esac
+
+# As this operation is likely configuration changing, restart the
+# name-services milestone (such that configuration-sensitive services
+# are in turn restarted).
+/usr/sbin/svcadm restart milestone/name-services
diff --git a/usr/src/cmd/rpcsvc/nis/bin/nissetup.sh b/usr/src/cmd/rpcsvc/nis/bin/nissetup.sh
new file mode 100644
index 0000000000..8ba5a11359
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/bin/nissetup.sh
@@ -0,0 +1,448 @@
+#!/bin/sh
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 1992-2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Initial setup for NIS+ domains. Note this only needs to be run on those
+# domains that you plan to make into user level domains (that is domains
+# with host maps and passwds etc)
+#
+
+USAGE="usage: nissetup [-Y] [domain]";
+PASSOFF="n-r"
+
+if [ $# -eq 0 ]
+then
+ A=access=og=rmcd,nw=r;
+ D=`nisdefaults -d`;
+else
+ case $1 in
+ -Y)
+ if [ $# -eq 1 ]
+ then
+ D=`nisdefaults -d`;
+ elif [ $# -eq 2 ]
+ then
+ D=$2;
+ else
+ echo $USAGE;
+ exit 1;
+ fi;
+ A=access=og=rmcd,nw=r;
+ PASSOFF="";; # allow "nobody" read access in YP mode
+ -*)
+ echo $USAGE;
+ exit 1;;
+ *)
+ if [ $# -ne 1 ]
+ then
+ echo $USAGE;
+ exit 1;
+ fi;
+ A=access=og=rmcd,nw=r;
+ D=$1;;
+ esac;
+fi;
+
+# column access for most tables (passwd excluded, see below)
+CA=nogw=
+
+if nistest org_dir.$D;
+then
+ if nistest -t D org_dir.$D;
+ then
+ echo org_dir.$D already exists;
+ else
+ echo org_dir.$D exists and is not a directory
+ exit 1;
+ fi;
+else
+ if nismkdir -D $A org_dir.$D;
+ then
+ echo org_dir.$D created;
+ else
+ echo couldn\'t create org_dir.$D
+ exit 1;
+ fi;
+fi;
+
+if nistest groups_dir.$D;
+then
+ if nistest -t D groups_dir.$D;
+ then
+ echo groups_dir.$D already exists;
+ else
+ echo groups_dir.$D exists and is not a directory
+ exit 1;
+ fi;
+else
+ if nismkdir -D $A groups_dir.$D;
+ then
+ echo groups_dir.$D created;
+ else
+ echo couldn\'t create groups_dir.$D
+ exit 1;
+ fi;
+fi;
+
+
+# Table creation order is arbitrary. We do passwd, group, then
+# other standard tables in alphabetical order.
+#
+# Default secure setup passwd table perms is og=rmcd,w=r: ----rmcdrmcdr---
+#
+# Column Perm Update Final Perms Notes
+# ====== =========== =========== =====
+# (login) name: n+r,og-mcd r---r---r---r---
+# passwd: og-mcd ----r---r---r--- 2.5+; see (5)
+# o-cd,g-mcd ----rm--r---r--- [def] 2.0-2.4; see (6)
+# uid, gid: n+r,og-mcd r---r---r---r---
+# gcos, home, shell: n+r r---rmcdrmcdr---
+# shadow: og-rmcd ---------------- see (7)
+#
+# Notes:
+# (1) The entire table is readable to authenticated users.
+# (2) Unauthenticated users can still see the "informational" columns,
+# but not the passwd or shadow columns. Unauthenticated clients
+# must have read access to the informational columns in order for
+# programs looking up this info while running as "daemon" to work.
+# (3) Entry owner can modify gcos, home, and shell; passwd for 2.0-2.4.
+# (4) In the YP compat service setup, the table-level permissions get
+# n+r added. This is required for YP client access, but overrides
+# the hiding of the passwd column from unauthenticated clients.
+# (5) The passwd column for 2.5 clients only: rpc.nispasswdd (NPD)
+# modifies entries... must manually set o-m permission
+# (6) The passwd column for 2.0-2.4: to support old clients
+# (i.e. no NPD). This is the DEFAULT setup.
+# (7) The shadow column contains password aging info;
+# no added perms above those on table.
+
+if nistest passwd.org_dir.$D;
+then
+ echo passwd.org_dir.$D already exists;
+else
+ if nistbladm -D $A,$PASSOFF -c -s : passwd_tbl name=S,nogw=r passwd=C,n=,ogw=r,o+m uid=S,nogw=r gid=,nogw=r gcos=,n+r,o+m home=,n+r shell=,n+r shadow=,nogw= passwd.org_dir.$D;
+
+ then
+ echo passwd.org_dir.$D created;
+ else
+ echo couldn\'t create passwd.org_dir.$D;
+ fi;
+fi;
+
+
+#group table permissions for default secure setup
+# table ----rmcdrmcdr---
+# passwd column o+m
+# members column o+m,n+r
+# all other columns n+r
+#
+#Explanation is same as for passwd table above.
+#
+
+if nistest group.org_dir.$D;
+then
+ echo group.org_dir.$D already exists;
+else
+ if nistbladm -D $A,$PASSOFF -c -s : group_tbl name=S,n+r passwd=C,o+m gid=S,n+r members=,o+m,n+r group.org_dir.$D;
+ then
+ echo group.org_dir.$D created;
+ else
+ echo couldn\'t create group.org_dir.$D;
+ fi;
+fi;
+
+# permissions on all other tables for default secure setup is:
+# A=access=og=rmcd,w=r,n=r;
+# this is to allow all processes run as 'daemon' to have access to
+# the information stored in the NIS+ tables.
+
+if nistest auto_master.org_dir.$D;
+then
+ echo auto_master.org_dir.$D already exists;
+else
+ if nistbladm -D $A -c automount_map key=S,$CA value=,$CA auto_master.org_dir.$D;
+ then
+ echo auto_master.org_dir.$D created;
+ else
+ echo couldn\'t create auto_master.org_dir.$D;
+ fi;
+fi;
+
+if nistest auto_home.org_dir.$D;
+then
+ echo auto_home.org_dir.$D already exists;
+else
+ if nistbladm -D $A -c automount_map key=S,$CA value=,$CA auto_home.org_dir.$D;
+ then
+ echo auto_home.org_dir.$D created;
+ else
+ echo couldn\'t create auto_home.org_dir.$D;
+ fi;
+fi;
+
+if nistest bootparams.org_dir.$D;
+then
+ echo bootparams.org_dir.$D already exists;
+else
+ if nistbladm -D $A -c bootparams_tbl key=SI,$CA value=,$CA bootparams.org_dir.$D;
+ then
+ echo bootparams.org_dir.$D created;
+ else
+ echo couldn\'t create bootparams.org_dir.$D;
+ fi;
+fi;
+
+if nistest cred.org_dir.$D;
+then
+ echo cred.org_dir.$D already exists;
+else
+ if nistbladm -D $A -c -s : cred_tbl cname=SI,$CA auth_type=SI,$CA auth_name=SI,$CA public_data=,$CA,o+m private_data=C,$CA,o+m cred.org_dir.$D;
+ then
+ echo cred.org_dir.$D created;
+ else
+ echo couldn\'t create cred.org_dir.$D;
+ fi;
+fi;
+
+if nistest ethers.org_dir.$D;
+then
+ echo ethers.org_dir.$D already exists;
+else
+ if nistbladm -D $A -c ethers_tbl addr=SI,$CA name=SI,$CA comment=,$CA ethers.org_dir.$D;
+ then
+ echo ethers.org_dir.$D created;
+ else
+ echo couldn\'t create ethers.org_dir.$D;
+ fi;
+fi;
+
+if nistest hosts.org_dir.$D;
+then
+ echo hosts.org_dir.$D already exists;
+else
+ if nistbladm -D $A -c hosts_tbl cname=SI,$CA name=SI,$CA addr=SI,$CA comment=,$CA hosts.org_dir.$D;
+ then
+ echo hosts.org_dir.$D created;
+ else
+ echo couldn\'t create hosts.org_dir.$D;
+ fi;
+fi;
+
+if nistest ipnodes.org_dir.$D;
+then
+ echo ipnodes.org_dir.$D already exists;
+else
+ if nistbladm -D $A -c ipnodes_tbl cname=SI,$CA name=SI,$CA addr=SI,$CA comment=,$CA ipnodes.org_dir.$D;
+ then
+ echo ipnodes.org_dir.$D created;
+ else
+ echo couldn\'t create ipnodes.org_dir.$D;
+ fi;
+fi;
+
+if nistest mail_aliases.org_dir.$D;
+then
+ echo mail_aliases.org_dir.$D already exists;
+else
+ if nistbladm -D $A -c mail_aliases alias=SI,$CA expansion=SI,$CA comments=,$CA options=,$CA mail_aliases.org_dir.$D;
+ then
+ echo mail_aliases.org_dir.$D created;
+ else
+ echo couldn\'t create mail_aliases.org_dir.$D;
+ fi;
+fi;
+
+if nistest netmasks.org_dir.$D;
+then
+ echo netmasks.org_dir.$D already exists;
+else
+ if nistbladm -D $A -c netmasks_tbl addr=SI,$CA mask=SI,$CA comment=,$CA netmasks.org_dir.$D;
+ then
+ echo netmasks.org_dir.$D created;
+ else
+ echo couldn\'t create netmasks.org_dir.$D;
+ fi;
+fi;
+
+if nistest netgroup.org_dir.$D;
+then
+ echo netgroup.org_dir.$D already exists;
+else
+ if nistbladm -D $A -c netgroup_tbl name=S,$CA group=S,$CA host=SI,$CA user=S,$CA domain=SI,$CA comment=,$CA netgroup.org_dir.$D;
+ then
+ echo netgroup.org_dir.$D created;
+ else
+ echo couldn\'t create netgroup.org_dir.$D;
+ fi;
+fi;
+
+if nistest networks.org_dir.$D;
+then
+ echo networks.org_dir.$D already exists;
+else
+ if nistbladm -D $A -c networks_tbl cname=SI,$CA name=SI,$CA addr=SI,$CA comment=,$CA networks.org_dir.$D;
+ then
+ echo networks.org_dir.$D created;
+ else
+ echo couldn\'t create networks.org_dir.$D;
+ fi;
+fi;
+
+
+if nistest protocols.org_dir.$D;
+then
+ echo protocols.org_dir.$D already exists;
+else
+ if nistbladm -D $A -c protocols_tbl cname=SI,$CA name=SI,$CA number=SI,$CA comment=,$CA protocols.org_dir.$D;
+ then
+ echo protocols.org_dir.$D created;
+ else
+ echo couldn\'t create protocols.org_dir.$D;
+ fi;
+fi;
+
+if nistest rpc.org_dir.$D;
+then
+ echo rpc.org_dir.$D already exists;
+else
+ if nistbladm -D $A -c rpc_tbl cname=SI,$CA name=SI,$CA number=SI,$CA comment=,$CA rpc.org_dir.$D;
+ then
+ echo rpc.org_dir.$D created;
+ else
+ echo couldn\'t create rpc.org_dir.$D;
+ fi;
+fi;
+
+if nistest services.org_dir.$D;
+then
+ echo services.org_dir.$D already exists;
+else
+ if nistbladm -D $A -c services_tbl cname=SI,$CA name=SI,$CA proto=SI,$CA port=SI,$CA comment=,$CA services.org_dir.$D;
+ then
+ echo services.org_dir.$D created;
+ else
+ echo couldn\'t create services.org_dir.$D;
+ fi;
+fi;
+
+if nistest timezone.org_dir.$D;
+then
+ echo timezone.org_dir.$D already exists;
+else
+ if nistbladm -D $A -c timezone_tbl name=SI,$CA tzone=,$CA comment=,$CA timezone.org_dir.$D;
+ then
+ echo timezone.org_dir.$D created;
+ else
+ echo couldn\'t create timezone.org_dir.$D;
+ fi;
+fi;
+
+# client_info table: this table is used to store client's server discovery
+# information such as preferred servers and preferred option.
+#
+if nistest client_info.org_dir.$D;
+then
+ echo client_info.org_dir.$D already exists;
+else
+ if nistbladm -D group=admin.$D:$A -c client_info_tbl client=SI,$CA attr=SI,$CA info=,$CA flags=,$CA client_info.org_dir.$D
+ then
+ echo client_info.org_dir.$D created;
+ else
+ echo couldn\'t create client_info.org_dir.$D;
+ fi;
+fi;
+
+# auth_attr table: This table is used to store authorization attributes
+#
+if nistest auth_attr.org_dir.$D;
+then
+ echo auth_attr.org_dir.$D already exists;
+else
+ if nistbladm -D group=admin.$D:$A -c auth_attr_tbl name=S,$CA res1=,$CA res2=,$CA short_desc=,$CA long_desc=,$CA attr=,$CA auth_attr.org_dir.$D
+ then
+ echo auth_attr.org_dir.$D created;
+ else
+ echo couldn\'t create auth_attr.org_dir.$D;
+ fi;
+fi;
+
+# exec_attr table: This table is used to store execution attributes
+#
+if nistest exec_attr.org_dir.$D;
+then
+ echo exec_attr.org_dir.$D already exists;
+else
+ if nistbladm -D group=admin.$D:$A -c exec_attr_tbl name=S,$CA policy=S,$CA type=,$CA res1=,$CA res2=,$CA id=S,$CA attr=,$CA exec_attr.org_dir.$D
+ then
+ echo exec_attr.org_dir.$D created;
+ else
+ echo couldn\'t create exec_attr.org_dir.$D;
+ fi;
+fi;
+
+# prof_attr table: This table is used to store profile attributes
+#
+if nistest prof_attr.org_dir.$D;
+then
+ echo prof_attr.org_dir.$D already exists;
+else
+ if nistbladm -D group=admin.$D:$A -c prof_attr_tbl name=S,$CA res1=,$CA res2=,$CA desc=,$CA attr=,$CA prof_attr.org_dir.$D
+ then
+ echo prof_attr.org_dir.$D created;
+ else
+ echo couldn\'t create prof_attr.org_dir.$D;
+ fi;
+fi;
+
+# user_attr table: This table is used to store extended user attributes
+#
+if nistest user_attr.org_dir.$D;
+then
+ echo user_attr.org_dir.$D already exists;
+else
+ if nistbladm -D group=admin.$D:$A -c user_attr_tbl name=S,$CA qualifier=S,$CA res1=,$CA res2=,$CA attr=,$CA user_attr.org_dir.$D
+ then
+ echo user_attr.org_dir.$D created;
+ else
+ echo couldn\'t create user_attr.org_dir.$D;
+ fi;
+fi;
+
+# audit_user table: This table is used to store audit attributes for users
+#
+if nistest audit_user.org_dir.$D;
+then
+ echo audit_user.org_dir.$D already exists;
+else
+ if nistbladm -D group=admin.$D:access=og=rmcd,nw= -c audit_user_tbl name=S,$CA always=,$CA never=,$CA audit_user.org_dir.$D
+ then
+ echo audit_user.org_dir.$D created;
+ else
+ echo couldn\'t create audit_user.org_dir.$D;
+ fi;
+fi;
+exit 0;
diff --git a/usr/src/cmd/rpcsvc/nis/bin/nisshowcache.c b/usr/src/cmd/rpcsvc/nis/bin/nisshowcache.c
new file mode 100644
index 0000000000..f73c65ef9e
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/bin/nisshowcache.c
@@ -0,0 +1,176 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * nisshowcache.c
+ *
+ * Copyright (c) 1988-1995 Sun Microsystems, Inc.
+ * All Rights Reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Ported from
+ * "@(#)dump_cache.c 1.5 90/11/13 Copyr 1988 Sun Micro";
+ *
+ * This program dumps the contents of the NIS+ location cache
+ * to the standard output.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <rpcsvc/nis.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+
+#ifndef _sys_sem_h
+/*
+ * This guard is defined only in 4.1 systems and is to
+ * be included only in 5.0 as it is not defined there.
+ */
+union semun {
+ int val;
+ struct semid_ds *buf;
+ ushort *array;
+};
+#endif /* _sys_sem_h */
+
+static void print_semaphores();
+static void __nis_print_sems(int, int);
+
+
+void
+usage(char *name)
+{
+ fprintf(stderr, "usage: %s [-v]\n", name);
+ exit(1);
+}
+
+
+/* dump the context cache on the system */
+void
+main(int argc, char *argv[])
+{
+ extern int __nis_debuglevel;
+ int c;
+ nis_error status;
+ int new__nis_debug_level = 0;
+ char *cptr; /* passed to __nis_CacheInit(); we don't use it */
+
+ __nis_debuglevel = 0;
+
+ /* get command line options */
+ while ((c = getopt(argc, argv, "vsd")) != EOF) {
+ switch (c) {
+ case 'v': /* verbose mode */
+ new__nis_debug_level = 3;
+ break;
+
+ case 's':
+ /*
+ * special format - prints information on one line.
+ * undocumented feature. see cache_entry.cc
+ */
+ new__nis_debug_level = 6;
+ break;
+
+ case 'd':
+ /*
+ * another undocumented feature.
+ * prints the semaphores
+ */
+ print_semaphores();
+ break;
+
+ case '?': /* error */
+ usage(argv[0]);
+ break;
+ }
+ }
+ if ((status = __nis_CacheInit(&cptr)) != NIS_SUCCESS) {
+ fprintf(stderr, "NIS+ initialization error: %s\n",
+ nis_sperrno(status));
+ exit((int)status);
+ }
+ if (new__nis_debug_level)
+ __nis_debuglevel = new__nis_debug_level;
+ else
+ __nis_debuglevel = 1;
+
+ __nis_CachePrint();
+
+ exit(0);
+}
+
+
+#define NIS_SEM_R_KEY 100302
+#define NIS_SEM_W_KEY 100303
+#define NIS_W_NSEMS 2
+#define NIS_R_NSEMS 1
+
+static void
+print_semaphores()
+{
+ int sem_writer, sem_reader;
+ int semflg = 0;
+
+ sem_reader = semget(NIS_SEM_R_KEY, NIS_R_NSEMS, semflg);
+ sem_writer = semget(NIS_SEM_W_KEY, NIS_W_NSEMS, semflg);
+
+ if (sem_reader == -1 || sem_writer == -1) {
+ printf("Could not open semaphores!\n");
+ return;
+ }
+ __nis_print_sems(sem_writer, sem_reader);
+}
+
+
+static void
+__nis_print_sems(int sem_writer, int sem_reader)
+{
+ int i;
+ ushort w_array[NIS_W_NSEMS];
+ ushort r_array[NIS_R_NSEMS];
+ union semun semarg;
+
+
+ /* cache_manager (writer) semaphores */
+ if (sem_writer != -1) {
+ semarg.array = w_array;
+ if (semctl(sem_writer, 0, GETALL, semarg) == -1) {
+ printf("nis_print_sems: semctl GETALL failed");
+ return;
+ }
+ for (i = 0; i < NIS_W_NSEMS; i++)
+ printf("sem_writer[%d] = %d\n", i, w_array[i]);
+ }
+ /* reader */
+ if (sem_reader != -1) {
+ semarg.array = r_array;
+ if (semctl(sem_reader, 0, GETALL, semarg) == -1) {
+ printf("nis_print_sems: semctl GETALL failed");
+ return;
+ }
+ for (i = 0; i < NIS_R_NSEMS; i++)
+ printf("sem_reader[%d] = %d\n", i, r_array[i]);
+ }
+}
diff --git a/usr/src/cmd/rpcsvc/nis/bin/nisstat.c b/usr/src/cmd/rpcsvc/nis/bin/nisstat.c
new file mode 100644
index 0000000000..87b352b69b
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/bin/nisstat.c
@@ -0,0 +1,258 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * nisstat.c
+ *
+ * Copyright (c) 1988-1992 Sun Microsystems Inc
+ * All Rights Reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * nisstat.c
+ *
+ * This program will print out the various statistics being maintained by
+ * the NIS+ server.
+ */
+
+#include <stdio.h>
+#include <rpcsvc/nis.h>
+#include <string.h>
+
+static struct statlist {
+ int stat_type;
+ char *stat_name;
+ char stat_data[80];
+} statlist[] = {
+ { TAG_ROOTSERVER, "root server", },
+ { TAG_NISCOMPAT, "NIS compat mode", },
+ { TAG_DNSFORWARDING, "DNS forwarding in NIS mode", },
+ { TAG_SECURITY_LEVEL, "security level", },
+ { TAG_DIRLIST, "serves directories", },
+ { TAG_OPSTATS, "Operation Statistics", },
+ { TAG_S_DCACHE, "directory cache", },
+ { TAG_S_GCACHE, "group cache", },
+ { TAG_S_STORAGE, "static storage", },
+ { TAG_HEAP, "dynamic storage", },
+ { TAG_UPTIME, "up since", },
+ { 0, NULL, }};
+
+extern int optind;
+extern char *optarg;
+
+static void
+usage(s)
+ char *s;
+{
+ fprintf(stderr, "usage: %s [-H host] [directory]\n", s);
+ exit(1);
+}
+
+/*
+ * Print all the names of the directories served by this server
+ * This list comes in a space separated buffer.
+ */
+static void
+print_list_dir(header, dirlist)
+ char *header;
+ char *dirlist;
+{
+ char *t, *tmp;
+
+ tmp = strdup(dirlist);
+ if (tmp == NULL) {
+ fprintf(stderr, "No memory!\n");
+ exit(1);
+ }
+
+ printf("Stat '%s':\n", header);
+ /* we will print the list in the reverse order */
+ while (t = strrchr(tmp, ' ')) {
+ *t = NULL;
+ printf("\t%s\n", t + sizeof (char));
+ }
+ printf("\t%s\n", tmp);
+}
+
+/*
+ * Print all statistical information on this server.
+ * This list comes in a "\n" separated buffer.
+ */
+static void
+print_stats(header, statlist)
+ char *header;
+ char *statlist;
+{
+ char *t, *tmp;
+ bool_t first = TRUE;
+
+ tmp = strdup(statlist);
+ if (tmp == NULL) {
+ fprintf(stderr, "No memory!\n");
+ exit(1);
+ }
+
+ printf("Stat '%s':\n", header);
+ while (t = strchr(tmp, '\n')) {
+ *t = NULL;
+ if (first) {
+ first = FALSE;
+ if ((tmp[0] == NULL) || (strcmp(tmp, "stats:") == 0)) {
+ /* dont print this */
+ tmp = t + sizeof (char);
+ continue;
+ }
+ }
+ printf("\t%s\n", tmp);
+ tmp = t + sizeof (char);
+ }
+ if (tmp && tmp[0])
+ printf("\t%s\n", tmp);
+}
+
+static
+int
+match_host(char *host, char *target)
+{
+ int len = strlen(host);
+
+ if (strncasecmp(host, target, len) == 0 &&
+ (target[len] == '.' || target[len] == '\0'))
+ return (1);
+
+ return (0);
+}
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ nis_server *servers;
+ nis_error status;
+ nis_tag tags[50], *res;
+ int j, i, maxtag, ns, c;
+ nis_name domain;
+ char dname[1024], *host = NULL;
+ nis_result *lres;
+ nis_object *obj;
+ bool_t print_once = FALSE;
+ bool_t nis_compat;
+
+ while ((c = getopt(argc, argv, "H:")) != -1) {
+ switch (c) {
+ case 'H' :
+ host = optarg;
+ break;
+ default :
+ usage(argv[0]);
+ break;
+ }
+ }
+ if (argc - optind)
+ domain = argv[optind];
+ else
+ domain = nis_local_directory();
+
+ /* expand the name if not fully qualified */
+ if (domain[strlen(domain) - 1] != '.')
+ lres = nis_lookup(domain, EXPAND_NAME);
+ else
+ lres = nis_lookup(domain, 0);
+
+ if (lres->status != NIS_SUCCESS) {
+ fprintf(stderr, "\"%s\": %s\n",
+ domain, nis_sperrno(lres->status));
+ exit(1);
+ }
+ obj = lres->objects.objects_val;
+ sprintf(dname, "%s.%s", obj->zo_name, obj->zo_domain);
+ if (__type_of(obj) != NIS_DIRECTORY_OBJ) {
+ fprintf(stderr, "\"%s\": Not a directory.\n", dname);
+ exit(1);
+ }
+ ns = obj->DI_data.do_servers.do_servers_len;
+ servers = obj->DI_data.do_servers.do_servers_val;
+
+ printf("Statistics for domain \"%s\" :\n\n", dname);
+ memset(tags, 0, sizeof (nis_tag) * 50);
+ for (i = 0; i < 50 && statlist[i].stat_type; i++) {
+ tags[i].tag_type = statlist[i].stat_type;
+ tags[i].tag_val = statlist[i].stat_data;
+ if (tags[i].tag_type == TAG_OPSTATS)
+ strcpy(tags[i].tag_val, "all");
+ }
+ maxtag = i;
+
+ for (i = 0; i < ns; i++) {
+ if (host && !match_host(host, servers[i].name))
+ continue;
+ if (print_once)
+ printf("\n");
+ else
+ print_once = TRUE;
+ status = nis_stats(&servers[i], tags, maxtag, &res);
+ if (status != NIS_SUCCESS) {
+ fprintf(stderr,
+ "nisstat: Error talking to host \"%s\": %s\n",
+ servers[i].name, nis_sperrno(status));
+ continue;
+ }
+ printf("Statistics from server : \"%s\"\n", servers[i].name);
+ nis_compat = FALSE;
+ for (j = 0; j < maxtag; j++) {
+ if (strcmp(res[j].tag_val, "<Unknown Statistic>") == 0)
+ /* Dont print this one */
+ continue;
+ switch (statlist[j].stat_type) {
+ case TAG_OPSTATS:
+ print_stats(statlist[j].stat_name,
+ res[j].tag_val);
+ break;
+ case TAG_DIRLIST:
+ print_list_dir(statlist[j].stat_name,
+ res[j].tag_val);
+ break;
+ case TAG_NISCOMPAT:
+ if (strcmp(res[j].tag_val, "OFF") != 0)
+ nis_compat = TRUE;
+ printf("Stat '%s' = '%s'\n",
+ statlist[j].stat_name, res[j].tag_val);
+ break;
+ case TAG_DNSFORWARDING:
+ /*
+ * Assume that the TAG_NISCOMPAT
+ * answer comes first
+ */
+ if (nis_compat == FALSE)
+ break; /* Dont print this answer */
+ /* else fall through */
+ default:
+ printf("Stat '%s' = '%s'\n",
+ statlist[j].stat_name, res[j].tag_val);
+ break;
+ }
+ }
+ }
+ nis_freeresult(lres);
+ exit(0);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/bin/nisupdkeys.c b/usr/src/cmd/rpcsvc/nis/bin/nisupdkeys.c
new file mode 100644
index 0000000000..73543d706b
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/bin/nisupdkeys.c
@@ -0,0 +1,548 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * nisupdkeys.c
+ *
+ * Copyright (c) 1988-1995 Sun Microsystems, Inc.
+ * All Rights Reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * nisupdkeys.c
+ *
+ * This function will read the list of servers from a directory object,
+ * update them with the proper public keys and write the directory object
+ * back.
+ *
+ */
+
+#include <stdio.h>
+#include <netdb.h>
+#include <netdir.h>
+#include <netconfig.h>
+#include <netinet/in.h>
+#include <rpc/key_prot.h>
+#include <rpcsvc/nis.h>
+#include <rpcsvc/nis_dhext.h>
+#include <string.h>
+#include <stdlib.h>
+
+extern int optind;
+extern char *optarg;
+
+extern char *inet_ntoa();
+extern int gethostname(char *name, int namelen);
+extern nis_server *__nis_host2nis_server_g();
+extern void __nis_netconfig2ep(struct netconfig *, endpoint *);
+
+static
+int
+match_host(char *host, char *target)
+{
+ int len = strlen(host);
+
+ if (strncasecmp(host, target, len) == 0 &&
+ (target[len] == '.' || target[len] == '\0'))
+ return (1);
+
+ return (0);
+}
+
+
+void
+usage(cmd)
+ char *cmd;
+{
+ fprintf(stderr, "usage: %s [-C | -a] [-H host] [directory]\n", cmd);
+ fprintf(stderr, "usage: %s -s [-C | -a] [-H host]\n", cmd);
+ exit(1);
+}
+
+int
+clearkeydata(h, ns, srvs)
+ char *h; /* host to act on */
+ int ns; /* number of servers */
+ nis_server *srvs; /* array of servers */
+{
+ int i, is_modified;
+
+ for (i = 0, is_modified = 0; i < ns; i++) {
+ if (h && !match_host(h, srvs[i].name))
+ continue;
+ if (srvs[i].key_type != NIS_PK_NONE) {
+ printf("\tClearing server %s's key\n", srvs[i].name);
+ srvs[i].key_type = NIS_PK_NONE;
+ srvs[i].pkey.n_bytes = 0;
+ srvs[i].pkey.n_len = 0;
+ is_modified++;
+ } else {
+ printf("\tNo keys exist for the server \"%s\"\n",
+ srvs[i].name);
+ }
+ }
+ return (is_modified);
+}
+
+void
+copyserv(nis_server *dst, nis_server *src, int *mod)
+{
+ dst->key_type = src->key_type;
+ dst->pkey.n_len = src->pkey.n_len;
+ dst->pkey.n_bytes = src->pkey.n_bytes;
+ *mod = *mod + 1;
+}
+
+
+int
+updkeydata(h, ns, srvs)
+ char *h; /* host to act on */
+ int ns; /* number of servers */
+ nis_server *srvs; /* array of servers */
+{
+ int i, is_modified;
+ char netname[MAXNETNAMELEN];
+ nis_server *newserver;
+ int errcode;
+
+ for (i = 0, is_modified = 0; i < ns; i++) {
+ if (h && !match_host(h, srvs[i].name))
+ continue;
+ if (! host2netname(netname, srvs[i].name, NULL)) {
+ fprintf(stderr, "\tERROR: No netname for \"%s\"\n",
+ srvs[i].name);
+ continue;
+ }
+#ifdef DEBUG
+ printf("\tFetching Public key for server %s ...\n",
+ srvs[i].name);
+ printf("\tnetname = '%s'\n", netname);
+#endif
+
+ /*
+ * Basically let __nis_host2nis_server do the magic of
+ * determining what kind of nis_server should be
+ * storred in the directory object. All we need to do
+ * is preserve endpoint (address) information and
+ * print out the appropriate message.
+ *
+ * The new publickey should come from the master so
+ * that a stale key is not returned from a replica.
+ * However there is currently no way of doing this so
+ * replicas should be sync'd with the master.
+ */
+
+ if (!(newserver =
+ __nis_host2nis_server_g(h, TRUE, FALSE,
+ &errcode))) {
+ nis_perror(errcode, "nisupdkeys");
+ fprintf(stderr, "No changes were made.\n");
+ exit(1);
+ }
+
+ if (strcmp(srvs[i].name, newserver->name) != 0)
+ continue;
+
+ switch (srvs[i].key_type) {
+ case NIS_PK_NONE:
+ switch (newserver->key_type) {
+ case NIS_PK_NONE:
+ break;
+ case NIS_PK_DH:
+ printf(
+ "\tInstalling %s's public key in this object\n",
+ srvs[i].name);
+ copyserv(&srvs[i], newserver, &is_modified);
+ break;
+ case NIS_PK_DHEXT:
+ printf(
+ "\tInstalling %s's public key(s) in this object\n",
+ srvs[i].name);
+ copyserv(&srvs[i], newserver, &is_modified);
+ break;
+ default:
+ fprintf(stderr,
+ "\tERROR! Unknown server type.\n");
+ continue;
+ }
+ break;
+ case NIS_PK_DH:
+ switch (newserver->key_type) {
+ case NIS_PK_NONE:
+ printf(
+ "\tRemoving %s's public key in this object\n",
+ srvs[i].name);
+ copyserv(&srvs[i], newserver, &is_modified);
+ break;
+ case NIS_PK_DH:
+ printf(
+ "\tUpdating %s's public key in this object\n",
+ srvs[i].name);
+ copyserv(&srvs[i], newserver, &is_modified);
+ break;
+ case NIS_PK_DHEXT:
+ printf(
+ "\tUpdating %s's public key(s) in this object\n",
+ srvs[i].name);
+ copyserv(&srvs[i], newserver, &is_modified);
+ break;
+ default:
+ fprintf(stderr,
+ "\tERROR! Unknown server type.\n");
+ continue;
+ }
+ break;
+ case NIS_PK_DHEXT:
+ switch (newserver->key_type) {
+ case NIS_PK_NONE:
+ printf(
+ "\tRemoving %s's public key in this object\n",
+ srvs[i].name);
+ copyserv(&srvs[i], newserver, &is_modified);
+ break;
+ case NIS_PK_DH:
+ printf(
+ "\tInstalling %s's public key in this object\n",
+ srvs[i].name);
+ copyserv(&srvs[i], newserver, &is_modified);
+ break;
+ case NIS_PK_DHEXT:
+ printf(
+ "\tUpdating %s's public key(s) in this object\n",
+ srvs[i].name);
+ copyserv(&srvs[i], newserver, &is_modified);
+ break;
+ default:
+ fprintf(stderr,
+ "\tERROR! Unknown server type.\n");
+ continue;
+ }
+ default:
+ switch (newserver->key_type) {
+ case NIS_PK_NONE:
+ break;
+ case NIS_PK_DH:
+ printf(
+ "\tInstalling %s's public key in this object\n",
+ srvs[i].name);
+ copyserv(&srvs[i], newserver, &is_modified);
+ break;
+ case NIS_PK_DHEXT:
+ printf(
+ "\tInstalling %s's public key(s) in this object\n",
+ srvs[i].name);
+ copyserv(&srvs[i], newserver, &is_modified);
+ break;
+ default:
+ fprintf(stderr,
+ "\tERROR! Unknown server type.\n");
+ continue;
+ }
+ }
+
+ }
+ return (is_modified);
+}
+
+/*
+ * updaddrdata()
+ *
+ * For each server in the list, update its address information to be
+ * current. If h is non-null only update information for that host.
+ */
+int
+updaddrdata(h, ns, srvs)
+ char *h; /* host to act on */
+ int ns; /* number of servers */
+ nis_server *srvs; /* array of servers */
+{
+ register int i, j, k;
+ endpoint *eps; /* endpoints */
+ int nep; /* num of eps */
+
+ struct netconfig *nc; /* netconfig structure */
+ void *nch; /* netconfig structure handle */
+ struct nd_hostserv hs; /* netconfig database hostserv */
+ struct nd_addrlist *addrs; /* netconfig database addr list */
+
+ /* XXX only update TCP/IP addresses at present */
+ for (i = 0; i < ns; i++) {
+ if (h && !match_host(h, srvs[i].name))
+ continue;
+ eps = srvs[i].ep.ep_val;
+ nep = srvs[i].ep.ep_len;
+
+ for (j = 0; j < nep; j++) {
+ free(eps[j].uaddr);
+ free(eps[j].family);
+ free(eps[j].proto);
+ }
+
+ /* setup params for netdir_getbyname() */
+ hs.h_host = srvs[i].name;
+ hs.h_serv = "rpcbind";
+
+ /* count how many server entries we need */
+ j = 0, nch = setnetconfig();
+ while (nc = getnetconfig(nch)) {
+ if (strcmp(nc->nc_protofmly, NC_LOOPBACK) == 0)
+ continue;
+ if (!netdir_getbyname(nc, &hs, &addrs)) {
+ j += addrs->n_cnt;
+ netdir_free((char *)addrs, ND_ADDRLIST);
+ }
+ }
+ endnetconfig(nch);
+
+ if (j == 0) {
+ fprintf(stderr,
+ "nisupdkeys: Can't get address information for "
+ "host \"%s\"\n",
+ srvs[i].name);
+ exit(1);
+ }
+
+ /* got server count and allocate space */
+ srvs[i].ep.ep_len = nep = j;
+ if (!(srvs[i].ep.ep_val = eps =
+ (endpoint*)malloc(nep*sizeof (struct endpoint)))) {
+ return (0);
+ }
+
+ /* fill in new server address info */
+ j = 0, nch = setnetconfig();
+
+ /* keep going if we still have more interfaces */
+ while (nc = getnetconfig(nch)) {
+ if (strcmp(nc->nc_protofmly, NC_LOOPBACK) == 0)
+ continue;
+ if (!netdir_getbyname(nc, &hs, &addrs)) {
+ for (k = 0; k < addrs->n_cnt; k++) {
+ eps[j].uaddr =
+ taddr2uaddr(nc, &(addrs->n_addrs[k]));
+ __nis_netconfig2ep(nc, &(eps[j]));
+ /* if any of these returned NULL, bail */
+ if (!(eps[j].uaddr && eps[j].family &&
+ eps[j].proto)) {
+ netdir_free((char *)addrs, ND_ADDRLIST);
+ endnetconfig(nch);
+ return (0);
+ }
+ j++;
+ }
+ netdir_free((char *)addrs, ND_ADDRLIST);
+ }
+ }
+ endnetconfig(nch); /* free(3C)'s NC data structs */
+
+ if (j == 0) {
+ fprintf(stderr,
+ "nisupdkeys: Can't get address information for "
+ "host \"%s\"\n",
+ srvs[i].name);
+ exit(1);
+ }
+ }
+ return (1);
+}
+
+#define UPD_KEYS 0
+#define CLR_KEYS 1
+#define UPD_ADDR 2
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char dname[NIS_MAXNAMELEN];
+ char *server = NULL;
+ nis_server *srvlist;
+ char *dirlist[NIS_MAXREPLICAS], **curdir;
+ int ns, is_modified;
+ nis_object *obj;
+ nis_result *res, *mres;
+ int c;
+ int op = UPD_KEYS;
+ int i = 0;
+ bool_t hostupdate = FALSE;
+ char *hostname;
+
+ while ((c = getopt(argc, argv, "CsaH:")) != -1) {
+ switch (c) {
+ case 'C' :
+ op = CLR_KEYS;
+ break;
+ case 'a' :
+ op = UPD_ADDR;
+ break;
+ case 'H' :
+ server = optarg;
+ break;
+ case 's' :
+ hostupdate = TRUE;
+ break;
+ case '?' :
+ default :
+ fprintf(stderr, "Unrecognized option.\n");
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ if (server)
+ hostname = server;
+ else
+ hostname = nis_local_host();
+
+ if (hostupdate == TRUE) {
+ /*
+ * get the list of directories served by this server and
+ * update all those directory objects.
+ */
+ nis_server *nisserver;
+ nis_tag tags, *tagres;
+ nis_error status;
+ char *t, *dirname, tmpbuf[1024];
+
+ if (argc > optind) {
+ fprintf(stderr,
+ "No directories allowed with -s option\n");
+ usage(argv[0]);
+ }
+ if (!(nisserver =
+ __nis_host2nis_server_g(hostname, TRUE, FALSE, NULL)))
+ exit(1);
+
+ /* Get a list of directories served by this server */
+ tags.tag_type = TAG_DIRLIST;
+ tags.tag_val = "";
+ status = nis_stats(nisserver, &tags, 1, &tagres);
+ if (status != NIS_SUCCESS) {
+ fprintf(stderr,
+ "nisupdkeys: Error talking to host \"%s\", "
+ "error was %s\n", hostname, nis_sperrno(status));
+ exit(1);
+ }
+ if ((strcmp(tagres->tag_val, "<Unknown Statistic>") == 0) ||
+ (strcasecmp(tagres->tag_val, "<error>") == 0) ||
+ (strcmp(tagres->tag_val, " ") == 0)) {
+ fprintf(stderr,
+ "Attributes for the server \"%s\" cannot be "
+ "updated by \"nisupdkeys -s\"\n", hostname);
+ fprintf(stderr,
+ "Instead, use the following for all directories "
+ "served by \"%s\"\n",
+ hostname);
+ fprintf(stderr, "\t%s [-a|-C] -H \"%s\" dir_name "
+ "... \n", argv[0], hostname);
+ exit(1);
+ }
+
+ strcpy(tmpbuf, tagres->tag_val);
+ dirname = tmpbuf;
+ while (t = strchr(dirname, ' ')) {
+ *t++ = NULL;
+ dirlist[i++] = dirname;
+ dirname = t;
+ }
+ dirlist[i++] = dirname;
+ } else {
+ while ((argc - optind) > 0)
+ dirlist[i++] = argv[optind++];
+ if (i == 0) {
+ dirlist[0] = nis_local_directory();
+ i++;
+ }
+ }
+ dirlist[i] = NULL;
+
+ res = NULL;
+ for (curdir = dirlist; *curdir; curdir++) {
+ is_modified = 0;
+
+ /* if res != NULL its been used before */
+ if (res)
+ nis_freeresult(res);
+
+ printf("Updating directory object \"%s\" ...\n", *curdir);
+ res = nis_lookup(*curdir, MASTER_ONLY+EXPAND_NAME);
+ if (res->status != NIS_SUCCESS) {
+ fprintf(stderr,
+ "\tERROR: Unable to retrieve object.\n");
+ nis_perror(res->status, *curdir);
+ continue;
+ }
+ obj = res->objects.objects_val;
+ sprintf(dname, "%s.%s", obj->zo_name, obj->zo_domain);
+ if (__type_of(obj) != NIS_DIRECTORY_OBJ) {
+ fprintf(stderr, "\tERROR: \"%s\" is not a directory.\n",
+ dname);
+ continue;
+ }
+ ns = obj->DI_data.do_servers.do_servers_len;
+ srvlist = obj->DI_data.do_servers.do_servers_val;
+ /* if a specific host has been specified */
+ if (server) {
+ for (i = 0; i < ns; ++i) {
+ if (match_host(server, srvlist[i].name))
+ break;
+ }
+ if (i == ns) {
+ fprintf(stderr,
+ "\tERROR: Host \"%s\" does not serve "
+ "directory \"%s\"\n", server, *curdir);
+ fprintf(stderr,
+ "\tDirectory \"%s\" is not being "
+ "modified\n", *curdir);
+ continue;
+ }
+ }
+ switch (op) {
+ case CLR_KEYS :
+ is_modified = clearkeydata(server, ns, srvlist);
+ break;
+ case UPD_KEYS :
+ is_modified = updkeydata(server, ns, srvlist);
+ break;
+ case UPD_ADDR :
+ is_modified = updaddrdata(server, ns, srvlist);
+ break;
+ default:
+ /* should not have happened */
+ exit(1);
+ }
+ if (is_modified) {
+ mres = nis_modify(dname, obj);
+ if (mres->status != NIS_SUCCESS) {
+ fprintf(stderr,
+ "\tERROR: Unable to modify directory "
+ "object \"%s\"\n", *curdir);
+ nis_perror(mres->status, dname);
+ }
+ nis_freeresult(mres);
+ }
+ }
+ if (res)
+ nis_freeresult(res);
+ return (0);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/bin/req.flg b/usr/src/cmd/rpcsvc/nis/bin/req.flg
new file mode 100644
index 0000000000..b3b75227c4
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/bin/req.flg
@@ -0,0 +1,28 @@
+#!/bin/sh
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+
+
+echo_file usr/src/lib/libnsl/include/rpcsvc/nis_dhext.h
diff --git a/usr/src/cmd/rpcsvc/nis/cachemgr/Makefile b/usr/src/cmd/rpcsvc/nis/cachemgr/Makefile
new file mode 100644
index 0000000000..ee2a2f5f73
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/cachemgr/Makefile
@@ -0,0 +1,84 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Sample makefile to build the cache manager
+#
+SED= sed
+
+PROTOCOL_DIR=$(ROOT)/usr/include/rpcsvc
+
+HDRS=
+
+SRCS=cachemgr.c
+
+OBJS=cachemgr.o nis_cache_svc.o nis_cache_xdr.o
+
+PROG=nis_cachemgr
+
+include $(SRC)/cmd/Makefile.cmd
+
+LDLIBS += -lnsl
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTUSRSBINPROG)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+nis_cache.h: $(PROTOCOL_DIR)/nis_cache.x
+ $(RPCGEN) -h $(PROTOCOL_DIR)/nis_cache.x |\
+ $(SED) -e 's!\"$(PROTOCOL_DIR)/nis_cache.h\"!"nis_cache.h"!' > $@
+
+nis_cache_svc.c: $(PROTOCOL_DIR)/nis_cache.x nis_clnt.h
+ $(RPCGEN) -m $(PROTOCOL_DIR)/nis_cache.x |\
+ $(SED) -e 's!\"$(PROTOCOL_DIR)/nis_cache.h\"!"nis_cache.h"!' > $@
+
+nis_cache_xdr.c: $(PROTOCOL_DIR)/nis_cache.x nis_clnt.h
+ $(RPCGEN) -c $(PROTOCOL_DIR)/nis_cache.x |\
+ $(SED) -e 's!\"$(PROTOCOL_DIR)/nis_cache.h\"!"nis_cache.h"!' > $@
+
+nis_clnt.h: $(PROTOCOL_DIR)/nis.x
+ $(RPCGEN) -C -h $(PROTOCOL_DIR)/nis.x |\
+ $(SED) -n -e '/EDIT_START/,$$ p' |\
+ $(SED) -e 's/_3_svc/_svc/' |\
+ $(SED) -e 's/_3/_clnt/' > $@
+
+nis_cachemgr: $(OBJS) nis_clnt.h
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+%.o: %.c nis_cache.h nis_clnt.h
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
+
+include $(SRC)/cmd/Makefile.targ
diff --git a/usr/src/cmd/rpcsvc/nis/cachemgr/cachemgr.c b/usr/src/cmd/rpcsvc/nis/cachemgr/cachemgr.c
new file mode 100644
index 0000000000..efc5804303
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/cachemgr/cachemgr.c
@@ -0,0 +1,533 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <signal.h>
+#include <netdir.h>
+#include <errno.h>
+#include <rpcsvc/daemon_utils.h>
+#include <rpcsvc/nis.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <priv.h>
+#include <priv_utils.h>
+#include "nis_cache.h"
+
+#define NIS_CACHEMGR_UID 0
+#define NIS_CACHEMGR_GID 3
+
+
+int mgr_verbose;
+int alarm_rang = 0;
+int got_sighup = 0;
+
+extern int __nis_debuglevel;
+extern void cacheprog_2(struct svc_req *, SVCXPRT *);
+extern nis_error __nis_CacheMgrInit_discard(int);
+
+static char *create_ti_server();
+static void my_svc_run();
+static void do_timers();
+static void do_config();
+static void set_file_ownership(char *);
+
+void
+hangup_handler(int sig)
+{
+ got_sighup = 1;
+}
+
+void
+cache_purge_handler(int sig)
+{
+ alarm_rang = 1;
+}
+
+/*
+ * Cleanup, set signal handler to default, and send ourselves
+ * the signal again.
+ */
+void
+cache_cleanup_handler(int sig)
+{
+ __nis_CacheMgrCleanup();
+ sigset(sig, SIG_DFL);
+ kill(getpid(), sig);
+}
+
+static void usage(name)
+ char *name;
+{
+ printf("\tusage: %s\n", name);
+ printf("\t\t-d [debug_level]\n");
+ printf("\t\t-i <initialize cache when starting up>\n");
+ printf("\t\t-m [max size of cache file (in pages) ]\n");
+ printf(
+ "\t\t-n <insecure mode - directory object signatures not checked>.\n");
+ printf("\t\t-s [initial size of cache file (in pages)]\n");
+ printf("\t\t-v <verbose - sends events to syslog>\n");
+ exit(1);
+}
+
+main(int argc, char **argv)
+{
+ int c;
+ int n;
+ int pid;
+ int console;
+ char *p;
+ int init_cache = 0;
+ char *options = 0;
+ char *uaddr;
+ nis_error err;
+
+ /*
+ * Ensure all files are owned by nis_cachemgr.
+ */
+ set_file_ownership(NIS_DIRECTORY);
+ set_file_ownership(CACHE_FILE);
+ set_file_ownership(PRIVATE_CACHE_FILE);
+ set_file_ownership(TMP_CACHE_FILE);
+ set_file_ownership(COLD_START_FILE);
+ set_file_ownership(DOT_FILE);
+
+ console = open("/dev/console", 2);
+ if (console == -1) {
+ (void) fprintf(stderr, "can't open /dev/console. %s\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ /*
+ * Make the process a privilege aware daemon.
+ * Only "basic" privileges are required.
+ */
+ if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET,
+ NIS_CACHEMGR_UID, NIS_CACHEMGR_GID, (char *)NULL) == -1) {
+ (void) fprintf(stderr, "should be run with"
+ " sufficient privileges\n");
+ exit(1);
+ }
+
+ /*
+ * Make sure any files we create without explicit permissions
+ * aren't group or world writeable.
+ */
+ (void) umask(0022);
+
+ openlog("nis_cachemgr", LOG_CONS, LOG_DAEMON);
+
+ while ((c = getopt(argc, argv, "ivd:a:ns:m:o:")) != EOF) {
+ switch (c) {
+ case 'd':
+ __nis_debuglevel = atoi(optarg);
+ break;
+
+ case 'i':
+ init_cache = 1;
+ break;
+
+ case 'm':
+ case 'n':
+ case 's':
+ /* obsolete */
+ break;
+
+ case 'v':
+ mgr_verbose = 1;
+ break;
+
+ case 'o':
+ options = optarg;
+ break;
+
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+
+ if (__nis_debuglevel && (setpflags(PRIV_DEBUG, 1) == -1)) {
+ (void) fprintf(stderr,
+ "can't turn on privilege debug flag. %s\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ /*
+ * We pass options to the cache library through the
+ * environment variable NIS_OPTIONS. If options are
+ * passed on the command line, then we put them into
+ * the environment.
+ */
+ if (options) {
+ n = strlen("NIS_OPTIONS=") + strlen(options) + 1;
+ p = (char *)malloc(n);
+ if (p == NULL) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ strcpy(p, "NIS_OPTIONS=");
+ strcat(p, options);
+ putenv(p);
+ }
+
+ if (init_cache) {
+ unlink(PRIVATE_CACHE_FILE);
+ unlink(TMP_CACHE_FILE);
+ }
+
+ err = __nis_CacheMgrInit_discard(init_cache);
+ if (err != NIS_SUCCESS) {
+ nis_perror(err, "can't initialize cache");
+ exit(1);
+ }
+
+ if (!__nis_debuglevel) {
+ pid = fork();
+ if (pid == -1) {
+ perror("fork");
+ exit(1);
+ } else if (pid != 0) {
+ exit(0);
+ }
+ close(0);
+ close(1);
+ close(2);
+ dup2(console, 1);
+ dup2(console, 2);
+ setsid();
+ }
+
+
+ /*
+ * Remove unnecessary privileges - fork and exec.
+ * Removing permitted privileges also removes effective privileges.
+ */
+ if (priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_PROC_FORK,
+ PRIV_PROC_EXEC, (char *)NULL) == -1) {
+ syslog(LOG_ERR,
+ "can't set reduced privileges. %s\n",
+ strerror(errno));
+ }
+
+ sigset(SIGHUP, hangup_handler);
+ sigset(SIGALRM, cache_purge_handler);
+ sigset(SIGPIPE, SIG_IGN);
+ sigset(SIGTERM, cache_cleanup_handler);
+ sigset(SIGINT, cache_cleanup_handler);
+ sigset(SIGKILL, cache_cleanup_handler);
+ sigset(SIGIOT, cache_cleanup_handler);
+ sigset(SIGILL, cache_cleanup_handler);
+ sigset(SIGQUIT, cache_cleanup_handler);
+ sigset(SIGSYS, cache_cleanup_handler);
+ sigset(SIGFPE, cache_cleanup_handler);
+
+ uaddr = create_ti_server();
+ __nis_CacheMgrUpdateUaddr(uaddr);
+ free(uaddr);
+ __nis_CacheMgrMarkUp();
+
+ if (mgr_verbose)
+ syslog(LOG_INFO, "running service, uaddr = %s", uaddr);
+ do_timers();
+ my_svc_run();
+ syslog(LOG_ERR, "svc_run() returned");
+ exit(1);
+}
+
+static void set_file_ownership(char *file)
+{
+ struct stat info;
+ if (stat(file, &info) == -1) {
+ if (errno == ENOENT)
+ return;
+ (void) fprintf(stderr, "can't stat file %s. %s\n",
+ file, strerror(errno));
+ exit(1);
+ }
+ if (info.st_uid != NIS_CACHEMGR_UID ||
+ info.st_gid != NIS_CACHEMGR_GID) {
+ if (chown(file, NIS_CACHEMGR_UID, NIS_CACHEMGR_GID) == -1) {
+ (void) fprintf(stderr, "can't chown %s to daemon. %s\n",
+ file, strerror(errno));
+ exit(1);
+ }
+ }
+
+}
+static
+void
+my_svc_run()
+{
+ int i;
+ int npollfds = 0;
+ pollfd_t *pollset = NULL;
+
+ while (1) {
+ if (got_sighup) {
+ got_sighup = 0;
+ do_config();
+ } else if (alarm_rang) {
+ alarm_rang = 0;
+ do_timers();
+ }
+
+ if (npollfds != svc_max_pollfd) {
+ pollset = realloc(pollset,
+ sizeof (pollfd_t) * svc_max_pollfd);
+ npollfds = svc_max_pollfd;
+ }
+
+ if (npollfds == 0)
+ break; /* None waiting, hence return */
+
+ /*
+ * Get existing array of pollfd's, should really compress
+ * this but it shouldn't get very large (or sparse).
+ */
+ (void) memcpy(pollset, svc_pollfd,
+ sizeof (pollfd_t) * svc_max_pollfd);
+
+ switch (i = poll(pollset, npollfds, -1)) {
+ case -1:
+ /*
+ * We ignore all errors, continuing with the assumption
+ * that it was set by the signal handlers (or any
+ * other outside event) and not caused by poll().
+ */
+ case 0:
+ continue;
+ default:
+ svc_getreq_poll(pollset, i);
+ }
+ }
+}
+
+
+static
+void
+do_config()
+{
+ ulong_t secs;
+
+ secs = __nis_CacheMgrRefreshCache();
+ alarm(secs);
+}
+
+
+
+static
+void
+do_timers()
+{
+ ulong_t secs;
+
+ secs = __nis_CacheMgrTimers();
+ alarm(secs);
+}
+
+static
+char *
+create_ti_server()
+{
+ SVCXPRT *transp;
+ struct netconfig *nconf;
+ char *uaddr;
+ void *nc_handle;
+ bool_t found_loopback = FALSE;
+
+ /*
+ * we want to get a loopback transport to have clients talk to the
+ * cachemgr
+ * this provides security in the sense that only local folk can access
+ * the cachemgr. Also, it makes the messages inherently more reliable.
+ * we also want a CLTS transport so search in the netconfig database
+ * for this type of transport.
+ * This is an implicit protocol between the cachemgr and the clients
+ * and the clients make the same selection of trasport (in
+ * cache_getclnt.c) and use the uaddr that the cachemgr writes into
+ * the cache file. This uaddr would be valid only for this transport
+ * that both agree on implicitly.
+ * If the selection scheme is changed here it should also be changed
+ * in the client side (cache_getclnt.c)
+ */
+
+ nc_handle = setnetconfig();
+ if (nc_handle != (void *) NULL) {
+ while (nconf = getnetconfig(nc_handle))
+ if ((strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) &&
+ (nconf->nc_semantics == NC_TPI_CLTS)) {
+ found_loopback = TRUE;
+ break;
+ }
+ }
+ if (!found_loopback) {
+ syslog(LOG_ERR,
+ "Could not get loopback transport from netconfig database");
+ exit(1);
+
+ }
+ /* create a new transport endpoint */
+ transp = svc_tp_create(
+ (void (*)(struct svc_req *, SVCXPRT *))cacheprog_2,
+ CACHEPROG, CACHE_VER_2, nconf);
+ if (!transp) {
+ syslog(LOG_ERR,
+ "create_ti_server: cannot create server handle on loopback transport");
+ exit(1);
+ }
+ uaddr = taddr2uaddr(nconf, &transp->xp_ltaddr);
+ endnetconfig(nc_handle);
+ return (uaddr);
+}
+
+/*
+ * Service routines.
+ */
+
+void *
+nis_cache_add_entry_2()
+{
+ if (mgr_verbose)
+ syslog(LOG_INFO, "nis_cache_add_entry");
+}
+
+void *
+nis_cache_remove_entry_2()
+{
+ if (mgr_verbose)
+ syslog(LOG_INFO, "nis_cache_remove_entry");
+}
+
+void *
+nis_cache_read_coldstart_2()
+{
+ if (mgr_verbose)
+ syslog(LOG_INFO, "nis_cache_read_coldstart");
+}
+
+void *
+nis_cache_refresh_entry_2()
+{
+ if (mgr_verbose)
+ syslog(LOG_INFO, "nis_cache_refresh_entry");
+}
+
+nis_error *
+nis_cache_bind_replica_2(char **argp, struct svc_req *rqstp)
+{
+ static nis_error err;
+
+ if (mgr_verbose)
+ syslog(LOG_INFO, "nis_cache_bind_replica_2(%s)", *argp);
+ err = __nis_CacheMgrBindReplica(*argp);
+ return (&err);
+}
+
+nis_error *
+nis_cache_bind_master_2(char **argp, struct svc_req *rqstp)
+{
+ static nis_error err;
+
+ if (mgr_verbose)
+ syslog(LOG_INFO, "nis_cache_bind_master_2(%s)", *argp);
+ err = __nis_CacheMgrBindMaster(*argp);
+ return (&err);
+}
+
+nis_error *
+nis_cache_bind_server_2(bind_server_arg *argp, struct svc_req *rqstp)
+{
+ static nis_error err;
+
+ if (mgr_verbose)
+ syslog(LOG_INFO,
+ "nis_cache_bind_server_2(%s)", argp->srv->name);
+ err = __nis_CacheMgrBindServer(argp->srv, argp->nsrv);
+ return (&err);
+}
+
+refresh_res *
+nis_cache_refresh_binding_2(nis_bound_directory *binding, struct svc_req *rqstp)
+{
+ static refresh_res res;
+
+ if (mgr_verbose)
+ syslog(LOG_INFO, "nis_cache_refresh_binding_2");
+ res.changed = __nis_CacheMgrRefreshBinding(binding);
+ res.ep.family = NULL;
+ res.ep.proto = NULL;
+ res.ep.uaddr = NULL;
+ return (&res);
+}
+
+refresh_res *
+nis_cache_refresh_address_2(nis_bound_endpoint *bep, struct svc_req *rqstp)
+{
+ static refresh_res res;
+
+ if (mgr_verbose)
+ syslog(LOG_INFO, "nis_cache_refresh_address_2");
+ /* xdr_free old result */
+ xdr_free(xdr_refresh_res, (char *)&res);
+ res.changed = __nis_CacheMgrRefreshAddress(bep);
+ res.ep.family = NULL; /* not used */
+ res.ep.proto = NULL; /* not used */
+ if (res.changed)
+ res.ep.uaddr = strdup(bep->uaddr);
+ else
+ res.ep.uaddr = NULL;
+ return (&res);
+}
+
+refresh_res *
+nis_cache_refresh_callback_2(nis_bound_endpoint *bep, struct svc_req *rqstp)
+{
+ static refresh_res res;
+
+ if (mgr_verbose)
+ syslog(LOG_INFO, "nis_cache_refresh_callback_2");
+ /* xdr_free old result */
+ xdr_free(xdr_refresh_res, (char *)&res);
+ res.changed = __nis_CacheMgrRefreshCallback(bep);
+ if (res.changed) {
+ res.ep.family = strdup(bep->cbep.family);
+ res.ep.proto = strdup(bep->cbep.proto);
+ res.ep.uaddr = strdup(bep->cbep.uaddr);
+ } else {
+ res.ep.family = NULL;
+ res.ep.proto = NULL;
+ res.ep.uaddr = NULL;
+ }
+ return (&res);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/cachemgr/cachemgr.h b/usr/src/cmd/rpcsvc/nis/cachemgr/cachemgr.h
new file mode 100644
index 0000000000..fd58917076
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/cachemgr/cachemgr.h
@@ -0,0 +1,38 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 1996, by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+
+#ifndef __CACHEMGR_H
+#define __CACHEMGR_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+extern int xdr_fd_result(XDR *, fd_result *);
+extern int xdr_directory_obj(XDR *, directory_obj *);
+extern int xdr_nis_error(XDR *, int *);
+extern int xdr_nis_server(XDR *, nis_server *);
+
+#endif /* __CACHEMGR_H */
diff --git a/usr/src/cmd/rpcsvc/nis/cachemgr/req.flg b/usr/src/cmd/rpcsvc/nis/cachemgr/req.flg
new file mode 100644
index 0000000000..b3b75227c4
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/cachemgr/req.flg
@@ -0,0 +1,28 @@
+#!/bin/sh
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+
+
+echo_file usr/src/lib/libnsl/include/rpcsvc/nis_dhext.h
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/Makefile b/usr/src/cmd/rpcsvc/nis/rpc.nisd/Makefile
new file mode 100644
index 0000000000..ff21cc810f
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/Makefile
@@ -0,0 +1,133 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Makefile for the nis+ daemon
+#
+
+INIT_OBJS= nisinit.o
+
+LOG_OBJS= nislog.o nis_log_common.o nis_mt.o nis_cleanup.o
+
+NISLDAPMAPTEST_OBJS= nisldapmaptest.o
+
+SRV_OBJS= \
+ nis_main.o nis_ns_proc.o nis_ib_proc.o nis_xx_proc.o \
+ nis_db.o nis_subr_proc.o nis_log_common.o yp_ns_proc.o \
+ nis_log_svc.o nis_service.o resolv_common.o resolv.o \
+ ypserv1.o nis_multival.o nis_opacc.o nis_mt.o \
+ nis_thread_funcs.o nis_cleanup.o ldap_log.o
+
+SRCS= \
+ nis_main.c nis_ns_proc.c nis_ib_proc.c nis_xx_proc.c nis_db.c \
+ nis_subr_proc.c nis_log_common.c yp_ns_proc.c nisinit.c \
+ nis_log_svc.c nis_service.c resolv_common.c resolv.c ypserv1.c \
+ nis_multival.c nis_opacc.c nis_mt.c nis_thread_funcs.c \
+ nisldapmaptest.c
+
+OBJS= $(SRV_OBJS) $(INIT_OBJS)
+PROG= nisinit rpc.nisd nislog nisldapmaptest
+
+# XX64 rpc.nisd nislog nisldapmaptest all require C++
+__GNUC:sh= echo \\043
+$(__GNUC)PROG= nisinit
+
+DERIVED_FILES= nis_svc.h
+CLEANFILES += $(DERIVED_FILES)
+
+include $(SRC)/cmd/Makefile.cmd
+
+PROTOCOL_DIR= $(ROOT)/usr/include/rpcsvc
+CPPFLAGS += -I$(SRC)/lib/libnsl/include -I$(SRC)/lib/libnisdb
+
+nislog := LDLIBS += -lnisdb
+nislog := CCFLAGS += -DNEED_DIROBJ
+rpc.nisd := CCFLAGS += $(CCMT) -DNEED_DIROBJ
+rpc.nisd := LDLIBS += -lnisdb -lldap
+nisldapmaptest := LDLIBS += -lnisdb -lldap
+LDLIBS += -lnsl -lc
+
+SUBDIR= resolv_server configs
+
+.KEEP_STATE:
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+lint := TARGET= lint
+
+all: $(DERIVED_FILES) $(PROG) $(SUBDIR)
+
+install: all $(ROOTUSRSBINPROG) $(SUBDIR)
+
+clean:
+ $(RM) $(LOG_OBJS) $(OBJS) $(CLEANFILES)
+
+lint: lint_SRCS
+
+#
+# Explicit link rules for the actual programs to get around the C++
+# situation.
+#
+nislog: $(LOG_OBJS)
+ $(LINK.cc) -o $@ $(LOG_OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+nisinit: $(INIT_OBJS)
+ $(LINK.c) -o $@ $(INIT_OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+rpc.nisd: $(SRV_OBJS)
+ $(LINK.cc) -o $@ $(SRV_OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+nisldapmaptest: $(NISLDAPMAPTEST_OBJS)
+ $(LINK.cc) -o $@ $(NISLDAPMAPTEST_OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+nplt: nisldapmaptest
+
+$(SUBDIR): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+SRC_DER=nis_main.o nis_service.o nis_subr_proc.o nis_xx_proc.o nisinit.o
+$(SRC_DER): nis_svc.h
+
+FRC:
+
+#
+# Rules for building the derived files :
+#
+nis_svc.h: $(PROTOCOL_DIR)/nis.x $(PROTOCOL_DIR)/nis_object.x
+ $(RPCGEN) -C -h $(PROTOCOL_DIR)/nis.x |\
+ $(SED) -n -e '/EDIT_START/,$$ p' |\
+ $(SED) -e 's/_2_svc/_svc/' |\
+ $(SED) -e 's/_3_svc/_svc/' |\
+ $(SED) -e 's/_3/_clnt/' |\
+ $(SED) -e 's/_2/_clnt/' >nis_svc.h
+
+include $(SRC)/cmd/Makefile.targ
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/configs/Makefile b/usr/src/cmd/rpcsvc/nis/rpc.nisd/configs/Makefile
new file mode 100644
index 0000000000..9f49a14ed9
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/configs/Makefile
@@ -0,0 +1,61 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/rpcsvc/nis/rpc.nisd/configs/Makefile
+
+ETCDEFAULTFILES= rpc.nisd
+VARNISFILES= NIS+LDAPmapping.template
+
+include $(SRC)/cmd/Makefile.cmd
+
+ROOTVARNIS= $(ROOT)/var/nis
+ROOTVARNISFILES= $(VARNISFILES:%=$(ROOTVARNIS)/%)
+ROOTETCDEFAULTFILES= $(ETCDEFAULTFILES:%=$(ROOTETC)/default/%)
+
+FILEMODE= 644
+OWNER= root
+GROUP= sys
+
+all: FRC
+
+install: all $(ROOTDIRS) $(ROOTETCDEFAULTFILES) $(ROOTVARNISFILES)
+
+$(ROOTDIRS):
+ $(INS.dir)
+
+clean: FRC
+
+clobber: FRC
+
+lint: FRC
+
+$(ROOTVARNIS)/NIS+LDAPmapping.template: NIS+LDAPmapping.template
+ $(INS.file) NIS+LDAPmapping.template
+
+$(ROOTETC)/default/rpc.nisd: rpc.nisd
+ $(INS.file) rpc.nisd
+
+FRC:
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/configs/NIS+LDAPmapping.template b/usr/src/cmd/rpcsvc/nis/rpc.nisd/configs/NIS+LDAPmapping.template
new file mode 100644
index 0000000000..6613ce9300
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/configs/NIS+LDAPmapping.template
@@ -0,0 +1,996 @@
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+# Configuration file for NIS+ to LDAP mapping, used by the rpc.nisd.
+# See NIS+LDAPmapping(4) for more information; see rpc.nisd(4) for
+# additional attributes controlling general rpc.nisd operation,
+# LDAP server(s), LDAP authentication method, etc.
+
+
+#################################################################
+# nisplusLDAPdatabaseIdMapping
+#
+# The nisplusLDAPdatabaseIdMapping attribute values establish a
+# connection between a NIS+ object and a database id label used
+# for identification in other mapping attributes.
+#################################################################
+
+# Standard NIS+ directories
+nisplusLDAPdatabaseIdMapping basedir:
+nisplusLDAPdatabaseIdMapping orgdir:org_dir
+nisplusLDAPdatabaseIdMapping groupsdir:groups_dir
+
+# Standard NIS+ groups.
+nisplusLDAPdatabaseIdMapping admin:admin.groups_dir
+
+# Add other NIS+ directory, group, or link objects here, if any.
+
+# Standard NIS+ table objects (for the table objects themselves;
+# the table entries are handled in the next section).
+nisplusLDAPdatabaseIdMapping passwd_table:passwd.org_dir
+nisplusLDAPdatabaseIdMapping group_table:group.org_dir
+nisplusLDAPdatabaseIdMapping auto_master_table:auto_master.org_dir
+nisplusLDAPdatabaseIdMapping auto_home_table:auto_home.org_dir
+nisplusLDAPdatabaseIdMapping bootparams_table:bootparams.org_dir
+nisplusLDAPdatabaseIdMapping ethers_table:ethers.org_dir
+nisplusLDAPdatabaseIdMapping hosts_table:hosts.org_dir
+nisplusLDAPdatabaseIdMapping ipnodes_table:ipnodes.org_dir
+nisplusLDAPdatabaseIdMapping cred_table:cred.org_dir
+nisplusLDAPdatabaseIdMapping aliases_table:mail_aliases.org_dir
+nisplusLDAPdatabaseIdMapping netgroup_table:netgroup.org_dir
+nisplusLDAPdatabaseIdMapping networks_table:networks.org_dir
+nisplusLDAPdatabaseIdMapping netmasks_table:netmasks.org_dir
+nisplusLDAPdatabaseIdMapping protocols_table:protocols.org_dir
+nisplusLDAPdatabaseIdMapping rpc_table:rpc.org_dir
+nisplusLDAPdatabaseIdMapping services_table:services.org_dir
+nisplusLDAPdatabaseIdMapping auth_attr_table:auth_attr.org_dir
+nisplusLDAPdatabaseIdMapping exec_attr_table:exec_attr.org_dir
+nisplusLDAPdatabaseIdMapping prof_attr_table:prof_attr.org_dir
+nisplusLDAPdatabaseIdMapping user_attr_table:user_attr.org_dir
+nisplusLDAPdatabaseIdMapping audit_user_table:audit_user.org_dir
+
+# Add other NIS+ table objects here, if any
+
+# Standard NIS+ table entries
+nisplusLDAPdatabaseIdMapping passwd:passwd.org_dir
+nisplusLDAPdatabaseIdMapping group:group.org_dir
+nisplusLDAPdatabaseIdMapping auto_master:auto_master.org_dir
+nisplusLDAPdatabaseIdMapping auto_home:auto_home.org_dir
+nisplusLDAPdatabaseIdMapping bootparams:bootparams.org_dir
+nisplusLDAPdatabaseIdMapping ethers:ethers.org_dir
+nisplusLDAPdatabaseIdMapping hosts:[addr="[0-9]*.[0-9]*.[0-9]*.[0-9]*"]\
+ hosts.org_dir
+nisplusLDAPdatabaseIdMapping ipnodes:[addr="*:*"]ipnodes.org_dir
+# The 'cred' table entries map to different containers, depending on the data.
+nisplusLDAPdatabaseIdMapping credlocal:[auth_type=LOCAL]cred.org_dir
+nisplusLDAPdatabaseIdMapping creduser:[auth_type="D*", \
+ auth_name="unix.[0-9]*"]cred.org_dir
+nisplusLDAPdatabaseIdMapping crednode:[auth_type="D*", \
+ auth_name="unix.[a-z]*"]cred.org_dir
+nisplusLDAPdatabaseIdMapping aliases:mail_aliases.org_dir
+nisplusLDAPdatabaseIdMapping netgroup:netgroup.org_dir
+nisplusLDAPdatabaseIdMapping networks:networks.org_dir
+nisplusLDAPdatabaseIdMapping netmasks:netmasks.org_dir
+nisplusLDAPdatabaseIdMapping protocols:protocols.org_dir
+nisplusLDAPdatabaseIdMapping rpc:rpc.org_dir
+nisplusLDAPdatabaseIdMapping services:services.org_dir
+nisplusLDAPdatabaseIdMapping auth_attr:auth_attr.org_dir
+nisplusLDAPdatabaseIdMapping exec_attr:exec_attr.org_dir
+nisplusLDAPdatabaseIdMapping prof_attr:prof_attr.org_dir
+nisplusLDAPdatabaseIdMapping user_attr:user_attr.org_dir
+nisplusLDAPdatabaseIdMapping audit_user:audit_user.org_dir
+
+# Add other NIS+ table entry mappings here, if any
+
+
+#################################################################
+# nisplusLDAPentryTtl
+#
+# Set TTLs for mapped objects, using the database id labels from
+# the nisplusLDAPdatabaseIdMapping attribute values above to
+# indicate the object to which the TTLs pertain.
+#################################################################
+
+# Standard NIS+ directories
+nisplusLDAPentryTtl basedir:21600:43200:43200
+nisplusLDAPentryTtl orgdir:21600:43200:43200
+nisplusLDAPentryTtl groupsdir:21600:43200:43200
+nisplusLDAPentryTtl admin:21600:43200:43200
+
+# Standard NIS+ tables
+nisplusLDAPentryTtl passwd_table:21600:43200:43200
+nisplusLDAPentryTtl group_table:21600:43200:43200
+nisplusLDAPentryTtl auto_master_table:21600:43200:43200
+nisplusLDAPentryTtl auto_home_table:21600:43200:43200
+nisplusLDAPentryTtl bootparams_table:21600:43200:43200
+nisplusLDAPentryTtl ethers_table:21600:43200:43200
+nisplusLDAPentryTtl hosts_table:21600:43200:43200
+nisplusLDAPentryTtl ipnodes_table:21600:43200:43200
+nisplusLDAPentryTtl cred_table:21600:43200:43200
+nisplusLDAPentryTtl aliases_table:21600:43200:43200
+nisplusLDAPentryTtl netgroup_table:21600:43200:43200
+nisplusLDAPentryTtl networks_table:21600:43200:43200
+nisplusLDAPentryTtl netmasks_table:21600:43200:43200
+nisplusLDAPentryTtl protocols_table:21600:43200:43200
+nisplusLDAPentryTtl rpc_table:21600:43200:43200
+nisplusLDAPentryTtl services_table:21600:43200:43200
+nisplusLDAPentryTtl auth_attr_table:21600:43200:43200
+nisplusLDAPentryTtl exec_attr_table:21600:43200:43200
+nisplusLDAPentryTtl prof_attr_table:21600:43200:43200
+nisplusLDAPentryTtl user_attr_table:21600:43200:43200
+nisplusLDAPentryTtl audit_user_table:21600:43200:43200
+
+# Standard NIS+ table entries
+#nisplusLDAPentryTtl passwd:1800:3600:3600
+#nisplusLDAPentryTtl group:1800:3600:3600
+#nisplusLDAPentryTtl auto_master:1800:3600:3600
+#nisplusLDAPentryTtl auto_home:1800:3600:3600
+#nisplusLDAPentryTtl bootparams:1800:3600:3600
+#nisplusLDAPentryTtl ethers:1800:3600:3600
+#nisplusLDAPentryTtl hosts:1800:3600:3600
+#nisplusLDAPentryTtl ipnodes:1800:3600:3600
+#nisplusLDAPentryTtl credlocal:1800:3600:3600
+#nisplusLDAPentryTtl creduser:1800:3600:3600
+#nisplusLDAPentryTtl crednode:1800:3600:3600
+#nisplusLDAPentryTtl aliases:1800:3600:3600
+#nisplusLDAPentryTtl netgroup:1800:3600:3600
+#nisplusLDAPentryTtl networks:1800:3600:3600
+#nisplusLDAPentryTtl netmasks:1800:3600:3600
+#nisplusLDAPentryTtl protocols:1800:3600:3600
+#nisplusLDAPentryTtl rpc:1800:3600:3600
+#nisplusLDAPentryTtl services:1800:3600:3600
+#nisplusLDAPentryTtl auth_attr:1800:3600:3600
+#nisplusLDAPentryTtl exec_attr:1800:3600:3600
+#nisplusLDAPentryTtl prof_attr:1800:3600:3600
+#nisplusLDAPentryTtl user_attr:1800:3600:3600
+#nisplusLDAPentryTtl audit_user:1800:3600:3600
+
+
+#################################################################
+# nisplusLDAPobjectDN
+#
+# The nisplusLDAPobjectDN attribute values establish the place in
+# the LDAP directory information tree where the data for mapped
+# NIS+ objects reside. The NIS+ objects are identified by the
+# database id labels from the nisplusLDAPdatabaseIdMapping values
+# above.
+#
+# Before using these attribute values, make sure that the LDAP
+# server(s) recognize the RFC 2307 attributes and object classes.
+# Also, the nisplusObject attribute and nisplusObjectContainer
+# object class must be known to the LDAP server(s) in order to
+# store NIS+ objects other than NIS+ table entries in LDAP.
+# See the Getting Started section on NIS+LDAPmapping(4) for
+# more information.
+#################################################################
+
+# Standard NIS+ directories
+nisplusLDAPobjectDN basedir:cn=basedir,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer:\
+ cn=basedir,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer,\
+ objectClass=top
+nisplusLDAPobjectDN orgdir:cn=orgdir,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer:\
+ cn=orgdir,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer,\
+ objectClass=top
+nisplusLDAPobjectDN groupsdir:cn=groupsdir,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer:\
+ cn=groupsdir,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer,\
+ objectClass=top
+nisplusLDAPobjectDN admin:cn=admin,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer:\
+ cn=admin,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer,\
+ objectClass=top
+
+# Standard NIS+ tables
+nisplusLDAPobjectDN passwd_table:cn=passwd,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer:\
+ cn=passwd,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer,\
+ objectClass=top
+nisplusLDAPobjectDN group_table:cn=group,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer:\
+ cn=group,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer,\
+ objectClass=top
+nisplusLDAPobjectDN auto_master_table:cn=auto_master,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer:\
+ cn=auto_master,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer,\
+ objectClass=top
+nisplusLDAPobjectDN auto_home_table:cn=auto_home,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer:\
+ cn=auto_home,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer,\
+ objectClass=top
+nisplusLDAPobjectDN bootparams_table:cn=bootparams,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer:\
+ cn=bootparams,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer,\
+ objectClass=top
+nisplusLDAPobjectDN ethers_table:cn=ethers,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer:\
+ cn=ethers,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer,\
+ objectClass=top
+nisplusLDAPobjectDN hosts_table:cn=hosts,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer:\
+ cn=hosts,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer,\
+ objectClass=top
+nisplusLDAPobjectDN ipnodes_table:cn=ipnodes,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer:\
+ cn=ipnodes,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer,\
+ objectClass=top
+nisplusLDAPobjectDN cred_table:cn=cred,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer:\
+ cn=cred,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer,\
+ objectClass=top
+nisplusLDAPobjectDN aliases_table:cn=aliases,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer:\
+ cn=aliases,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer,\
+ objectClass=top
+nisplusLDAPobjectDN netgroup_table:cn=netgroup,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer:\
+ cn=netgroup,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer,\
+ objectClass=top
+nisplusLDAPobjectDN networks_table:cn=networks,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer:\
+ cn=networks,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer,\
+ objectClass=top
+nisplusLDAPobjectDN netmasks_table:cn=netmasks,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer:\
+ cn=netmasks,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer,\
+ objectClass=top
+nisplusLDAPobjectDN protocols_table:cn=protocols,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer:\
+ cn=protocols,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer,\
+ objectClass=top
+nisplusLDAPobjectDN rpc_table:cn=rpc,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer:\
+ cn=rpc,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer,\
+ objectClass=top
+nisplusLDAPobjectDN services_table:cn=services,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer:\
+ cn=services,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer,\
+ objectClass=top
+nisplusLDAPobjectDN auth_attr_table:cn=auth_attr,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer:\
+ cn=auth_attr,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer,\
+ objectClass=top
+nisplusLDAPobjectDN exec_attr_table:cn=exec_attr,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer:\
+ cn=exec_attr,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer,\
+ objectClass=top
+nisplusLDAPobjectDN prof_attr_table:cn=prof_attr,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer:\
+ cn=prof_attr,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer,\
+ objectClass=top
+nisplusLDAPobjectDN user_attr_table:cn=user_attr,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer:\
+ cn=user_attr,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer,\
+ objectClass=top
+nisplusLDAPobjectDN audit_user_table:cn=audit_user,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer:\
+ cn=audit_user,ou=nisPlus,?base?\
+ objectClass=nisplusObjectContainer,\
+ objectClass=top
+
+
+# Standard NIS+ table entries
+
+# If storing table entry owner, group, access, and/or TTL in LDAP,
+# the write-portion of the nisplusLDAPobjectDN needs the following
+# additional object class specification:
+# objectClass=nisplusEntryData
+
+nisplusLDAPobjectDN passwd:ou=People,?one?objectClass=shadowAccount,\
+ objectClass=posixAccount:\
+ ou=People,?one?objectClass=shadowAccount,\
+ objectClass=posixAccount,\
+ objectClass=account,objectClass=top
+nisplusLDAPobjectDN group:ou=Group,?one?objectClass=posixGroup:\
+ ou=Group,?one?objectClass=posixGroup,\
+ objectClass=top
+#
+# For backward compatibility with Solaris 8 LDAP Naming Services,
+# use these entries instead of using automountMapName:
+#
+#nisplusLDAPobjectDN auto_master:nismapname=auto_master,\
+# ?one?objectClass=nisObject:\
+# nismapname=auto_master,\
+# ?one?objectClass=nisObject,\
+# objectClass=top
+#nisplusLDAPobjectDN auto_home:nismapname=auto_home,\
+# ?one?objectClass=nisObject:\
+# nismapname=auto_home,\
+# ?one?objectClass=nisObject,\
+# objectClass=top
+nisplusLDAPobjectDN auto_master:automountmapname=auto_master,\
+ ?one?objectClass=automount:\
+ automountmapname=auto_master,\
+ ?one?objectClass=automount,\
+ objectClass=top
+nisplusLDAPobjectDN auto_home:automountmapname=auto_home,\
+ ?one?objectClass=automount:\
+ automountmapname=auto_home,\
+ ?one?objectClass=automount,\
+ objectClass=top
+nisplusLDAPobjectDN bootparams:ou=Ethers,?one?objectClass=bootableDevice,\
+ bootParameter=*:\
+ ou=Ethers,?one?objectClass=bootableDevice,\
+ objectClass=device,\
+ objectClass=top:\
+ dbid=bootparams_del
+nisplusLDAPobjectDN ethers:ou=Ethers,?one?objectClass=ieee802Device,\
+ macAddress=*:\
+ ou=Ethers,?one?objectClass=ieee802Device,\
+ objectClass=device,\
+ objectClass=top:\
+ dbid=ethers_del
+nisplusLDAPobjectDN hosts:ou=Hosts,?one?objectClass=ipHost:\
+ ou=Hosts,?one?objectClass=ipHost,\
+ objectClass=device,objectClass=top
+nisplusLDAPobjectDN ipnodes:ou=Hosts,?one?objectClass=ipHost:\
+ ou=Hosts,?one?objectClass=ipHost,\
+ objectClass=device,objectClass=top
+# Unless you have enabled nisplusPrincipalName in the ou=People container,
+# the credlocal entries will be precisely those that have the nisKeyObject
+# object class, so it's unnecessary to write credlocal entries to LDAP.
+# If nisplusPrincipalName is enabled, you can add a write specification
+# to the credlocal nisplusLDAPobjectDN:
+#nisplusLDAPobjectDN credlocal:ou=People,?one?objectClass=nisKeyObject:\
+# ou=People,?one?objectClass=nisKeyObject,\
+# objectClass=nisplusAuthName
+nisplusLDAPobjectDN credlocal:ou=People,?one?objectClass=nisKeyObject
+
+# If nisplusPrincipalName/nisplusNetname are enabled, the write specifications
+# for 'creduser' and 'crednode' should inlcude the nisplusAuthName object
+# class. Replace the write-portion with:
+# ou=People,?one?objectClass=nisKeyObject,\
+# objectClass=nisplusAuthName
+nisplusLDAPobjectDN creduser:ou=People,?one?objectClass=nisKeyObject:\
+ ou=People,?one?objectClass=nisKeyObject
+nisplusLDAPobjectDN crednode:ou=Hosts,?one?objectClass=nisKeyObject:\
+ ou=Hosts,?one?objectClass=nisKeyObject
+nisplusLDAPobjectDN aliases:ou=Aliases,?one?objectClass=mailGroup:\
+ ou=Aliases,?one?objectClass=mailGroup,\
+ objectClass=top
+nisplusLDAPobjectDN netgroup:ou=Netgroup,?one?objectClass=nisNetgroup:\
+ ou=Netgroup,?one?objectClass=nisNetgroup,\
+ objectClass=top
+nisplusLDAPobjectDN networks:ou=Networks,?one?objectClass=ipNetwork:\
+ ou=Networks,?one?objectClass=ipNetwork,\
+ objectClass=top
+nisplusLDAPobjectDN netmasks:ou=Networks,?one?objectClass=ipNetwork,\
+ ipNetMaskNumber=*:\
+ ou=Networks,?one?objectClass=ipNetwork:\
+ dbid=netmasks_del
+nisplusLDAPobjectDN protocols:ou=Protocols,?one?objectClass=ipProtocol:\
+ ou=Protocols,?one?objectClass=ipProtocol,\
+ objectClass=top
+nisplusLDAPobjectDN rpc:ou=Rpc,?one?objectClass=oncRpc:\
+ ou=Rpc,?one?objectClass=oncRpc,objectClass=top
+nisplusLDAPobjectDN services:ou=Services,?one?objectClass=ipService:\
+ ou=Services,?one?objectClass=ipService,\
+ objectClass=top
+nisplusLDAPobjectDN auth_attr:\
+ ou=SolarisAuthAttr,?one?objectClass=SolarisAuthAttr:\
+ ou=SolarisAuthAttr,?one?objectClass=SolarisAuthAttr,\
+ objectClass=top
+nisplusLDAPobjectDN exec_attr:\
+ ou=SolarisProfAttr,?one?objectClass=SolarisExecAttr,\
+ SolarisKernelSecurityPolicy=*:\
+ ou=SolarisProfAttr,?one?objectClass=SolarisExecAttr,\
+ objectClass=SolarisProfAttr,\
+ objectClass=top
+nisplusLDAPobjectDN prof_attr:\
+ ou=SolarisProfAttr,?one?objectClass=SolarisProfAttr,\
+ SolarisAttrLongDesc=*:\
+ ou=SolarisProfAttr,?one?objectClass=SolarisProfAttr,\
+ objectClass=SolarisExecAttr,\
+ objectClass=top
+nisplusLDAPobjectDN user_attr:ou=People,?one?objectClass=SolarisUserAttr,\
+ solarisAttrKeyValue=*:\
+ ou=People,?one?objectClass=SolarisUserAttr:\
+ dbid=user_attr_del
+nisplusLDAPobjectDN audit_user:ou=People,?one?objectClass=SolarisAuditUser,\
+ SolarisAuditAlways=*,\
+ SolarisAuditNever=*:\
+ ou=People,?one?objectClass=SolarisAuditUser:\
+ dbid=audit_user_del
+
+
+#################################################################
+# nisplusLDAPattributeFromColumn
+#
+# The nisplusLDAPattributeFromColumn values establish the mapping
+# of LDAP container entries to NIS+ table columns.
+#
+# Before using, make sure that the LDAP server(s) understand the
+# RFC 2307 attributes and object classes.
+# See the Getting Started section on NIS+LDAPmapping(4) for
+# more information.
+#################################################################
+
+# Standard NIS+ table entries
+nisplusLDAPattributeFromColumn \
+ passwd: dn=("uid=%s,", name), \
+ cn=name, \
+ uid=name, \
+ userPassword=("{crypt}%s", passwd), \
+ uidNumber=uid, \
+ gidNumber=gid, \
+ gecos=gcos, \
+ homeDirectory=home, \
+ loginShell=shell, \
+ (shadowLastChange,shadowMin,shadowMax, \
+ shadowWarning, shadowInactive,shadowExpire)=\
+ (shadow, ":")
+# If owner/group/access information for NIS+ passwd entries should be
+# stored in LDAP, make sure the LDAP server knows about the nisplusEntryData
+# object class, add it to the write portion of the nisplusLDAPobjectDN for
+# passwd, and enable the lines below.
+# nisplusEntryOwner=zo_owner, \
+# nisplusEntryGroup=zo_group, \
+# nisplusEntryAccess=zo_access
+nisplusLDAPattributeFromColumn \
+ group: dn=("cn=%s,", name), \
+ cn=name, \
+ userPassword=("{crypt}%s", passwd), \
+ gidNumber=gid, \
+ (memberUid)=(members, ",")
+#
+# For backward compatibility with Solaris 8 LDAP Naming Services,
+# use these entries instead of using automountMapName:
+#
+#nisplusLDAPattributeFromColumn \
+# auto_master: dn=("cn=%s,", key), \
+# cn=key, \
+# nismapentry=value, \
+# nismapname=("auto_master")
+#nisplusLDAPattributeFromColumn \
+# auto_home: dn=("cn=%s,", key), \
+# cn=key, \
+# nismapentry=value, \
+# nismapname=("auto_home")
+nisplusLDAPattributeFromColumn \
+ auto_master: dn=("automountKey=%s,", key), \
+ automountKey=key, \
+ automountInformation=value
+nisplusLDAPattributeFromColumn \
+ auto_home: dn=("automountKey=%s,", key), \
+ automountKey=key, \
+ automountInformation=value
+nisplusLDAPattributeFromColumn \
+ bootparams: dn=("cn=%s,", key), \
+ cn=key, \
+ (bootParameter)=(value, " ")
+nisplusLDAPattributeFromColumn \
+ bootparams_del: dn=("cn=%s,", key), \
+ bootParameter=
+nisplusLDAPattributeFromColumn \
+ ethers: dn=("cn=%s,", name), \
+ macAddress=addr, \
+ cn=name
+nisplusLDAPattributeFromColumn \
+ ethers_del: dn=("cn=%s,", name), \
+ macAddress=
+nisplusLDAPattributeFromColumn \
+ hosts: dn=("cn=%s+ipHostNumber=%s,", cname, addr), \
+ cn=cname, \
+ cn=name, \
+ ipHostNumber=addr, \
+ description=comment
+nisplusLDAPattributeFromColumn \
+ ipnodes: dn=("cn=%s+ipHostNumber=%s,", cname, addr), \
+ cn=cname, \
+ cn=name, \
+ ipHostNumber=addr, \
+ description=comment
+
+# The cred* entries below only support principals in the local domain. In
+# order to allow out-of-domain principals, read the Getting Started section
+# on NIS+LDAPmapping(4) for information concerning the 'nisplusPrincipalName'
+# and 'nisplusNetname' attributes. Once these have been configured for your
+# LDAP server, you can replace the cred* entries with the commented ones below.
+#nisplusLDAPattributeFromColumn \
+# credlocal: dn=("uid=%s,", (cname, "%s.*")), \
+# nisplusPrincipalName=cname, \
+# uidNumber=auth_name, \
+# gidNumber=public_data, \
+# nisplusEntryOwner=zo_owner, \
+# nisplusEntryGroup=zo_group, \
+# nisplusEntryAccess=zo_access
+#nisplusLDAPattributeFromColumn \
+# creduser: dn=("uid=%s,", (cname, "%s.*")), \
+# nisplusPrincipalName=cname, \
+# nisplusNetname=auth_name, \
+# nisPublicKey=("{%s}%s", \
+# auth_type, public_data), \
+# nisSecretKey=("{%s}%s", \
+# auth_type, private_data), \
+# nisplusEntryOwner=zo_owner, \
+# nisplusEntryGroup=zo_group, \
+# nisplusEntryAccess=zo_access
+## The 'dn' setting for 'crednode' requires that there's an ipHostNumber
+## available when the credentials are stored.
+#nisplusLDAPattributeFromColumn \
+# crednode: dn=("cn=%s+ipHostNumber=%s,", \
+# (cname, "%s.*"), \
+# ldap:ipHostNumber:?one?("cn=%s", (cname, "%s.*"))), \
+# nisplusPrincipalName=cname, \
+# nisplusNetname=auth_name, \
+# nisPublicKey=("{%s}%s", \
+# auth_type, public_data), \
+# nisSecretKey=("{%s}%s", \
+# auth_type, private_data), \
+# nisplusEntryOwner=zo_owner, \
+# nisplusEntryGroup=zo_group, \
+# nisplusEntryAccess=zo_access
+
+nisplusLDAPattributeFromColumn \
+ credlocal: dn=("uid=%s,", (cname, "%s.*")), \
+ uidNumber=auth_name, \
+ gidNumber=public_data
+# If owner/group/access information for NIS+ cred LOCAL entries should be
+# stored in LDAP, make sure the LDAP server knows about the nisplusEntryData
+# object class, add it to the write portion of the nisplusLDAPobjectDN for
+# credlocal, and enable the lines below.
+# nisplusEntryOwner=zo_owner, \
+# nisplusEntryGroup=zo_group, \
+# nisplusEntryAccess=zo_access
+nisplusLDAPattributeFromColumn \
+ creduser: dn=("uid=%s,", (cname, "%s.*")), \
+ nisPublicKey=("{%s}%s", \
+ auth_type, public_data), \
+ nisSecretKey=("{%s}%s", \
+ auth_type, private_data)
+# If owner/group/access information for NIS+ cred user entries should be
+# stored in LDAP, make sure the LDAP server knows about the nisplusEntryData
+# object class, add it to the write portion of the nisplusLDAPobjectDN for
+# creduser, and enable the lines below.
+# nisplusEntryOwner=zo_owner, \
+# nisplusEntryGroup=zo_group, \
+# nisplusEntryAccess=zo_access
+
+# The 'dn' setting for 'crednode' requires that there's an ipHostNumber
+# available when the credentials are stored.
+nisplusLDAPattributeFromColumn \
+ crednode: dn=("cn=%s+ipHostNumber=%s,", \
+ (cname, "%s.*"), \
+ ldap:ipHostNumber:?one?("cn=%s", (cname, "%s.*"))), \
+ nisPublicKey=("{%s}%s", \
+ auth_type, public_data), \
+ nisSecretKey=("{%s}%s", \
+ auth_type, private_data)
+# If owner/group/access information for NIS+ cred node entries should be
+# stored in LDAP, make sure the LDAP server knows about the nisplusEntryData
+# object class, add it to the write portion of the nisplusLDAPobjectDN for
+# crednode, and enable the lines below.
+# nisplusEntryOwner=zo_owner, \
+# nisplusEntryGroup=zo_group, \
+# nisplusEntryAccess=zo_access
+
+nisplusLDAPattributeFromColumn \
+ aliases: dn=("mail=%s,", alias), \
+ cn=alias, \
+ mail=alias, \
+ (mgrprfc822mailmember)= (expansion, ",")
+nisplusLDAPattributeFromColumn \
+ netgroup: dn=("cn=%s,", name), \
+ cn=name, \
+ memberNisNetgroup=group, \
+ nisNetgroupTriple=("(%s,%s,%s)", \
+ host, user, domain), \
+ description=comment
+nisplusLDAPattributeFromColumn \
+ networks: dn=("ipNetworkNumber=%s,", addr), \
+ cn=cname, \
+ cn=name, \
+ ipNetworkNumber=addr, \
+ description=comment
+nisplusLDAPattributeFromColumn \
+ netmasks: dn=("ipNetworkNumber=%s,", addr), \
+ ipNetworkNumber=addr, \
+ ipNetmaskNumber=mask, \
+ description=comment
+# Since netmasks share the ou=Networks container with networks, the default
+# is to delete just the ipNetmaskNumber attribute.
+nisplusLDAPattributeFromColumn \
+ netmasks_del: dn=("ipNetworkNumber=%s,", addr), \
+ ipNetmaskNumber=
+nisplusLDAPattributeFromColumn \
+ protocols: dn=("cn=%s,", cname), \
+ cn=cname, \
+ cn=name, \
+ ipProtocolNumber=number, \
+ description=comment
+nisplusLDAPattributeFromColumn \
+ rpc: dn=("cn=%s,", cname), \
+ cn=cname, \
+ cn=name, \
+ oncRpcNumber=number, \
+ description=comment
+nisplusLDAPattributeFromColumn \
+ services: dn=("cn=%s+ipServiceProtocol=%s,",\
+ cname, proto), \
+ cn=cname, \
+ cn=name, \
+ ipServiceProtocol=proto, \
+ ipServicePort=port, \
+ description=comment
+nisplusLDAPattributeFromColumn \
+ auth_attr: dn=("cn=%s,", name), \
+ cn=name, \
+ SolarisAttrReserved1=res1, \
+ SolarisAttrReserved2=res2, \
+ SolarisAttrShortDesc=short_desc, \
+ SolarisAttrLongDesc=long_desc, \
+ SolarisAttrKeyValue=attr
+nisplusLDAPattributeFromColumn \
+ exec_attr: dn=("cn=%s+SolarisKernelSecurityPolicy=%s+SolarisProfileType=%s+SolarisProfileId=%s,", name, policy, type, id), \
+ cn=name, \
+ SolarisKernelSecurityPolicy=policy, \
+ SolarisProfileType=type, \
+ SolarisAttrReserved1=res1, \
+ SolarisAttrReserved2=res2, \
+ SolarisProfileId=id, \
+ SolarisAttrKeyValue=attr
+nisplusLDAPattributeFromColumn \
+ prof_attr: dn=("cn=%s,", name), \
+ cn=name, \
+ SolarisAttrReserved1=res1, \
+ SolarisAttrReserved2=res2, \
+ SolarisAttrLongDesc=desc, \
+ SolarisAttrKeyValue=attr
+nisplusLDAPattributeFromColumn \
+ user_attr: dn=("uid=%s,", name), \
+ SolarisUserQualifier=qualifier, \
+ SolarisAttrReserved1=res1, \
+ SolarisAttrReserved2=res2, \
+ SolarisAttrKeyValue=attr
+nisplusLDAPattributeFromColumn \
+ audit_user: dn=("uid=%s,", name), \
+ SolarisAuditAlways=always, \
+ SolarisAuditNever=never
+# Since user_attr and audit_user share the ou=People container, only delete
+# the user_attr/audit_user attributes proper.
+nisplusLDAPattributeFromColumn \
+ user_attr_del: dn=("uid=%s,", name), \
+ SolarisUserQualifier=, \
+ SolarisAttrReserved1=, \
+ SolarisAttrReserved2=, \
+ SolarisAttrKeyValue=
+nisplusLDAPattributeFromColumn \
+ audit_user_del: dn=("uid=%s,", name), \
+ SolarisAuditAlways=, \
+ SolarisAuditNever=
+
+
+#################################################################
+# nisplusLDAPcolumnFromAttribute
+#
+# The nisplusLDAPcolumnFromAttribute values establish the mapping
+# from NIS+ table columns to LDAP container entries.
+#
+# Before using, make sure that the LDAP server(s) understand the
+# RFC 2307 attributes and object classes.
+# See the Getting Started section on NIS+LDAPmapping(4) for
+# more information.
+#################################################################
+
+# Standard NIS+ table entries
+nisplusLDAPcolumnFromAttribute \
+ passwd: name=uid, \
+ ("{crypt}%s", passwd)=userPassword, \
+ uid=uidNumber, \
+ gid=gidNumber, \
+ gcos=gecos, \
+ home=homeDirectory, \
+ shell=loginShell, \
+ shadow=("%s:%s:%s:%s:%s:%s", \
+ shadowLastChange, \
+ shadowMin, \
+ shadowMax, \
+ shadowWarning, \
+ shadowInactive, \
+ shadowExpire)
+# If owner/group/access information for NIS+ passwd entries should be
+# retrieved from LDAP, make sure the LDAP server knows about the
+# nisplusEntryData object class, and enable the lines below.
+# zo_owner=nisplusEntryOwner, \
+# zo_group=nisplusEntryGroup, \
+# zo_access=nisplusEntryAccess
+nisplusLDAPcolumnFromAttribute \
+ group: name=cn, \
+ ("{crypt}%s", passwd)=userPassword, \
+ gid=gidNumber, \
+ members=("%s,", (memberUid), ",")
+#
+# For backward compatibility with Solaris 8 LDAP Naming Services,
+# use these entries instead of using automountMapName:
+#
+#nisplusLDAPcolumnFromAttribute \
+# auto_master: key=cn, \
+# value=nismapentry
+#nisplusLDAPcolumnFromAttribute \
+# auto_home: key=cn, \
+# value=nismapentry
+nisplusLDAPcolumnFromAttribute \
+ auto_master: key=automountKey, \
+ value=automountInformation
+nisplusLDAPcolumnFromAttribute \
+ auto_home: key=automountKey, \
+ value=automountInformation
+nisplusLDAPcolumnFromAttribute \
+ bootparams: key=cn, \
+ value=("%s ", (bootParameter), " ")
+nisplusLDAPcolumnFromAttribute \
+ ethers: addr=macAddress, \
+ name=cn
+nisplusLDAPcolumnFromAttribute \
+ hosts: cname=cn, \
+ (name)=(cn), \
+ addr=ipHostNumber, \
+ comment=description
+nisplusLDAPcolumnFromAttribute \
+ ipnodes: cname=cn, \
+ (name)=(cn), \
+ addr=ipHostNumber, \
+ comment=description
+
+# The cred* entries below only support principals in the local domain. In
+# order to allow out-of-domain principals, read the Getting Started section
+# on NIS+LDAPmapping(4) for information concerning the 'nisplusPrincipalName'
+# and 'nisplusNetname' attributes. Once these have been configured for your
+# LDAP server, you can replace the cred* entries with the commented ones below.
+#nisplusLDAPcolumnFromAttribute \
+# credlocal: cname=nisplusPrincipalName=cname, \
+# auth_type=("LOCAL"), \
+# auth_name=uidNumber, \
+# public_data=gidNumber, \
+# zo_owner=nisplusEntryOwner, \
+# zo_group=nisplusEntryGroup, \
+# zo_access=nisplusEntryAccess
+#nisplusLDAPcolumnFromAttribute \
+# creduser: cname=nisplusPrincipalName, \
+# auth_name=nisplusNetname, \
+# ("{%s}%s", auth_type, public_data)= \
+# nisPublicKey, \
+# ("{%s}%s", auth_type, private_data)= \
+# nisSecretKey, \
+# zo_owner=nisplusEntryOwner, \
+# zo_group=nisplusEntryGroup, \
+# zo_access=nisplusEntryAccess
+#nisplusLDAPcolumnFromAttribute \
+# crednode: \
+# cname=nisplusPrincipalName, \
+# auth_name=nisplusNetname, \
+# ("{%s}%s", auth_type, public_data)= \
+# nisPublicKey, \
+# ("{%s}%s", auth_type, private_data)= \
+# nisSecretKey, \
+# zo_owner=nisplusEntryOwner, \
+# zo_group=nisplusEntryGroup, \
+# zo_access=nisplusEntryAccess
+
+nisplusLDAPcolumnFromAttribute \
+ credlocal: cname=("%s.%s", uid, \
+ (nis+:zo_owner[]cred.org_dir, "*.%s")), \
+ auth_type=("LOCAL"), \
+ auth_name=uidNumber, \
+ public_data=gidNumber
+# Enable if owner/group/access information is stored in LDAP,
+# and the LDAP server knows about the nisplusEntryData object class.
+# zo_owner=nisplusEntryOwner, \
+# zo_group=nisplusEntryGroup, \
+# zo_access=nisplusEntryAccess
+nisplusLDAPcolumnFromAttribute \
+ creduser: cname=("%s.%s", uid, \
+ (nis+:zo_owner[]cred.org_dir, "*.%s")), \
+ auth_name=("unix.%s@%s", uidNumber, \
+ (nis+:zo_owner[]cred.org_dir, "*.%s.")), \
+ ("{%s}%s", auth_type, public_data)= \
+ nisPublicKey, \
+ ("{%s}%s", auth_type, private_data)= \
+ nisSecretKey
+# Enable if owner/group/access information is stored in LDAP,
+# and the LDAP server knows about the nisplusEntryData object class.
+# zo_owner=nisplusEntryOwner, \
+# zo_group=nisplusEntryGroup, \
+# zo_access=nisplusEntryAccess
+
+# If your Hosts container store fully qualified host names, the 'cname'
+# and 'auth_name' assignments below should be replaced with:
+# cname=("%s.", cn), \
+# auth_name=("unix.%s@%s", (cn, "%s.*"), \
+# (cn, "*.%s.")) \
+#
+nisplusLDAPcolumnFromAttribute \
+ crednode: \
+ cname=("%s.%s", cn, \
+ (nis+:zo_owner[]cred.org_dir, "*.%s")), \
+ auth_name=("unix.%s@%s", cn, \
+ (nis+:zo_owner[]cred.org_dir, "*.%s.")), \
+ ("{%s}%s", auth_type, public_data)= \
+ nisPublicKey, \
+ ("{%s}%s", auth_type, private_data)= \
+ nisSecretKey
+# Enable if owner/group/access information is stored in LDAP,
+# and the LDAP server knows about the nisplusEntryData object class.
+# zo_owner=nisplusEntryOwner, \
+# zo_group=nisplusEntryGroup, \
+# zo_access=nisplusEntryAccess
+nisplusLDAPcolumnFromAttribute \
+ aliases: alias=mail, \
+ expansion= \
+ ("%s,", (mgrprfc822mailmember), ",")
+nisplusLDAPcolumnFromAttribute \
+ netgroup: name=cn, \
+ (group)=(memberNisNetgroup), \
+ ("(%s,%s,%s)", host, user, domain)= \
+ (nisNetgroupTriple), \
+ comment=description
+nisplusLDAPcolumnFromAttribute \
+ networks: cname=cn, \
+ (name)=(cn), \
+ addr=ipNetworkNumber, \
+ comment=description
+nisplusLDAPcolumnFromAttribute \
+ netmasks: addr=ipNetworkNumber, \
+ mask=ipNetmaskNumber, \
+ comment=description
+nisplusLDAPcolumnFromAttribute \
+ protocols: cname=cn, \
+ (name)=(cn), \
+ number=ipProtocolNumber, \
+ comment=description
+nisplusLDAPcolumnFromAttribute \
+ rpc: cname=cn, \
+ (name)=(cn), \
+ number=oncRpcNumber, \
+ comment=description
+nisplusLDAPcolumnFromAttribute \
+ services: cname=cn, \
+ (name)=(cn), \
+ proto=ipServiceProtocol, \
+ port=ipServicePort, \
+ comment=description
+nisplusLDAPcolumnFromAttribute \
+ auth_attr: name=cn, \
+ res1=SolarisAttrReserved1, \
+ res2=SolarisAttrReserved2, \
+ short_desc=SolarisAttrShortDesc, \
+ long_desc=SolarisAttrLongDesc, \
+ attr=SolarisAttrKeyValue
+nisplusLDAPcolumnFromAttribute \
+ exec_attr: name=cn, \
+ policy=SolarisKernelSecurityPolicy, \
+ type=SolarisProfileType, \
+ res1=SolarisAttrReserved1, \
+ res2=SolarisAttrReserved2, \
+ id=SolarisProfileId, \
+ attr=SolarisAttrKeyValue
+nisplusLDAPcolumnFromAttribute \
+ prof_attr: name=cn, \
+ res1=SolarisAttrReserved1, \
+ res2=SolarisAttrReserved2, \
+ desc=SolarisAttrLongDesc, \
+ attr=SolarisAttrKeyValue
+nisplusLDAPcolumnFromAttribute \
+ user_attr: name=cn, \
+ qualifier=SolarisUserQualifier, \
+ res1=SolarisAttrReserved1, \
+ res2=SolarisAttrReserved2, \
+ attr=SolarisAttrKeyValue
+nisplusLDAPcolumnFromAttribute \
+ audit_user: name=cn, \
+ always=SolarisAuditAlways, \
+ never=SolarisAuditNever
+
+
+#################################################################
+# timezone and client_info
+#
+# The standard NIS+ "timezone" and "client_info" tables require
+# definitions of object classes and attributes other than the
+# RFC 2307 ones. See the Getting Started section on the
+# NIS+LDAPmapping(4) man page before enabling the configuration
+# below.
+#################################################################
+
+#nisplusLDAPdatabaseIdMapping client_info_table:client_info.org_dir
+#nisplusLDAPdatabaseIdMapping timezone_table:timezone.org_dir
+
+#nisplusLDAPdatabaseIdMapping client_info:client_info.org_dir
+#nisplusLDAPdatabaseIdMapping timezone:timezone.org_dir
+
+#nisplusLDAPentryTtl client_info_table:21600:43200:43200
+#nisplusLDAPentryTtl timezone_table:21600:43200:43200
+
+#nisplusLDAPentryTtl client_info:1800:3600:3600
+#nisplusLDAPentryTtl timezone:1800:3600:3600
+
+#nisplusLDAPobjectDN client_info_table:cn=client_info,ou=nisPlus,?base?\
+# objectClass=nisplusObjectContainer:\
+# cn=client_info,ou=nisPlus,?base?\
+# objectClass=nisplusObjectContainer,\
+# objectClass=top
+#nisplusLDAPobjectDN timezone_table:cn=timezone,ou=nisPlus,?base?\
+# objectClass=nisplusObjectContainer:\
+# cn=timezone,ou=nisPlus,?base?\
+# objectClass=nisplusObjectContainer,\
+# objectClass=top
+
+#nisplusLDAPobjectDN client_info:ou=ClientInfo,?one?\
+# objectClass=nisplusClientInfoData:\
+# ou=ClientInfo,?one?\
+# objectClass=nisplusClientInfoData,\
+# objectClass=top
+#nisplusLDAPobjectDN timezone:ou=TimeZone,?one?\
+# objectClass=nisplusTimeZoneData:\
+# ou=TimeZone,?one?\
+# objectClass=nisplusTimeZoneData,\
+# objectClass=top
+
+#nisplusLDAPattributeFromColumn \
+# client_info: dn=("cn=%s,", client), \
+# cn=client, \
+# nisplusClientInfoAttr=attr, \
+# nisplusClientInfoInfo=info, \
+# nisplusClientInfoFlags=flags
+#nisplusLDAPattributeFromColumn \
+# timezone: dn=("cn=%s,", name), \
+# cn=name, \
+# nisplusTimeZone=tzone, \
+# description=comment
+
+#nisplusLDAPcolumnFromAttribute \
+# client_info: client=cn, \
+# attr=nisplusClientInfoAttr, \
+# info=nisplusClientInfoInfo, \
+# flags=nisplusClientInfoFlags
+#nisplusLDAPcolumnFromAttribute \
+# timezone: name=cn, \
+# tzone=nisplusTimeZone, \
+# comment=description
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/configs/rpc.nisd b/usr/src/cmd/rpcsvc/nis/rpc.nisd/configs/rpc.nisd
new file mode 100644
index 0000000000..7bd0c1f184
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/configs/rpc.nisd
@@ -0,0 +1,233 @@
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+# Configuration file for rpc.nisd(1M); see rpc.nisd(4) for more information,
+# and NIS+LDAPmapping(4) for configuration of NIS+ to LDAP mapping.
+
+# Unless otherwise noted, commented lines show default values.
+
+
+# The number of RPC service threads. Zero selects three plus
+# the number of CPUs available when the rpc.nisd starts.
+#
+#nisplusNumberOfServiceThreads=0
+
+# The maximum record size that RPC can use over connection oriented
+# transports. Values below 9000 are invalid and will result in the
+# default being used.
+#
+#nisplusMaxRPCRecordSize=9000
+
+# The action to take when thread creation failed. Does not apply to
+# RPC service threads.
+#
+#nisplusThreadCreationErrorAction=pass_error
+
+# Number of times to retry a failed thread creation. The default when
+# nisplusThreadCreationErrorAttempts has no value is unlimited
+#
+#nisplusThreadCreationErrorAttempts=
+
+# The time to wait (seconds) between each attempt to create a thread.
+#
+#nisplusThreadCreationErrorTimeout=15
+
+
+# The action to take when a full dump of a NIS+ directory fails.
+#
+#nisplusDumpErrorAction=retry
+
+# Number of times to retry a failed full dump. The default when
+# nisplusDumpErrorAttempts has no value is unlimited.
+#
+#nisplusDumpErrorAttempts=
+
+# The time to wait (seconds) bewteen each failed full dump.
+#
+#nisplusDumpErrorTimeout=120
+
+
+# The type of NIS+ service provided by a replica during a resync.
+#
+#nisplusResyncService=from_copy
+
+
+# How updates are batched on a master server before pinging replicas
+#
+#nisplusUpdateBatching=accumulate
+
+# The minimum length of time (seconds) during which updates are batched.
+#
+#nisplusUpdateBatchingTimeout=120
+
+
+# Where to look for configuration information in LDAP. Leave empty or undefined
+# to use this file, in which case the values of the other 'nisplusLdapConfig*'
+# attributes are ignored.
+#
+#nisplusLDAPconfigDN=
+
+
+# Server(s) for configuration information. There is no default; use the
+# value on the line below for an LDAP server running on this machine, at
+# port 389.
+#
+#nisplusLDAPconfigPreferredServerList=127.0.0.1:389
+
+
+# Authentication method(s) to obtain configuration information.
+#
+#nisplusLDAPconfigAuthenticationMethod=none
+
+
+# Transport layer security for configuration information
+#
+#nisplusLDAPconfigTLS=none
+
+
+# Certificate DB for transport layer security
+#
+#nisplusLDAPconfigTLSCertificateDBPath=/var/nis/cert7.db
+
+
+# Proxy user(s) to obtain configuration information. The line below
+# is an example of the format.
+#
+#nisplusLDAPconfigProxyUser=cn=nisplusAdmin,ou=People,
+
+
+# Password for proxy user. Must be supplied if the authentication method
+# requires a password. If a password appears in this file, it should be
+# protected appropriately agains access by unauthorized users.
+#
+#nisplusLDAPconfigProxyPassword=
+
+
+# Server list for mapping data to/from LDAP. There is no default; use the
+# value on the line below for an LDAP server running on this machine, at
+# port 389.
+#
+preferredServerList=127.0.0.1:389
+
+
+# Authentication method for mapping data to/from LDAP
+#
+authenticationMethod=none
+
+
+# Starting point for LDAP data. Mapping entries can override this value.
+# The default is derived from the NIS+ domain name (baseDomain).
+#
+#defaultSearchBase=
+
+
+# NIS+ domain name to use for mapping data to/from LDAP. The default is
+# the rpc.nisd's notion of the domain it serves.
+#
+#nisplusLDAPbaseDomain=
+
+
+# Transport layer security for mapping data to/from LDAP.
+#
+nisplusLDAPTLS=none
+
+
+# Certificate DB for transport layer security
+#
+#nisplusLDAPTLSCertificateDBPath=/var/nis/cert7.db
+
+
+# Proxy user for rpc.nisd. Assumed to have appropriate permission to read
+# and/or create or modify LDAP data. The line below is an example of the
+# format.
+#
+#nisplusLDAPproxyUser=cn=nisplusAdmin,ou=People,
+
+
+# Password for proxy user. Must be supplied if the authentication method
+# requires a password. If a password appears in this file, it should be
+# protected appropriately against unauthorized access.
+#
+#nisplusLDAPproxyPassword=
+
+
+# Timeouts and time/size limits for LDAP operations.
+#
+#nisplusLDAPbindTimeout=15
+#nisplusLDAPsearchTimeout=15
+#nisplusLDAPmodifyTimeout=15
+#nisplusLDAPaddTimeout=15
+#nisplusLDAPdeleteTimeout=15
+#nisplusLDAPsearchTimeLimit=15
+#nisplusLDAPsearchSizeLimit=0
+
+
+# Should the rpc.nisd follow LDAP referrals ?
+#
+#nisplusLDAPfollowReferral=no
+
+
+# Up- or down-load NIS+ data to or from LDAP when starting
+#
+#nisplusLDAPinitialUpdateAction=none
+
+
+# Should the rpc.nisd exit following an initial update
+#
+#nisplusLDAPinitialUpdateOnly=no
+
+
+# Action, number of attempts, and timeout following an LDAP retrieval error
+#
+#nisplusLDAPretrieveErrorAction=use_cached
+#nisplusLDAPretrieveErrorAttempts=
+#nisplusLDAPretrieveErrorTimeout=15
+
+
+# Action, number of attempts, and timeout following an LDAP store error
+#
+#nisplusLDAPstoreErrorAction=retry
+#nisplusLDAPstoreErrorAttempts=
+#nisplusLDAPstoreErrorTimeout=15
+
+
+# Action, number of attempts, and timeout following an LDAP refresh error
+#
+#nisplusLDAPrefreshErrorAction=continue_using
+#nisplusLDAPrefreshErrorAttempts=
+#nisplusLDAPrefreshErrorTimeout=15
+
+
+# When to fetch LDAP data for NIS+ match operations
+#
+#nisplusLDAPmatchFetchAction=no_match_only
+
+
+# ENABLE_NIS_YP_EMULATION affects whether rpc.nisd is started in in NIS
+# (YP) compatibility mode, see rpc.nisd(1M) for details. The default
+# value is "NO". A value of "YES" (any case) results in rpc.nisd being
+# started in NIS (YP) compatibility mode; any other value is ignored.
+#
+#ENABLE_NIS_YP_EMULATION=NO
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/ldap_log.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/ldap_log.c
new file mode 100644
index 0000000000..9dabb03339
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/ldap_log.c
@@ -0,0 +1,146 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <time.h>
+#include <rpcsvc/nis.h>
+#include "nis_proc.h"
+#include <nis_servlist.h>
+#include <ldap_util.h>
+
+int
+beginTransaction(void) {
+ return (begin_transaction(nis_local_principal()));
+}
+
+int
+endTransaction(int xid, nis_object *dirObj) {
+ int ret, master;
+
+ /* Are we the master for the directory ? */
+ if (dirObj != 0 && dirObj->zo_data.zo_type == NIS_DIRECTORY_OBJ &&
+ dirObj->DI_data.do_servers.do_servers_len > 1 &&
+ nis_isserving(dirObj) == 1) {
+ master = 1;
+ } else {
+ master = 0;
+ }
+
+ /*
+ * End the trans.log operation. If we're the master, we also
+ * modify the update time cache, otherwise not.
+ */
+ ret = end_transaction_x(xid, master);
+
+ /*
+ * If we're the master, and there are replicas, make a note to
+ * ping them.
+ */
+ if (master) {
+ ulong_t ttime = last_update(dirObj->DI_data.do_name);
+
+ add_pingitem(dirObj, ttime, &ping_list);
+ }
+
+ return (ret);
+}
+
+extern void flush_local_dircache(nis_name name);
+
+int
+addUpdate(log_entry_t type, char *name, int numAttr, nis_attr *attr,
+ nis_object *obj, nis_object *oldDir, uint32_t ttime) {
+ log_entry le;
+ __nis_buffer_t b = {0, 0};
+ char *myself = "addUpdate";
+
+ if (name == 0 || obj == 0)
+ return (-1);
+
+ /* Supply current time for log entry, if the caller specified zero */
+ if (ttime == 0)
+ ttime = time(0);
+
+ le.le_time = ttime;
+ le.le_type = type;
+ le.le_princp = nis_local_principal();
+ le.le_name = name;
+ le.le_attrs.le_attrs_val = attr;
+ le.le_attrs.le_attrs_len = numAttr;
+ le.le_object = *obj;
+
+ add_update(&le);
+
+ /* Flush the relevant caches */
+ switch (obj->zo_data.zo_type) {
+ case NIS_DIRECTORY_OBJ:
+ /*
+ * Since flush_dircache() looks for a new version in the
+ * directory cache (assuming it's been flushed by the update,
+ * which doesn't happen in our case), we first need to flush
+ * the rpc.nisd dir cache.
+ */
+ flush_local_dircache(obj->DI_data.do_name);
+ flush_dircache(obj->DI_data.do_name, &obj->DI_data);
+ /*
+ * If an old (pre-mod) directory object was supplied, and
+ * we were the master for that object, tell all replicas
+ * (of the old incarnation) to flush it from their caches.
+ */
+ if (oldDir != 0 && nis_isserving(oldDir) == 1) {
+ nis_taglist taglist;
+ nis_tag tags;
+
+ tags.tag_type = TAG_DCACHE_ONE_REFRESH;
+ tags.tag_val = oldDir->DI_data.do_name;
+ taglist.tags.tags_len = 1;
+ taglist.tags.tags_val = &tags;
+ (void) nis_mcast_tags(&oldDir->DI_data, &taglist);
+ }
+ break;
+ case NIS_TABLE_OBJ:
+ bp2buf(myself, &b, "%s.%s", obj->zo_name, obj->zo_domain);
+ if (b.len > 0) {
+ flush_tablecache(b.buf);
+ free(b.buf);
+ }
+ break;
+ case NIS_GROUP_OBJ:
+ bp2buf(myself, &b, "%s.%s", obj->zo_name, obj->zo_domain);
+ if (b.len > 0) {
+ flush_groupcache(b.buf);
+ free(b.buf);
+ }
+ default:
+ break;
+ }
+
+ multival_invalidate(obj);
+
+ return (0);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/log.h b/usr/src/cmd/rpcsvc/nis/rpc.nisd/log.h
new file mode 100644
index 0000000000..e89e427f9f
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/log.h
@@ -0,0 +1,154 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 1992-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Definitions for this server's log implementation. This is server
+ * dependent and may change from implementation to implementation.
+ */
+
+#ifndef _RPC_NISD_LOG_H
+#define _RPC_NISD_LOG_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LOG_FILE "/var/nis/trans.log"
+
+/*
+ * old_stamp_item is used to keep track of the timestamps for all the
+ * directories that have been removed from the transaction log.
+ */
+
+struct old_stamp_item {
+ NIS_HASH_ITEM item;
+ u_long utime;
+ int stamped;
+ u_long xid;
+};
+typedef struct old_stamp_item old_stamp_item;
+
+
+struct stamp_item {
+ NIS_HASH_ITEM item;
+ u_long utime;
+};
+typedef struct stamp_item stamp_item;
+
+/*
+ * The update log is mapped into memory from the file "trans.log".
+ * This mapping is accomplished by the nis_main function when it starts.
+ * When the directory checkpoints, it may truncate the log by making a
+ * copy of it.
+ */
+struct log_upd {
+ u_long lu_magic; /* Update magic number */
+ u_long lu_xid; /* Update transaction ID */
+ u_long lu_time; /* Time of this update */
+ nis_name lu_dirname; /* Directory of this update */
+ struct log_upd *lu_next; /* pointer to next update */
+ struct log_upd *lu_prev; /* pointer to previous update */
+ u_long lu_size; /* size of the data buffer */
+ u_char lu_data[4]; /* pointer to XDR'd log_entry */
+};
+typedef struct log_upd log_upd;
+
+struct log_hdr {
+ u_long lh_magic; /* Log header magic number */
+ u_long lh_state; /* Updating/Stable/Checkpointing */
+ struct log_hdr *lh_addr; /* Current address */
+ u_long lh_num; /* Number of updates in the log */
+ u_long lh_xid; /* Last stable transaction */
+ u_long lh_first; /* Timestamp of earliest update */
+ u_long lh_last; /* Timestamp of last update */
+ log_upd *lh_head; /* pointer to the "first" update */
+ log_upd *lh_tail; /* pointer to the "last" update */
+};
+typedef struct log_hdr log_hdr;
+
+#define LOG_HDR_MAGIC 0x5551212 /* It is a directory service! */
+#define LOG_UPD_MAGIC 0x5552223
+#define LOG_UPD_MODMAG 0x5553332 /* Part of a modify operation */
+
+ /* Tune these */
+/* XXX these need to be tuned */
+#define MAXLOGLEN 0x10000000 /* 256 MB log file */
+#define HIWATER 0x800000 /* 8 MB high water mark */
+#define FILE_BLK_SZ 0x10000 /* grow file based log by 64K */
+
+#define LOG_STABLE 1
+#define LOG_CHECKPOINT 2
+#define LOG_UPDATE 3
+#define LOG_RESYNC 4
+
+#define BACKUP_LOG ".BACKUP_LOG"
+
+/*
+ * Flags used for __log_resync() routine. These flags are mainly used
+ * to check if msync() should be called or not. msync() is used to flush
+ * the current mmap area to the the disk. nislog program should never
+ * call msync().
+ */
+#define FNISD 0 /* called from nisd */
+#define FNISLOG 1 /* called from nislog program */
+#define FCHKPT 2 /* called from checkpointing section */
+
+/*
+ * This macro returns the next longword aligned address.
+ */
+#define NISRNDUP(x) (((x) & 3) ? ((x) + 4) & ~3 : (x))
+
+/*
+ * This macro defines the linear XID size of a transaction update
+ * in the file. The "end address" of the log, and the log size in bytes.
+ */
+#define XID_SIZE(u) (NISRNDUP(sizeof (log_upd) + \
+ (u)->lu_size + \
+ strlen((u)->lu_dirname)+1))
+
+#define LOG_END(log) NISRNDUP(((u_long)(log->lh_tail) + \
+ XID_SIZE(log->lh_tail)))
+#define LOG_SIZE(log) (LOG_END(log) - (u_long)(log))
+
+/* Function prototypes */
+#ifdef __STDC__
+extern char *__make_name(log_entry *);
+extern int __log_resync(log_hdr *, int);
+extern bool_t xdr_log_entry(XDR *, log_entry *);
+#else
+extern char *__make_name();
+extern int __log_resync();
+extern bool_t xdr_log_entry();
+#endif
+
+/* global data */
+extern log_hdr *__nis_log;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RPC_NISD_LOG_H */
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_cleanup.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_cleanup.c
new file mode 100644
index 0000000000..4cf5f7e45c
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_cleanup.c
@@ -0,0 +1,194 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * The functions in this file used to reside in nis_subr_proc.c. In order
+ * to simplify building nislog(1M) (which links in some but not all of the
+ * rpc.nisd object files), it was convenient to move the functions to their
+ * own file.
+ */
+
+#include <syslog.h>
+#include <rpcsvc/nis.h>
+#include "nis_proc.h"
+
+extern FILE *cons;
+
+void
+free_abort(p)
+ char *p;
+{
+ syslog(LOG_CRIT, "Attempting to free a free rag!");
+ syslog(LOG_ERR, "Attempting to free a free rag!");
+ abort();
+}
+
+/*
+ * These functions are used to clean up after ourselves when we return
+ * a result. The primary client are the svc functions which malloc
+ * their data and then put it on the cleanup list when they return.
+ * this allows them to be reentrant.
+ */
+void
+do_cleanup(stuff)
+ cleanup *stuff;
+{
+ cleanup *this, *nextrag;
+ nis_tsd_t *tsd;
+
+ if (stuff == 0)
+ return;
+
+ tsd = __nis_get_tsd();
+
+ for (this = stuff; this; this = nextrag) {
+ nextrag = this->next;
+#ifdef DEBUG
+ if ((this->tag) && verbose)
+ syslog(LOG_INFO, "do_cleanup : '%s'", this->tag);
+#endif
+ (*(this->func))(this->data);
+ this->func = free_abort;
+ this->data = NULL;
+ this->tag = NULL;
+ this->next = tsd->free_rags;
+ tsd->free_rags = this;
+ }
+}
+
+/*
+ * The non-MT code allocates space for 1024 rags. Because the auto-MT
+ * mode of RPC usually creates a new thread for every RPC request,
+ * and since each thread has its own rags list, we only need enough
+ * for one NIS+ operation. Testing showed that handling a nis_list()
+ * (without callback) used seven rags, so 16 seems a good choice,
+ * making it unlikely that a thread will need to allocate rags more
+ * than once.
+ */
+#define RAGCHUNK 16
+
+void
+add_cleanup(clean_func, clean_data, ragtag)
+ void (*clean_func)();
+ void *clean_data;
+ char *ragtag;
+{
+ register cleanup *newrag;
+ int i;
+ nis_tsd_t *tsd;
+
+ if ((! clean_func) || (! clean_data)) {
+ if (cons)
+ fprintf(cons, "no func or data : %s\n", ragtag);
+ return;
+ }
+
+ tsd = __nis_get_tsd();
+
+ if (! tsd->free_rags) {
+ if (verbose)
+ syslog(LOG_DEBUG,
+ "add_cleanup: Low on rags, allocating some more.");
+ /* Allocate extra space to keep track of the newrag block */
+#define newragoffset (1+(sizeof (cleanupblock_t)/sizeof (cleanup)))
+ newrag = (cleanup *)XCALLOC((RAGCHUNK + newragoffset),
+ sizeof (cleanup));
+ if (! newrag) {
+ syslog(LOG_CRIT,
+ "add_cleanup: Can't allocate more rags.");
+ return;
+ }
+
+ /*
+ * Use the first 'newragoffset' element(s) of the 'newrag'
+ * array for housekeeping that enables us to free the array
+ * on thread exit.
+ */
+ {
+ cleanupblock_t **b = &(tsd->ragblocks);
+ cleanupblock_t *n = (cleanupblock_t *)newrag;
+
+ n->next = *b;
+ *b = n;
+#ifdef NIS_MT_DEBUG
+ printf("%d: newrag 0x%x (%d bytes)\n",
+ pthread_self(), n,
+ (RAGCHUNK+newragoffset)*sizeof (cleanup));
+#endif /* NIS_MT_DEBUG */
+ }
+ for (i = newragoffset; i < RAGCHUNK + newragoffset; i++) {
+ newrag[i].next = tsd->free_rags;
+ tsd->free_rags = &(newrag[i]);
+ }
+ }
+
+ newrag = tsd->free_rags;
+ tsd->free_rags = tsd->free_rags->next;
+
+ newrag->func = clean_func;
+ newrag->data = clean_data;
+ newrag->tag = ragtag;
+ newrag->next = tsd->looseends;
+ newrag->id = tsd->cleanup_tag;
+#ifdef DEBUG
+ if (verbose)
+ syslog(LOG_INFO, "add_cleanup (thread # %d: tag # %d '%s'",
+ pthread_self(), tsd->cleanup_tag, ragtag);
+#endif /* DEBUG */
+ tsd->cleanup_tag++;
+ tsd->looseends = newrag;
+}
+
+void
+do_xdr_cleanup(data)
+ xdr_clean_data *data;
+{
+ if (! data)
+ return;
+
+ xdr_free(data->xdr_func, data->xdr_data);
+ XFREE(data->xdr_data);
+ XFREE(data);
+}
+
+void
+add_xdr_cleanup(func, data, t)
+ bool_t (*func)();
+ char *data;
+ char *t;
+{
+ xdr_clean_data *dat = NULL;
+
+ dat = (xdr_clean_data *)XCALLOC(1, sizeof (xdr_clean_data));
+ if (! dat)
+ return;
+
+ dat->xdr_func = func;
+ dat->xdr_data = (void *)data;
+ add_cleanup(do_xdr_cleanup, dat, t);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_db.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_db.c
new file mode 100644
index 0000000000..29bf6defab
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_db.c
@@ -0,0 +1,3343 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 1990-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Ported from
+ * "@(#)nis_db.c 1.42 91/03/21 Copyr 1990 Sun Micro";
+ *
+ * This module contains the glue routines between the actual database
+ * code and the NIS+ server. Presumably they are the routines that should
+ * be exported in the shared library, but they may be at too high a level.
+ */
+
+#include <stdio.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <malloc.h>
+#include <rpcsvc/nis_db.h>
+#include "nis_proc.h"
+#include <lber.h>
+#include <ldap.h>
+#include "ldap_xdr.h"
+#include "ldap_util.h"
+#include "nisdb_mt.h"
+#include "ldap_parse.h"
+
+static nis_error __create_table(char *, nis_object *);
+
+extern bool_t xdr_nis_object();
+extern bool_t xdr_nis_name();
+extern bool_t xdr_nis_oid();
+extern bool_t xdr_objdata(XDR *, objdata*);
+extern bool_t xdr_entry_col(XDR*, entry_col *);
+extern char *relative_name();
+extern db_status __db_defer(char *);
+extern db_status __db_commit(char *);
+extern db_status __db_rollback(char *);
+extern db_result *__db_list_entries(char *, int, nis_attr *, bool_t);
+extern db_status __db_configure(char *);
+int retrieveDone(__nisdb_retry_t *refreshRetry,
+ __nisdb_retry_t *retrieveretry,
+ int nisPlusStat, int ldapStat,
+ nis_error *outStat);
+int storeDone(__nisdb_retry_t *storeRetry,
+ int nisPlusStat, int ldapStat,
+ nis_error *outStat);
+extern db_result *__db_add_entry_nosync(char *, int, nis_attr *, entry_obj *);
+extern db_result *__db_remove_entry_nosync(char *, int, nis_attr *);
+extern db_result *__db_add_entry_nolog(char *, int, nis_attr *, entry_obj *);
+
+typedef struct table_list_entry {
+ char *table;
+ struct table_list_entry *next;
+} table_list_entry_t;
+
+static table_list_entry_t *table_list = 0;
+DECLMUTEXLOCK(table_list);
+
+static int mark_for_sync(char *);
+
+NIS_HASH_TABLE *table_cache = NULL;
+
+/*
+ * Locking of the database is implemented here. We use a two level locking
+ * mechanism:
+ *
+ * (1) Directories
+ *
+ * Write lock Must have full access to the directory,
+ * and prevent any other access.
+ *
+ * Read lock Make sure we get to do our stuff without
+ * interaction with someone that may modify
+ * the directory.
+ *
+ * (2) Tables
+ *
+ * Write lock Any operation that modifies the table or
+ * data in the table.
+ *
+ * Read lock Any other operation on the table.
+ *
+ * Much of this functionality relies on the implicit locking and condition
+ * signaling provided by our private implementation of NIS_HASH_TABLEs.
+ */
+
+/*
+ * If the database is MT safe, 'RO' should be '1' (meaning, really
+ * do just read-only locking); otherwise, it should be '-1' to always
+ * use exclusive locks.
+ */
+#define RO 1
+#define WR -1
+
+static NIS_HASH_TABLE dirlocks = NIS_HASH_TABLE_MT_INIT;
+
+/* Stored in the 'dirlocks' list */
+typedef struct {
+ NIS_HASH_ITEM item;
+ NIS_HASH_TABLE tablelocks;
+ __nis_defer_t defer;
+} dirlock_t;
+
+/* Stored in the 'tablelocks' list */
+typedef struct {
+ NIS_HASH_ITEM item;
+ __nis_defer_t defer;
+} tablelock_t;
+
+#define LOCK_DIRECTORY(name, rw, status, msg) \
+ { \
+ int trylock = (ldapConfig.exclusiveWaitMode == block) ? \
+ 0 : 1; \
+ status = NIS_SUCCESS; \
+ if (__nis_lock_db_directory(name, rw, &trylock, msg) == 0) { \
+ if (trylock == -1) { \
+ status = NIS_TRYAGAIN; \
+ } else { \
+ status = NIS_SYSTEMERROR; \
+ } \
+ } \
+ }
+
+#define ULOCK_DIRECTORY(name, rw, msg) \
+ (void) __nis_ulock_db_directory(name, rw, 0, msg)
+
+#define LOCK_TABLE_NORET(name, rw, status, msg) \
+ { \
+ int trylock = (ldapConfig.exclusiveWaitMode == block) ? \
+ 0 : 1; \
+ status = NIS_SUCCESS; \
+ if (__nis_lock_db_table(name, rw, &trylock, msg) == 0) { \
+ if (trylock == -1) { \
+ status = NIS_TRYAGAIN; \
+ } else { \
+ status = NIS_SYSTEMERROR; \
+ } \
+ } \
+ }
+
+#define LOCK_TABLE(name, rw, res, status, msg) \
+ { \
+ int trylock = (ldapConfig.exclusiveWaitMode == block) ? \
+ 0 : 1; \
+ if (__nis_lock_db_table(name, rw, &trylock, msg) == 0) { \
+ if (trylock == -1) { \
+ status = NIS_TRYAGAIN; \
+ } else { \
+ status = NIS_SYSTEMERROR; \
+ } \
+ return (res); \
+ } \
+ }
+
+#define ULOCK_TABLE(name, rw, msg) \
+ (void) __nis_ulock_db_table(name, rw, 0, msg)
+
+#define ULOCK_TABLE_REMCOND(name, rw, condition, msg) \
+ (void) __nis_ulock_db_table(name, rw, (condition), msg)
+
+static const char *rootname = ".";
+
+static char *
+__dirname(nis_name name) {
+
+ if (name == 0 || (name = strchr(name, '.')) == 0 ||
+ *(++name) == '\0') {
+ name = (char *)rootname;
+ }
+
+ return (name);
+}
+
+void *
+__nis_lock_db_directory(nis_name name, int readwrite, int *trylock, char *msg) {
+
+ dirlock_t *dirlock;
+ char namebuf[NIS_MAXNAMELEN * 2];
+
+ name = __dirname(name);
+
+ if ((dirlock = (dirlock_t *)__nis_find_item_mt(name, &dirlocks,
+ readwrite, trylock)) == 0) {
+ if (trylock != 0 && *trylock == -1) {
+ MT_LOG(1, (LOG_WARNING,
+ "%d: db directory %s lock deferred \"%s\"",
+ pthread_self(), MT_LOCK_TYPE(readwrite), name));
+ return (0);
+ }
+ /* Doesn't exist; create one */
+ if ((dirlock = malloc(sizeof (*dirlock))) == 0) {
+ return (0);
+ }
+ if ((dirlock->item.name = strdup(name)) == 0) {
+ free(dirlock);
+ return (0);
+ }
+ __nis_init_hash_table(&dirlock->tablelocks, 0);
+ dirlock->defer = d_none;
+ if (__nis_insert_item_mt((NIS_HASH_ITEM *)dirlock, &dirlocks,
+ readwrite) == 0) {
+ free(dirlock->item.name);
+ free(dirlock);
+ /*
+ * nis_insert_item() => 0 could mean that the item
+ * already exists (i.e., someone else created it),
+ * so we'll try to find it one last time.
+ */
+ if ((dirlock = (dirlock_t *)__nis_find_item_mt(name,
+ &dirlocks, readwrite, trylock)) == 0) {
+ return (0);
+ }
+ }
+ MT_LOG(1, (LOG_NOTICE, "db directory lock created \"%s\"",
+ name));
+ }
+
+ MT_LOG(1, (LOG_NOTICE, "%d: acquired db directory %s lock \"%s\"",
+ pthread_self(), MT_LOCK_TYPE(readwrite), name));
+ return (dirlock);
+}
+
+int
+__nis_ulock_db_directory(nis_name name, int readwrite, int remove, char *msg) {
+
+ dirlock_t *dirlock;
+ char namebuf[NIS_MAXNAMELEN * 2];
+
+ name = __dirname(name);
+
+ if ((dirlock = (dirlock_t *)__nis_find_item_mt(name, &dirlocks,
+ readwrite, 0)) != 0) {
+ /* Only remove if table list is empty and not in defer mode */
+ if (remove && dirlock->tablelocks.first == 0 &&
+ dirlock->defer == d_none) {
+ NIS_HASH_ITEM *item;
+ item = __nis_remove_item_mt(name, &dirlocks);
+ if (item != 0) {
+ free(item->name);
+ free(item);
+ }
+ MT_LOG(1, (LOG_NOTICE,
+ "%d: db dir %s lock removed \"%s\"",
+ pthread_self(), MT_LOCK_TYPE(readwrite), name));
+ } else {
+ /*
+ * The __nis_lock_db_directory() call incremented the
+ * refcount on the item, and we've just incremented it
+ * again via __nis_find_item_mt(). Thus, we double the
+ * refcount to be released for __nis_release_item.
+ */
+ if (__nis_release_item(dirlock, &dirlocks,
+ 2*readwrite)) {
+ MT_LOG(1, (LOG_NOTICE,
+ "%d: db dir %s lock released \"%s\"",
+ pthread_self(), MT_LOCK_TYPE(readwrite), name));
+ } else {
+ MT_LOG(1, (LOG_WARNING,
+ "%d: error releasing dirlock item for \"%s\"",
+ pthread_self(), name));
+ }
+ }
+
+ return (1);
+ }
+
+ MT_LOG(1, (LOG_ERR, "%d: db dir %s lock not found (unlock) \"%s\"",
+ pthread_self(), MT_LOCK_TYPE(readwrite), name));
+
+ return (0);
+}
+
+/*
+ * Note that the 'trylock' semantics only apply to the directory in which
+ * the table resides, not to the table itself.
+ */
+int
+__nis_lock_db_table(nis_name name, int readwrite, int *trylock, char *msg) {
+
+ dirlock_t *dirlock;
+ tablelock_t *tablelock;
+ char *tablename = name;
+
+ if ((dirlock = (dirlock_t *)__nis_lock_db_directory(name, 1, trylock,
+ name)) == 0)
+ return (0);
+
+ if ((tablelock = (tablelock_t *)__nis_find_item_mt(tablename,
+ &dirlock->tablelocks, readwrite, 0)) == 0) {
+ if ((tablelock = malloc(sizeof (*tablelock))) == 0) {
+ (void) __nis_ulock_db_directory(name, 1, 0, name);
+ return (0);
+ }
+ if ((tablelock->item.name = strdup(tablename)) == 0) {
+ free(tablelock);
+ (void) __nis_ulock_db_directory(name, 1, 0, name);
+ return (0);
+ }
+ tablelock->defer = d_none;
+ if (__nis_insert_item_mt((NIS_HASH_ITEM *)tablelock,
+ &dirlock->tablelocks, readwrite) == 0) {
+ free(tablelock->item.name);
+ free(tablelock);
+ /* Some other thread may have created it */
+ if ((tablelock = (tablelock_t *)__nis_find_item_mt(
+ tablename, &dirlock->tablelocks, readwrite, 0)) == 0) {
+ (void) __nis_ulock_db_directory(name, 1, 0,
+ name);
+ return (0);
+ }
+ }
+ /* If directory is in defer mode, make the table so as well */
+ if (dirlock->defer == d_defer && tablelock->defer == d_none) {
+ db_status stat = __db_defer(tablelock->item.name);
+ if (stat == DB_SUCCESS || stat == DB_NOTFOUND) {
+ tablelock->defer = d_defer;
+ } else {
+#ifdef NIS_MT_DEBUG
+ abort();
+#endif /* NIS_MT_DEBUG */
+ MT_LOG(1, (LOG_WARNING,
+ "%d: defer DB error %d for \"%s\"",
+ pthread_self(), stat,
+ tablelock->item.name));
+ }
+ }
+ MT_LOG(1, (LOG_NOTICE, "%d: db table lock created \"%s\"",
+ pthread_self(), name));
+ }
+ /*
+ * We've got the item, so we're done. Hang on to the read-only
+ * directory lock so that an exclusive access to the directory
+ * (probably a checkpoint) must wait for us to be done with the table.
+ */
+ MT_LOG(1, (LOG_NOTICE, "%d: db table %s lock acquired \"%s\"",
+ pthread_self(), MT_LOCK_TYPE(readwrite), name));
+ return (1);
+}
+
+int
+__nis_ulock_db_table(nis_name name, int readwrite, int remove, char *msg) {
+
+ dirlock_t *dirlock;
+ tablelock_t *tablelock;
+ char *tablename = name;
+
+ /*
+ * At this point, we should be holding a read-only lock on the
+ * directory entry, so we just obtain it without any additional
+ * locking.
+ */
+ dirlock = (dirlock_t *)__nis_lock_db_directory(name, 0, 0, name);
+ if (dirlock == 0) {
+ return (0);
+ }
+
+ if ((tablelock = (tablelock_t *)__nis_find_item_mt(tablename,
+ &dirlock->tablelocks, readwrite, 0)) != 0) {
+ if (remove && readwrite == WR && tablelock->defer == d_none) {
+ NIS_HASH_ITEM *item;
+ item = __nis_remove_item_mt(tablename,
+ &dirlock->tablelocks);
+ if (item != 0) {
+ free(item->name);
+ free(item);
+ }
+ MT_LOG(1, (LOG_NOTICE,
+ "%d: removed db table lock \"%s\"",
+ pthread_self(), tablename));
+ } else {
+ /*
+ * Normally, if the directory is in defer mode, the
+ * table would have been put in defer mode during the
+ * table lock. However, if the operation that acquired
+ * the lock also created the table, it couldn't be
+ * deferred back then, so we do it now instead.
+ */
+ if (dirlock->defer == d_defer &&
+ tablelock->defer == d_none) {
+ db_status s;
+ s = __db_defer(tablelock->item.name);
+ if (s == DB_SUCCESS) {
+ tablelock->defer = d_defer;
+ } else if (s != DB_NOTFOUND) {
+ MT_LOG(1, (LOG_WARNING,
+ "%d: defer DB error %d for \"%s\"",
+ pthread_self(), s,
+ tablelock->item.name));
+ }
+ }
+ (void) __nis_release_item((NIS_HASH_ITEM *)tablelock,
+ &dirlock->tablelocks, 2*readwrite);
+ MT_LOG(1, (LOG_NOTICE,
+ "%d: released db %s table lock \"%s\"",
+ pthread_self(), MT_LOCK_TYPE(readwrite), tablename));
+ }
+ }
+
+ MT_LOG(dirlock == 0, (LOG_ERR,
+ "%d: could not find %s lock (unlock) \"%s\"",
+ pthread_self(), MT_LOCK_TYPE(readwrite), tablename));
+
+ return (__nis_ulock_db_directory(name, 1, 0, name));
+}
+
+/*
+ * Set the directory (and any known tables) to be deferred.
+ * If there's a failure attempting to defer a table, we try
+ * to rollback those deferred so far, and return the failure.
+ */
+db_status
+db_defer(nis_name name) {
+
+ dirlock_t *dirlock;
+ db_status stat = DB_SUCCESS;
+
+ if ((dirlock = __nis_lock_db_directory(name, WR, NULL, "db_defer")) ==
+ 0)
+ return (DB_NOTFOUND);
+
+ /*
+ * If the directory isn't already in defer mode, make it so.
+ */
+ if (dirlock->defer == d_none) {
+ tablelock_t *t;
+ db_status s;
+
+ dirlock->defer = d_defer;
+
+ /* Traverse table locks and set defer mode */
+ for (t = (tablelock_t *)dirlock->tablelocks.first;
+ t != 0 && stat == DB_SUCCESS;
+ t = (tablelock_t *)t->item.nxt_item) {
+ if (t->defer == d_none) {
+ s = __db_defer(t->item.name);
+ /*
+ * DB_NOTFOUND is OK; the tablelocks list is
+ * out of date. Don't mark the table deferred,
+ * though, because it doesn't exist in the DB.
+ */
+ if (s == DB_SUCCESS) {
+ t->defer = d_defer;
+ } else if (s != DB_NOTFOUND) {
+ /* Remember the first failure */
+ if (stat == DB_SUCCESS)
+ stat = s;
+#ifdef NIS_MT_DEBUG
+ abort();
+#endif /* NIS_MT_DEBUG */
+ MT_LOG(1, (LOG_WARNING,
+ "%d: defer DB error %d for table \"%s\"",
+ pthread_self(), s,
+ t->item.name));
+ }
+ }
+ }
+
+ if (stat != DB_SUCCESS) {
+ /* Try to rollback deferred tables */
+ for (t = (tablelock_t *)dirlock->tablelocks.first;
+ t != 0;
+ t = (tablelock_t *)t->item.nxt_item) {
+ if (t->defer == d_defer) {
+ s = __db_rollback(t->item.name);
+ if (s == DB_SUCCESS) {
+ t->defer = d_none;
+ } else {
+#ifdef NIS_MT_DEBUG
+ abort();
+#endif /* NIS_MT_DEBUG */
+ MT_LOG(1, (LOG_WARNING,
+ "%d: rollback DB error %d for table \"%s\"",
+ pthread_self(), s,
+ t->item.name));
+ }
+ }
+ }
+ dirlock->defer = d_none;
+ }
+ }
+
+ if (__nis_ulock_db_directory(name, WR, 0, "db_defer") == 0) {
+ MT_LOG(1, (LOG_WARNING,
+ "%d: db_defer: error unlocking \"%s\"",
+ pthread_self(), name));
+ }
+
+ return (stat);
+}
+
+static db_status
+__db_undefer(nis_name name, __nis_defer_t mode) {
+
+ dirlock_t *dirlock;
+ tablelock_t *t;
+ db_status stat = DB_SUCCESS;
+
+ if (mode != d_commit && mode != d_rollback)
+ return (DB_BADQUERY);
+
+ if ((dirlock = __nis_lock_db_directory(name, WR, 0, "db_undefer")) == 0)
+ return (DB_NOTFOUND);
+
+ /* Commit or rollback deferred table changes */
+ for (t = (tablelock_t *)dirlock->tablelocks.first;
+ t != 0;
+ t = (tablelock_t *)t->item.nxt_item) {
+ db_status s;
+
+ if (t->defer == d_defer) {
+ if (mode == d_commit)
+ s = __db_commit(t->item.name);
+ else
+ s = __db_rollback(t->item.name);
+ /*
+ * DB_NOTFOUND is OK; just means that the tablelocks
+ * list is out-of-date.
+ */
+ if (s == DB_SUCCESS || s == DB_NOTFOUND) {
+ t->defer = d_none;
+ } else {
+ /* Keep first err */
+ if (stat == DB_SUCCESS)
+ stat = s;
+ MT_LOG(1, (LOG_WARNING,
+ "%d: %s DB error %d for table \"%s\"",
+ pthread_self(),
+ (mode == d_commit) ? "commit" : "rollback",
+ s, t->item.name));
+ }
+ }
+ }
+ /*
+ * Even if there were errors un-deferring one or more of the tables,
+ * we still want to set the directory undeferred. Otherwise, we'd
+ * continue to defer any new (meaning a tablelocks entry added)
+ * tables.
+ */
+ dirlock->defer = d_none;
+
+ if (__nis_ulock_db_directory(name, WR, 0, "db_undefer") == 0) {
+ MT_LOG(1, (LOG_WARNING,
+ "%d: db_undefer: error unlocking \"%s\"",
+ pthread_self(), name));
+ }
+
+ return (stat);
+}
+
+db_status
+db_commit(nis_name name) {
+ return (__db_undefer(name, d_commit));
+}
+
+db_status
+db_rollback(nis_name name) {
+ return (__db_undefer(name, d_rollback));
+}
+
+/*
+ * Free resources associated with a db_result structure
+ */
+static void
+free_db_result(db_result *dr)
+{
+ int i;
+
+ if (dr == 0)
+ return;
+
+ if (dr->status != DB_SUCCESS) {
+ /* Can't have valid objects */
+ free(dr);
+ return;
+ }
+
+ for (i = 0; i < dr->objects.objects_len; i++)
+ free_entry(dr->objects.objects_val[i]);
+ free(dr->objects.objects_val);
+ free(dr);
+}
+
+static void
+free_nis_db_result(nis_db_result *res)
+{
+ /* We do not free obj here because it is cached in table_cache */
+ XFREE(res);
+}
+
+static nis_error
+map_db_status_to_nis_status(db_status dstatus)
+{
+ switch (dstatus) {
+ case DB_SUCCESS:
+ return (NIS_SUCCESS);
+ case DB_NOTFOUND:
+ return (NIS_NOTFOUND);
+ case DB_BADTABLE:
+ return (NIS_NOSUCHTABLE);
+ case DB_MEMORY_LIMIT:
+ return (NIS_NOMEMORY);
+ case DB_STORAGE_LIMIT:
+ return (NIS_NOFILESPACE);
+ case DB_NOTUNIQUE:
+ return (NIS_NAMEEXISTS);
+ case DB_BADQUERY:
+ return (NIS_BADREQUEST);
+ case DB_BADOBJECT:
+ return (NIS_BADOBJECT);
+ case DB_INTERNAL_ERROR:
+ default:
+ return (NIS_SYSTEMERROR);
+ }
+}
+
+/*
+ * This function converts the internal format entries of the DB into
+ * a list of nis_objects that the server understands. The object returned may
+ * be destroyed with nis_destroy_object();
+ *
+ * Notes : When listing directories the list function expects to see entry
+ * objects and this function will mangle regular objects into entry
+ * objects. The entry has one column which contains the binary
+ * equivalent of what the type would have been if it hadn't been
+ * mangled.
+ *
+ * When dumping directories we need the objects intact so we set mangle
+ * to false and return the real objects.
+ */
+static obj_list *
+cvt2object(
+ nis_name tablename, /* table which has these entries */
+ entry_obj *ep[],
+ uint_t num,
+ int *got,
+ int mangle) /* Make non-entry objects into psuedo entries */
+{
+ register obj_list *oa; /* object array */
+ nis_object *tmp; /* Temporary object(s) */
+ XDR xdrs; /* temporary xdr stream */
+ int status, /* XDR op status */
+ curr_obj, /* Current Object */
+ i, j, mc;
+ entry_obj *eo; /* tmp, makes life easier */
+ entry_col *ec;
+ unsigned long etype; /* for fake entries */
+ struct table_item *te; /* Table cache entry */
+ struct nis_object *tobj; /* Table nis_object */
+
+ *got = 0; /* Number of objects decoded */
+ te = __nis_find_item_mt(tablename, table_cache, RO, 0);
+ if (te == NULL) {
+ /* Do a db_lookup() so that cache is populated */
+ nis_db_result *dbres;
+
+ if (((dbres = db_lookup(tablename)) == NULL) ||
+ (dbres->status != NIS_SUCCESS))
+ tobj = NULL;
+ else
+ tobj = dbres->obj;
+ /* dbres is freed automatically during cleanup */
+ } else {
+ tobj = te->ibobj;
+ }
+ oa = (obj_list *)XCALLOC(num, sizeof (obj_list));
+ if (oa == NULL) {
+ if (te != 0)
+ __nis_release_item(te, table_cache, RO);
+ return (NULL);
+ }
+
+ curr_obj = 0;
+ for (i = 0; i < num; i++) {
+ if (! ep[i]) {
+ syslog(LOG_ERR,
+ "cvt2object: NULL Object in database, ignored.");
+ continue;
+ }
+ ec = ep[i]->en_cols.en_cols_val;
+ mc = ep[i]->en_cols.en_cols_len;
+ /*
+ * Set up a memory stream pointing at the first column value
+ * which contains the XDR encoded NIS+ object. The second
+ * column contains the name of the NIS+ object.
+ */
+ xdrmem_create(&xdrs, ec->ENVAL, ec->ENLEN, XDR_DECODE);
+ tmp = (nis_object *)XCALLOC(1, sizeof (nis_object));
+ if (tmp == NULL) {
+ /* I'll return with the current list of objects */
+ if (te != 0)
+ __nis_release_item(te, table_cache, RO);
+ return (oa);
+ }
+ /*
+ * Decode it into the object structure. If some fields
+ * are NULL, fill in appropriate values from the table
+ * nis_object structure.
+ *
+ * If the entry object has type "IN_DIRECTORY" then it
+ * is a NIS+ directory we are listing else it is
+ * an ENTRY Object. For ENTRY objects, we call our
+ * special xdr_nis_fetus_object() which knows how to
+ * reconstruct the entry object from the given info.
+ * XXX: _any_ other value we are hosed. If it is 0 or a
+ * NULL string, it denotes that it is an entry object.
+ */
+ if (((ep[i]->en_type == 0) || (ep[i]->en_type[0] == 0)) && tobj)
+ status = xdr_nis_fetus_object(&xdrs, tmp, tobj);
+ else
+ status = xdr_nis_object(&xdrs, tmp);
+ /*
+ * POLICY : What to do about undecodeable objects ?
+ * ANSWER : Leave it blank and continue. (soft failure)
+ */
+ if (! status) {
+ syslog(LOG_ERR,
+ "cvt2object: Corrupted object ('%s') in database %s",
+ ec[1].ENVAL, tablename);
+ XFREE(tmp);
+ continue;
+ }
+
+ /*
+ * If the entry object has type 0 or "IN_TABLE" then it
+ * is an entry object. If it has type "IN_DIRECTORY" then it
+ * is a NIS+ directory that we are listing.
+ * XXX: _any_ other value we are hosed.
+ */
+ if ((ep[i]->en_type == 0) || (ep[i]->en_type[0] == 0) ||
+ strcmp(ep[i]->en_type, "IN_TABLE") == 0) {
+ if (__type_of(tmp) != NIS_ENTRY_OBJ) {
+ syslog(LOG_ERR,
+ "cvt2object: Corrupt database, entry expected for %s", ec[1].ENVAL);
+ xdr_free(xdr_nis_object, (char *)tmp);
+ XFREE(tmp);
+ continue;
+ }
+ /*
+ * Set the column fields appropriately. Copy all the
+ * col entry pointers to the new entry object. We are
+ * mucking around with the list returned by
+ * db_list_entries - UNCLEAN, UNCLEAN!
+ */
+ eo = &(tmp->EN_data);
+ eo->en_cols.en_cols_len = mc - 1;
+ eo->en_cols.en_cols_val = XMALLOC((mc - 1) *
+ (sizeof (entry_col)));
+ if (eo->en_cols.en_cols_val == NULL) {
+ xdr_free(xdr_nis_object, (char *)tmp);
+ XFREE(tmp);
+ if (te != 0)
+ __nis_release_item(te, table_cache,
+ RO);
+ return (oa);
+ }
+ for (j = 1; j < mc; j++) {
+ eo->en_cols.en_cols_val[j-1] =
+ ep[i]->en_cols.en_cols_val[j];
+ }
+ /* We set len to 1, so that other cols are not freed */
+ ep[i]->en_cols.en_cols_len = 1;
+ } else if (mangle) {
+ /* Convert this dir object into a "fake" entry object */
+ etype = htonl(__type_of(tmp)); /* save the old type */
+ /* first free the object specific data */
+ xdr_free(xdr_objdata, (char *)&tmp->zo_data);
+ memset(&tmp->zo_data, 0, sizeof (objdata));
+ /* Now fake a entry object */
+ __type_of(tmp) = NIS_ENTRY_OBJ;
+ eo = &(tmp->EN_data);
+ eo->en_type = strdup("NIS object"); /* special type */
+ if (eo->en_type == NULL) {
+ xdr_free(xdr_nis_object, (char *)tmp);
+ if (te != 0)
+ __nis_release_item(te, table_cache,
+ RO);
+ return (oa);
+ }
+ ec = (entry_col *) XMALLOC(sizeof (entry_col));
+ if (ec == NULL) {
+ xdr_free(xdr_nis_object, (char *)tmp);
+ if (te != 0)
+ __nis_release_item(te, table_cache,
+ RO);
+ return (oa);
+ }
+ eo->en_cols.en_cols_len = 1;
+ eo->en_cols.en_cols_val = ec;
+ ec[0].ec_flags = EN_BINARY + EN_XDR;
+ ec[0].ENVAL = XMALLOC(4);
+ if (ec[0].ENVAL == NULL) {
+ xdr_free(xdr_nis_object, (char *)tmp);
+ if (te != 0)
+ __nis_release_item(te, table_cache,
+ RO);
+ return (oa);
+ }
+ ec[0].ENLEN = 4;
+ memcpy(ec[0].ENVAL, (char *)&etype, 4);
+ }
+ oa[curr_obj++].o = tmp;
+ (*got)++; /* Add one to the total */
+ }
+
+ if (te != 0)
+ __nis_release_item(te, table_cache, RO);
+ return (oa);
+}
+
+/*
+ * Release hash item. Intended for use by the cleanup code, executed
+ * when the thread is about to return to the RPC dispatch.
+ */
+void
+tableCacheReleaseRO(void *ptr) {
+ struct table_item *te = ptr;
+
+ if (te != 0)
+ (void) __nis_release_item(te, table_cache, RO);
+}
+
+/*
+ * Destroy a table cache item.
+ */
+void
+destroyTableCacheItem(void *item) {
+ struct table_item *ti = item;
+
+ if (ti != 0) {
+ free(ti->ti_item.name);
+ nis_destroy_object(ti->ibobj);
+ free(ti);
+ }
+}
+
+/*
+ * Return value if allocation of nis_db_result return value fails. Must
+ * not be freed, of course, but that's OK since callers already expect
+ * that to happen through the cleanup code.
+ */
+static nis_db_result __no_mem_nis_db_result = {NIS_NOMEMORY, 0, 0};
+
+/*
+ * The following routines implement the database operations on the namespace.
+ * The database only understands tables, so it is up these routines to
+ * "fake out" the database and to build a table that defines the namespace
+ * that we are serving. The format of this table is fixed. It consists of
+ * a table with two columns, the first being the object (XDR encoded)
+ * and the second being the "name" of the object.
+ *
+ * Note : All of these routines assume they are passed "full" NIS+ names
+ * Note : The fake entry structure is the way it is to be compatible with
+ * the cvt2object() function above.
+ *
+ * Lookup NIS+ objects in the namespace that are stored as entries in a
+ * table by the same name as the directory being served. These entries
+ * have two columns, column 0 is the object data and column 1 is the
+ * object name.
+ */
+nis_db_result *
+db_lookup_deferred(nis_name name, bool_t useDeferred)
+{
+ int status;
+ nis_attr attr;
+ db_result *dbres;
+ register entry_col *ec;
+ XDR xdrs;
+ nis_db_result *res;
+ char *table; /* The table path name */
+ char tblbuf[NIS_MAXNAMELEN * 2]; /* table path buf */
+ struct table_item *te; /* Table cache entry */
+ int triedLDAP;
+ char *myself = "db_lookup_deferred";
+
+ if (verbose)
+ syslog(LOG_INFO, "db_lookup: Looking for %s%s", name,
+ useDeferred ? " in deferred DB" : "");
+
+ if ((res = (nis_db_result *) XCALLOC(1, sizeof (nis_db_result))) == 0)
+ return (&__no_mem_nis_db_result);
+ add_cleanup(free_nis_db_result, (void *)(res), "db_lookup result");
+
+ if (!table_cache) {
+ table_cache = (NIS_HASH_TABLE *)
+ XCALLOC(1, sizeof (NIS_HASH_TABLE));
+ if (!table_cache) {
+ syslog(LOG_ERR, "db_lookup: out of memory");
+ res->status = NIS_NOMEMORY;
+ return (res);
+ }
+ __nis_init_hash_table(table_cache, destroyTableCacheItem);
+ }
+
+ LOCK_TABLE(name, RO, res, res->status, name);
+
+ /*
+ * Now we check the cache to see if we have it cached locally.
+ */
+ te = __nis_find_item_mt(name, table_cache, RO, 0);
+ if (te) {
+ if (verbose)
+ syslog(LOG_INFO, "db_lookup: table cache hit");
+ res->status = NIS_SUCCESS;
+ /* Defer release of item */
+ add_cleanup(tableCacheReleaseRO, te, "table obj from cache");
+ ULOCK_TABLE(name, RO, name);
+ res->obj = te->ibobj;
+ return (res);
+ }
+
+ table = internal_table_name(nis_domain_of(name), tblbuf);
+ /* This happens when we're looking for directory objects. */
+ if (! table) {
+ res->status = NIS_NOSUCHTABLE;
+ ULOCK_TABLE(name, RO, name);
+ return (res);
+ }
+ attr.zattr_ndx = "name";
+ attr.ZAVAL = nis_leaf_of(name);
+ attr.ZALEN = strlen(attr.ZAVAL)+1;
+
+ if (verbose)
+ syslog(LOG_INFO, "db_lookup: looking up %s in table %s",
+ attr.ZAVAL, table);
+ __start_clock(1);
+
+ {
+ nisdb_tsd_t fallback, *tsd = __nisdb_get_tsd();
+ __nisdb_retry_t refreshRetry = ldapDBTableMapping.
+ refreshErrorRetry;
+ __nisdb_retry_t retrieveRetry = ldapDBTableMapping.
+ retrieveErrorRetry;
+
+ if (tsd == 0) {
+ logmsg(MSG_NOMEM, LOG_ERR,
+ "%s: No memory for TSD; unable to get full status for DB operation",
+ myself);
+ tsd = &fallback;
+ }
+
+ triedLDAP = 0;
+ while (1) {
+ /* Establish default status (success) */
+ tsd->nisPlusStat = NIS_SUCCESS;
+ tsd->ldapStat = LDAP_SUCCESS;
+ dbres = __db_list_entries(table, 1, &attr,
+ useDeferred);
+ if (dbres == 0) {
+ res->status = NIS_NOMEMORY;
+ break;
+ } else {
+ int rd;
+
+ res->status =
+ map_db_status_to_nis_status(dbres->status);
+ if (res->status == NIS_SUCCESS &&
+ tsd->ldapStat == LDAP_SUCCESS &&
+ tsd->nisPlusStat == NIS_SUCCESS)
+ break;
+ /* Unlock while we (possibly) sleep */
+ ULOCK_TABLE(name, RO, name);
+ rd = retrieveDone(&refreshRetry,
+ &retrieveRetry,
+ tsd->nisPlusStat,
+ tsd->ldapStat, &res->status);
+ LOCK_TABLE(name, RO, res, res->status, name);
+ if (rd) {
+ /*
+ * If we failed because there was
+ * no such entry in the directory,
+ * try to get the entry from LDAP.
+ * If successful, look in the dir
+ * again.
+ */
+ if (!triedLDAP && res->status ==
+ NIS_NOTFOUND) {
+ db_status dstat;
+ int lstat;
+ char *intName;
+
+ triedLDAP = 1;
+ intName = internalTableName(
+ name);
+ if (intName == 0) {
+ res->status =
+ NIS_NOMEMORY;
+ break;
+ }
+ refreshRetry =
+ ldapDBTableMapping.refreshErrorRetry;
+ retrieveRetry =
+ ldapDBTableMapping.retrieveErrorRetry;
+ do {
+ tsd->nisPlusStat =
+ NIS_SUCCESS;
+ tsd->ldapStat =
+ LDAP_SUCCESS;
+ dstat =
+ dbCreateFromLDAP(
+ intName, &lstat);
+
+ res->status =
+ map_db_status_to_nis_status(dstat);
+
+ rd = retrieveDone(
+ &refreshRetry,
+ &retrieveRetry,
+ NIS_SUCCESS,
+ lstat,
+ &res->status);
+ } while (!rd);
+ free(intName);
+ if (dstat == DB_SUCCESS)
+ continue;
+ else if (lstat ==
+ LDAP_NO_SUCH_OBJECT) {
+ res->status =
+ NIS_NOTFOUND;
+ logmsg(MSG_NOTIMECHECK,
+ LOG_INFO,
+ "%s: no LDAP data for \"%s\"",
+ name);
+ } else if (lstat !=
+ LDAP_SUCCESS) {
+ logmsg(MSG_NOTIMECHECK,
+ LOG_WARNING,
+ "%s: LDAP error for \"%s\": %s",
+ myself, name,
+ ldap_err2string(lstat));
+ }
+ }
+ break;
+ }
+ }
+ free_db_result(dbres);
+ }
+ res->ticks = __stop_clock(1);
+ }
+
+ if (res->status == NIS_SUCCESS || res->status == NIS_CACHEEXPIRED) {
+ /* ASSERT(dbres->objects.objects_len == 1); */
+
+ /*
+ * convert from XDR format that the DB returns to
+ * the nis_object format
+ */
+ ec = dbres->objects.objects_val[0]->en_cols.en_cols_val;
+
+ xdrmem_create(&xdrs, ec->ENVAL, ec->ENLEN, XDR_DECODE);
+ res->obj = (nis_object *)XCALLOC(1, sizeof (nis_object));
+ if (!(res->obj))
+ res->status = NIS_NOMEMORY;
+ else {
+ status = xdr_nis_object(&xdrs, res->obj);
+ if (! status) {
+ syslog(LOG_ERR, "db_lookup: Corrupt object %s",
+ name);
+ XFREE(res->obj);
+ res->obj = NULL;
+ res->status = NIS_SYSTEMERROR;
+ }
+ }
+ }
+
+ if (verbose && dbres)
+ syslog(LOG_INFO, "db_lookup: exit status is %d", dbres->status);
+
+ if (res->obj) {
+ int doRead;
+ __nis_buffer_t b = {0, 0};
+
+ /*
+ * If the object is read-mapped from LDAP, we don't want
+ * to cache it in the table_cache.
+ */
+ bp2buf("db_lookup_deferred", &b, "%s.%s",
+ res->obj->zo_name, res->obj->zo_domain);
+ if ((__type_of(res->obj) == NIS_TABLE_OBJ) &&
+ useDeferred &&
+ (getObjMapping(b.buf, 0, 1, &doRead, 0) == 0 ||
+ !doRead) &&
+ strstr(name, "org_dir")) {
+ /*
+ * Cache the table objects in the "org_dir"
+ * dir. We want to cache only the "org_dir" tables
+ * instead of caching all zillions of tables.
+ */
+ te = (struct table_item *)XCALLOC(1, sizeof (*te));
+ if (te) {
+ te->ti_item.name = (nis_name) strdup(name);
+ if (te->ti_item.name == NULL) {
+ add_cleanup(nis_destroy_object,
+ (void *)(res->obj),
+ "db_lookup result");
+ free(te);
+ } else {
+ te->ibobj = res->obj;
+ (void) __nis_insert_item_mt(te,
+ table_cache, 0);
+ if (verbose)
+ syslog(LOG_INFO,
+ "Added %s to the table cache",
+ name);
+ }
+ } else {
+ add_cleanup(nis_destroy_object,
+ (void *)(res->obj), "db_lookup result");
+ }
+ } else {
+ add_cleanup(nis_destroy_object,
+ (void *)(res->obj), "db_lookup result");
+ }
+ if (b.buf != 0)
+ free(b.buf);
+ }
+
+ free_db_result(dbres);
+ ULOCK_TABLE(name, RO, name);
+ return (res);
+}
+
+nis_db_result *
+db_lookup(nis_name name) {
+ return (db_lookup_deferred(name, TRUE));
+}
+
+/*
+ * __db_add()
+ *
+ * The internal version of db_add, this one doesn't add an entry into
+ * the log. This function converts the nis_object into a DB style object
+ * and then adds it to the database.
+ */
+nis_error
+__db_add(
+ nis_name name,
+ nis_object *obj,
+ int modop) /* Modify operation flag */
+{
+ nis_attr attr;
+ int i;
+ db_result *dbres;
+ entry_col ecols[2];
+ entry_obj entry;
+ char *table;
+ uchar_t *buf;
+ nis_error res;
+ XDR xdrs;
+ char tblbuf[NIS_MAXNAMELEN * 2];
+ char *myself = "__db_add";
+
+ if (verbose)
+ syslog(LOG_INFO, "__db_add: attempting to %s %s",
+ modop? "modify" : "add", name);
+
+ LOCK_TABLE(name, WR, res, res, name);
+
+ if ((__type_of(obj) == NIS_TABLE_OBJ) && (!modop)) {
+ if ((res = __create_table(name, obj)) != NIS_SUCCESS) {
+ ULOCK_TABLE(name, WR, name);
+ syslog((res != NIS_NAMEEXISTS) ? LOG_ERR : LOG_INFO,
+ "__db_add: Unable to create database for NIS+ table %s: %s.",
+ name, nis_sperrno(res));
+ return (res);
+ }
+ }
+
+ table = internal_table_name(nis_domain_of(name), tblbuf);
+ if (! table) {
+ ULOCK_TABLE(name, WR, name);
+ return (NIS_BADNAME);
+ }
+
+ i = xdr_sizeof(xdr_nis_object, obj);
+ buf = __get_xdr_buf(i);
+ xdrmem_create(&xdrs, (char *)buf, i, XDR_ENCODE);
+ i = xdr_nis_object(&xdrs, obj);
+ if (! i) {
+ ULOCK_TABLE(name, WR, name);
+ syslog(LOG_ERR, "__db_add: cannot encode object %s", name);
+ return (NIS_SYSTEMERROR);
+ }
+
+ /* Now we cons up an entry for this object in the name space */
+ entry.en_type = "IN_DIRECTORY";
+ entry.en_cols.en_cols_len = 2;
+ entry.en_cols.en_cols_val = ecols;
+ ecols[0].ec_flags = EN_XDR + EN_MODIFIED;
+ ecols[0].ENVAL = (char *)buf;
+ ecols[0].ENLEN = xdr_getpos(&xdrs);
+ ecols[1].ec_flags = EN_MODIFIED;
+ ecols[1].ENVAL = nis_leaf_of(name);
+ ecols[1].ENLEN = strlen(ecols[1].ENVAL)+1;
+ attr.zattr_ndx = "name";
+ attr.ZAVAL = ecols[1].ENVAL;
+ attr.ZALEN = ecols[1].ENLEN;
+
+ {
+ nisdb_tsd_t fallback, *tsd = __nisdb_get_tsd();
+ __nisdb_retry_t storeRetry = ldapDBTableMapping.
+ storeErrorRetry;
+
+ if (tsd == 0) {
+ logmsg(MSG_NOMEM, LOG_ERR,
+ "%s: No memory for TSD; unable to get full status for DB operation",
+ myself);
+ tsd = &fallback;
+ }
+
+ while (1) {
+ /* Establish default status (success) */
+ tsd->nisPlusStat = NIS_SUCCESS;
+ tsd->ldapStat = LDAP_SUCCESS;
+ dbres = db_add_entry(table, 1, &attr, &entry);
+ if (dbres == 0) {
+ res = NIS_NOMEMORY;
+ break;
+ } else {
+ int rd;
+
+ res = map_db_status_to_nis_status(dbres->
+ status);
+ if (res == NIS_SUCCESS &&
+ tsd->ldapStat == LDAP_SUCCESS &&
+ tsd->nisPlusStat == NIS_SUCCESS)
+ break;
+ /* Unlock while we (possibly) sleep */
+ ULOCK_TABLE(name, WR, name);
+ rd = storeDone(&storeRetry, tsd->nisPlusStat,
+ tsd->ldapStat, &res);
+ LOCK_TABLE(name, WR, res, res, name);
+ if (rd)
+ break;
+ }
+ free_db_result(dbres);
+ }
+ }
+
+ switch (res) {
+ case NIS_NOMEMORY:
+ case NIS_NOFILESPACE:
+ case NIS_SYSTEMERROR:
+ case NIS_UNAVAIL:
+ break; /* these are expected */
+ case NIS_SUCCESS:
+ if (modop) {
+ /* Flush various cached objects */
+ switch __type_of(obj) {
+ case NIS_TABLE_OBJ:
+ flush_tablecache(name);
+ break;
+ case NIS_DIRECTORY_OBJ:
+ flush_dircache(name, (directory_obj *)NULL);
+ break;
+ case NIS_GROUP_OBJ:
+ flush_groupcache(name);
+ break;
+ default:
+ break;
+ }
+ }
+ (void) mark_for_sync(table);
+ break;
+ default:
+ syslog(LOG_ERR, "__db_add: unexpected database result %d",
+ dbres->status);
+ break;
+ }
+
+ free_db_result(dbres);
+ if (res == NIS_SUCCESS) {
+ multival_invalidate(obj);
+ }
+ ULOCK_TABLE(name, WR, name);
+ return (res);
+}
+
+/*
+ * db_add()
+ *
+ * External wrapper for the real db_add function. This one creates a
+ * transaction log entry. The internal one skips the log transaction.
+ */
+nis_db_result *
+db_add(
+ nis_name name,
+ nis_object *obj,
+ int mod_op)
+{
+#define res __nis_get_tsd()->db_add_res
+ log_entry le; /* A log entry */
+
+ memset((char *)&res, 0, sizeof (res));
+ memset((char *)&le, 0, sizeof (le));
+ if (verbose) {
+ if (mod_op == 0)
+ syslog(LOG_INFO, "db_add: Adding object %s", name);
+ else
+ syslog(LOG_INFO, "db_add: Modifying object %s", name);
+ }
+
+ LOCK_TABLE(name, WR, &res, res.status, name);
+
+ le.le_time = obj->zo_oid.mtime;
+ if (mod_op)
+ le.le_type = MOD_NAME_NEW;
+ else
+ le.le_type = ADD_NAME;
+ le.le_name = name;
+ le.le_object = *obj;
+ add_update(&le);
+ __start_clock(1);
+ res.status = __db_add(name, obj, mod_op);
+ res.ticks = __stop_clock(1);
+ if (verbose || (res.status != NIS_SUCCESS))
+ syslog(LOG_INFO, "db_add: returning %s",
+ nis_sperrno(res.status));
+ ULOCK_TABLE(name, WR, name);
+ return (&res);
+}
+
+#undef res
+
+nis_error
+__db_remove(
+ nis_name name, /* Name of object to remove */
+ nis_object *obj) /* Its type (for deleting tables) */
+{
+ nis_attr attr;
+ char *table;
+ db_result *dbres;
+ nis_error res;
+ char tblbuf[NIS_MAXNAMELEN * 2];
+ int table_deleted = 0;
+ char *myself = "__db_remove";
+
+ LOCK_TABLE(name, WR, res, res, name);
+ if (__type_of(obj) == NIS_TABLE_OBJ) {
+ table = internal_table_name(name, tblbuf);
+ if (! table) {
+ ULOCK_TABLE(name, WR, name);
+ return (NIS_BADNAME);
+ }
+
+ /* First make sure the table is empty */
+
+ {
+ nisdb_tsd_t fallback, *tsd = __nisdb_get_tsd();
+ __nisdb_retry_t refreshRetry = ldapDBTableMapping.
+ refreshErrorRetry;
+ __nisdb_retry_t retrieveRetry = ldapDBTableMapping.
+ retrieveErrorRetry;
+
+ if (tsd == 0) {
+ logmsg(MSG_NOMEM, LOG_ERR,
+ "%s: No memory for TSD; unable to get full status for DB operation",
+ myself);
+ tsd = &fallback;
+ }
+
+ while (1) {
+ /* Establish default status (success) */
+ tsd->nisPlusStat = NIS_SUCCESS;
+ tsd->ldapStat = LDAP_SUCCESS;
+ dbres = db_first_entry(table, 0, NULL);
+ if (dbres == 0) {
+ break;
+ } else {
+ nis_error err;
+ int rd;
+
+ err = map_db_status_to_nis_status(
+ dbres->status);
+ if (err == NIS_SUCCESS &&
+ tsd->ldapStat == LDAP_SUCCESS &&
+ tsd->nisPlusStat == NIS_SUCCESS)
+ break;
+ ULOCK_TABLE(name, WR, name);
+ rd = retrieveDone(&refreshRetry,
+ &retrieveRetry,
+ tsd->nisPlusStat,
+ tsd->ldapStat, &err);
+ LOCK_TABLE(name, WR, err, err, name);
+ if (rd)
+ break;
+ }
+ free_db_result(dbres);
+ }
+ }
+
+ if (dbres == 0 || dbres->status == DB_MEMORY_LIMIT) {
+ free_db_result(dbres);
+ ULOCK_TABLE(name, WR, name);
+ return (NIS_NOMEMORY);
+ } else if (dbres->status == DB_SUCCESS) {
+ free(dbres->nextinfo.db_next_desc_val); /* cookie */
+ free_db_result(dbres);
+ ULOCK_TABLE(name, WR, name);
+ return (NIS_NOTEMPTY);
+ } else if (dbres->status == DB_NOTFOUND) {
+ if ((res = db_destroy(name)) != NIS_SUCCESS)
+ syslog(LOG_WARNING,
+ "__db_remove: Unable to destroy table %s: %s.",
+ table, nis_sperrno(res));
+ else
+ table_deleted = 1;
+ } else {
+ /*
+ * POLICY : What should we do, remove the object?
+ * or abort() because the database is
+ * inconsistent.
+ * ANSWER : Notify the system operator, and continue
+ * to remove the table object.
+ */
+ syslog(LOG_ERR,
+ "__db_remove: table %s not in dictionary (err=%d)",
+ table, dbres->status);
+ if ((res = db_destroy(name)) != NIS_SUCCESS)
+ syslog(LOG_WARNING,
+ "__db_remove: Unable to destroy table %s: %s.",
+ table, nis_sperrno(res));
+ else
+ table_deleted = 1;
+ }
+ free_db_result(dbres);
+ }
+
+ table = internal_table_name(nis_domain_of(name), tblbuf);
+ if (! table) {
+ ULOCK_TABLE(name, WR, name);
+ return (NIS_BADNAME);
+ }
+
+ attr.zattr_ndx = "name";
+ attr.ZAVAL = nis_leaf_of(name);
+ attr.ZALEN = strlen(attr.ZAVAL)+1;
+
+ {
+ nisdb_tsd_t fallback, *tsd = __nisdb_get_tsd();
+ __nisdb_retry_t storeRetry = ldapDBTableMapping.
+ storeErrorRetry;
+
+ if (tsd == 0) {
+ logmsg(MSG_NOMEM, LOG_ERR,
+ "%s: No memory for TSD; unable to get full status for DB operation",
+ myself);
+ tsd = &fallback;
+ }
+
+ while (1) {
+ /* Establish default status (success) */
+ tsd->nisPlusStat = NIS_SUCCESS;
+ tsd->ldapStat = LDAP_SUCCESS;
+ dbres = db_remove_entry(table, 1, &attr);
+ if (dbres == 0) {
+ res = NIS_NOMEMORY;
+ break;
+ } else {
+ int rd;
+
+ res = map_db_status_to_nis_status(dbres->
+ status);
+ if (res == NIS_SUCCESS &&
+ tsd->ldapStat == LDAP_SUCCESS &&
+ tsd->nisPlusStat == NIS_SUCCESS)
+ break;
+ ULOCK_TABLE(name, WR, name);
+ rd = storeDone(&storeRetry, tsd->nisPlusStat,
+ tsd->ldapStat, &res);
+ LOCK_TABLE(name, WR, res, res, name);
+ if (rd)
+ break;
+ }
+ free_db_result(dbres);
+ }
+ }
+
+ switch (res) {
+ case NIS_NOMEMORY:
+ case NIS_NOFILESPACE:
+ case NIS_SYSTEMERROR:
+ case NIS_UNAVAIL:
+ break; /* these are expected */
+ case NIS_SUCCESS:
+ /* Flush various cached objects */
+ switch __type_of(obj) {
+ case NIS_TABLE_OBJ:
+ flush_tablecache(name);
+ break;
+ case NIS_DIRECTORY_OBJ:
+ flush_dircache(name, (directory_obj *)NULL);
+ break;
+ case NIS_GROUP_OBJ:
+ flush_groupcache(name);
+ break;
+ default:
+ break;
+ }
+ (void) mark_for_sync(table);
+ break;
+ default:
+ syslog(LOG_ERR,
+ "__db_remove: unexpected result from database %d",
+ dbres->status);
+ break;
+ }
+
+ free_db_result(dbres);
+ if (res == NIS_SUCCESS) {
+ multival_invalidate(obj);
+ }
+ ULOCK_TABLE_REMCOND(name, WR, table_deleted && res == NIS_SUCCESS,
+ name);
+ return (res);
+}
+
+nis_db_result *
+db_remove(
+ nis_name name, /* Name of object to remove */
+ nis_object *obj, /* Its type (for deleting tables) */
+ ulong_t xid_time) /* Time of "this" transaction */
+{
+#define res __nis_get_tsd()->db_remove_res
+ log_entry le; /* A log entry */
+
+ memset((char *)&res, 0, sizeof (res));
+ memset((char *)&le, 0, sizeof (le));
+ if (verbose)
+ syslog(LOG_INFO, "db_remove: removing %s from the namespace",
+ name);
+ LOCK_TABLE(name, WR, &res, res.status, name);
+
+ le.le_time = xid_time;
+ le.le_type = REM_NAME;
+ le.le_name = name;
+ le.le_object = *obj;
+ add_update(&le);
+ __start_clock(1);
+ res.status = __db_remove(name, obj);
+ res.ticks = __stop_clock(1);
+ if (verbose || (res.status != NIS_SUCCESS))
+ syslog(LOG_INFO, "db_remove: returning %s",
+ nis_sperrno(res.status));
+ ULOCK_TABLE(name, WR, name);
+ return (&res);
+}
+
+#undef res
+
+static nis_db_list_result __no_memory_db_list_result = {
+ NIS_NOMEMORY,
+ 0, 0, 0
+};
+
+/*
+ * db_list(table, numattrs, attrs)
+ *
+ * function to call the database list function. When called we know that
+ * object is "readable" by the principal.
+ *
+ */
+
+static nis_db_list_result *
+db_list_x(
+ nis_name name, /* Table we are listing */
+ int na, /* Number of attributes */
+ nis_attr *a, /* An array of attributes */
+ ulong_t flags)
+{
+ int i;
+ nis_error err;
+ char *table; /* internal table name */
+ char tblbuf[NIS_MAXNAMELEN * 2]; /* table path buf */
+ db_result *dbres;
+ nis_db_list_result *res;
+ int got;
+ int nm = 0;
+ nis_object *o;
+ nis_object *tobj;
+ nis_db_result *tres;
+ char *myself = "db_list_x";
+
+ res = (nis_db_list_result *)XCALLOC(1, sizeof (nis_db_list_result));
+ if ((flags & FN_NORAGS) == 0) {
+ if (res == 0)
+ return (&__no_memory_db_list_result);
+ add_cleanup(free_db_list, (void *)res, "db_list result");
+ }
+ got = 0;
+ table = internal_table_name(name, tblbuf);
+ if (! table) {
+ res->status = NIS_BADNAME;
+ return (res);
+ }
+
+ if (verbose)
+ syslog(LOG_INFO, "db_list: listing %s", table);
+
+ LOCK_TABLE(name, RO, res, res->status, name);
+
+ __start_clock(1);
+ tres = db_lookup(name);
+ if (tres == NULL) {
+ res->ticks = __stop_clock(1);
+ ULOCK_TABLE(name, RO, name);
+ res->status = NIS_NOMEMORY;
+ res->numo = 0;
+ res->objs = NULL;
+ return (res);
+ }
+ if (tres->status == NIS_SUCCESS) {
+ tobj = tres->obj;
+ } else if (tres->status == NIS_NOSUCHTABLE) {
+ /* this happens on top directory in server's database */
+ tobj = NULL;
+ } else {
+ res->ticks = __stop_clock(1);
+ ULOCK_TABLE(name, RO, name);
+ res->status = tres->status;
+ res->numo = 0;
+ res->objs = NULL;
+ return (res);
+ }
+
+ /* check for multival searches */
+ if (tobj) {
+ err = multival_attr(tobj, a, &na, &nm);
+ if (err != NIS_SUCCESS) {
+ res->ticks = __stop_clock(1);
+ ULOCK_TABLE(name, RO, name);
+ res->status = err;
+ return (res);
+ }
+ }
+
+ /*
+ * If we have regular attributes or if we have no attributes
+ * at all (na == 0 && nm == 0), search the normal way.
+ */
+ if (na || nm == 0) {
+ {
+ nisdb_tsd_t fallback, *tsd = __nisdb_get_tsd();
+ __nisdb_retry_t refreshRetry = ldapDBTableMapping.
+ refreshErrorRetry;
+ __nisdb_retry_t retrieveRetry = ldapDBTableMapping.
+ retrieveErrorRetry;
+
+ if (tsd == 0) {
+ logmsg(MSG_NOMEM, LOG_ERR,
+ "%s: No memory for TSD; unable to get full status for DB operation",
+ myself);
+ tsd = &fallback;
+ }
+
+ while (1) {
+ /* Establish default status (success) */
+ tsd->nisPlusStat = NIS_SUCCESS;
+ tsd->ldapStat = LDAP_SUCCESS;
+ dbres = db_list_entries(table, na, a);
+ if (dbres == 0) {
+ res->status = NIS_NOMEMORY;
+ break;
+ } else {
+ int rd;
+
+ res->status =
+ map_db_status_to_nis_status(dbres->status);
+ if (res->status == NIS_SUCCESS &&
+ tsd->ldapStat ==
+ LDAP_SUCCESS &&
+ tsd->nisPlusStat ==
+ NIS_SUCCESS)
+ break;
+ ULOCK_TABLE(name, RO, name);
+ rd = retrieveDone(&refreshRetry,
+ &retrieveRetry,
+ tsd->nisPlusStat,
+ tsd->ldapStat, &res->status);
+ LOCK_TABLE(name, RO, res, res->status,
+ name);
+ if (rd)
+ break;
+ }
+ free_db_result(dbres);
+ }
+ }
+
+ res->ticks = __stop_clock(1);
+ /* Process the entries into "objects" */
+ switch (res->status) {
+ case NIS_NOMEMORY:
+ break;
+ case NIS_SUCCESS:
+ case NIS_CACHEEXPIRED:
+ /* Convert from database format to NIS+ object format */
+ res->objs = cvt2object(name, dbres->objects.objects_val,
+ dbres->objects.objects_len, &got, 1);
+ if (got > 0) {
+ res->numo = got;
+ res->status = NIS_SUCCESS;
+ } else {
+ res->numo = 0;
+ res->status = NIS_NOTFOUND;
+ res->objs = NULL;
+ }
+ break;
+ case NIS_NOTFOUND:
+ case NIS_TRYAGAIN:
+ case NIS_UNAVAIL:
+ case NIS_NOSUCHNAME:
+ res->numo = 0;
+ res->objs = NULL;
+ break;
+ default:
+ strcpy(tblbuf, "[");
+ for (i = 0; i < na; i++) {
+ if (i != 0)
+ strcat(tblbuf, ",");
+ strcat(tblbuf, a[i].zattr_ndx);
+ strcat(tblbuf, "=");
+ strncat(tblbuf,
+ a[i].zattr_val.zattr_val_val,
+ a[i].zattr_val.zattr_val_len);
+ }
+ strcat(tblbuf, "],");
+ strcat(tblbuf, name);
+ syslog(LOG_ERR,
+ "Database search failed on %s, status = %d",
+ tblbuf, dbres->status);
+ break;
+ }
+ if (verbose)
+ syslog(LOG_INFO,
+ "db_list: returning status = %d, entries = %d",
+ res->status, got);
+ free_db_result(dbres);
+ } else {
+ res->ticks = __stop_clock(1);
+ }
+
+ /*
+ * If we have multival attributes and regular attributes,
+ * filter out the objects gotten above that don't match
+ * on the multival columns. If there were no regular
+ * attributes, then list based only on the multival columns.
+ */
+ if (nm) {
+ if (na) {
+ for (i = 0; i < res->numo; i++) {
+ o = res->objs[i].o;
+ if (multival_filter(tobj, nm, a + na, o)) {
+ nis_destroy_object(o);
+ res->objs[i].o = NULL;
+ }
+ }
+ } else {
+ /* na = 0 */
+ multival_list(tobj, nm, a, res);
+ }
+ }
+
+ ULOCK_TABLE(name, RO, name);
+ return (res);
+}
+
+nis_db_list_result *
+db_list(
+ nis_name name, /* Table we are listing */
+ int na, /* Number of attributes */
+ nis_attr *a) /* An array of attributes */
+{
+ return (db_list_x(name, na, a, 0));
+}
+
+nis_db_list_result *
+db_list_flags(
+ nis_name name, /* Table we are listing */
+ int na, /* Number of attributes */
+ nis_attr *a, /* An array of attributes */
+ ulong_t flags)
+{
+ return (db_list_x(name, na, a, flags));
+}
+
+/*
+ * This function has to release all of the components that were allocated
+ * by db_list above.
+ */
+void
+free_db_list(nis_db_list_result *list)
+{
+ int i;
+
+ if (list == NULL)
+ return;
+ for (i = 0; i < list->numo; i++) {
+ if (list->objs[i].o)
+ nis_destroy_object(list->objs[i].o);
+ }
+ if ((list->objs) && (list->numo))
+ XFREE(list->objs); /* Free the entire array */
+ free(list);
+}
+
+static nis_fn_result __no_memory_fn_result = {
+ NIS_NOMEMORY,
+ 0, {0}, 0
+};
+
+/*
+ * nis_fn_result *db_firstib(name)
+ *
+ * Get the "first" entry from a table. Note this function returns an opaque
+ * "cookie" that has what ever state the underlying database needs to
+ * find the next entry in a chain of entries. Since it is specific to the
+ * underlying database it is opaque to this interface.
+ */
+nis_fn_result *
+db_firstib(
+ nis_name name, /* Table name */
+ int na, /* Number of attributes */
+ nis_attr *a, /* Attribute list */
+ int flags, /* Mangle objects into entries */
+ char *table)
+{
+ db_result *dbres;
+ obj_list *olist;
+ int got;
+ char tblbuf[NIS_MAXNAMELEN * 2];
+ nis_fn_result *res;
+ char *myself = "db_firstib";
+
+ res = (nis_fn_result *)XCALLOC(1, sizeof (nis_fn_result));
+ if ((flags & FN_NORAGS) == 0) {
+ if (res == 0)
+ return (&__no_memory_fn_result);
+ add_cleanup((void (*)())XFREE, (char *)res, "fn (first) res");
+ }
+ if (! table)
+ table = internal_table_name(name, tblbuf);
+ if (! table) {
+ res->status = NIS_BADNAME;
+ return (res);
+ }
+
+ if (verbose)
+ syslog(LOG_INFO, "db_firstib: Fetching first entry from %s",
+ table);
+ LOCK_TABLE(name, RO, res, res->status, name);
+ __start_clock(1);
+
+ {
+ nisdb_tsd_t fallback, *tsd = __nisdb_get_tsd();
+ __nisdb_retry_t refreshRetry = ldapDBTableMapping.
+ refreshErrorRetry;
+ __nisdb_retry_t retrieveRetry = ldapDBTableMapping.
+ retrieveErrorRetry;
+
+ if (tsd == 0) {
+ logmsg(MSG_NOMEM, LOG_ERR,
+ "%s: No memory for TSD; unable to get full status for DB operation",
+ myself);
+ tsd = &fallback;
+ }
+
+ while (1) {
+ /* Establish default status (success) */
+ tsd->nisPlusStat = NIS_SUCCESS;
+ tsd->ldapStat = LDAP_SUCCESS;
+ dbres = db_first_entry(table, na, a);
+ if (dbres == 0) {
+ break;
+ } else {
+ int rd;
+
+ res->status = map_db_status_to_nis_status(
+ dbres->status);
+ if (res->status == NIS_SUCCESS &&
+ tsd->ldapStat == LDAP_SUCCESS &&
+ tsd->nisPlusStat == NIS_SUCCESS)
+ break;
+ ULOCK_TABLE(name, RO, name);
+ rd = retrieveDone(&refreshRetry,
+ &retrieveRetry,
+ tsd->nisPlusStat,
+ tsd->ldapStat, &res->status);
+ LOCK_TABLE(name, RO, res, res->status, name);
+ if (rd)
+ break;
+ }
+ free_db_result(dbres);
+ }
+ }
+
+ switch (res->status) {
+ case NIS_NOMEMORY:
+ case NIS_NOTFOUND:
+ case NIS_TRYAGAIN:
+ case NIS_UNAVAIL:
+ case NIS_NOSUCHNAME:
+ break; /* expected */
+ case NIS_BADREQUEST:
+ if ((flags & FN_NOERROR) == 0)
+ syslog(LOG_ERR,
+ "db_firstib: Table: '%s', Bad Attribute",
+ name);
+ break;
+ case NIS_NOSUCHTABLE:
+ if ((flags & FN_NOERROR) == 0)
+ syslog(LOG_ERR,
+ "db_firstib: Missing table '%s'", name);
+ break;
+ case NIS_SUCCESS:
+ case NIS_CACHEEXPIRED:
+ /* ASSERT(dbres->objects.objects_len == 1); */
+ /* Convert the entry into a NIS+ object */
+ olist = cvt2object(name, dbres->objects.objects_val, 1, &got,
+ (FN_MANGLE & flags));
+ if ((olist == NULL) || (got > 1)) {
+ syslog(LOG_ERR, "db_firstib: Database is corrupt.");
+ res->status = NIS_SYSTEMERROR;
+ } else {
+ res->obj = olist->o; /* Now that's nice and clear! */
+ if ((flags & FN_NORAGS) == 0)
+ add_cleanup(nis_destroy_object,
+ (char *)res->obj, "firstib object.");
+ XFREE(olist); /* free list struct but not obj in it */
+ }
+ /* Now clone the nextinfo cookie */
+ res->cookie.n_len = dbres->nextinfo.db_next_desc_len;
+ res->cookie.n_bytes = (char *)XMALLOC(res->cookie.n_len);
+ memcpy(res->cookie.n_bytes, dbres->nextinfo.db_next_desc_val,
+ dbres->nextinfo.db_next_desc_len);
+ free(dbres->nextinfo.db_next_desc_val);
+ dbres->nextinfo.db_next_desc_val = 0;
+ if (res->obj == NULL) {
+ syslog(LOG_ERR, "db_firstib: Object not found.");
+ res->status = NIS_SYSTEMERROR;
+ }
+ break;
+
+ default:
+ if ((flags & FN_NOERROR) == 0)
+ syslog(LOG_ERR,
+ "db_firstib: Unexpected database result %d.",
+ dbres->status);
+ break;
+ }
+ /* Don't need this anymore */
+ free_db_result(dbres);
+ if (verbose)
+ syslog(LOG_INFO, "db_firstib: returning status of %s",
+ nis_sperrno(res->status));
+ ULOCK_TABLE(name, RO, name);
+ return (res); /* Return it finally */
+}
+
+/*
+ * nis_fn_result *db_nextib(name, cookie)
+ *
+ * Get the "next" entry from a table.
+ */
+nis_fn_result *
+db_nextib(
+ nis_name name,
+ netobj *cookie,
+ int flags,
+ char *table)
+{
+ nis_fn_result *res;
+ db_result *dbres;
+ obj_list *olist;
+ int got;
+ char tblbuf[NIS_MAXNAMELEN * 2];
+ char *myself = "db_nextib";
+
+ res = (nis_fn_result *)XCALLOC(1, sizeof (nis_fn_result));
+ if ((flags & FN_NORAGS) == 0) {
+ if (res == 0)
+ return (&__no_memory_fn_result);
+ add_cleanup((void (*)())XFREE, (char *)res, "fn (next) res");
+ }
+ if (! table)
+ table = internal_table_name(name, tblbuf);
+ if (! table) {
+ res->status = NIS_BADNAME;
+ return (res);
+ }
+ if (verbose)
+ syslog(LOG_INFO, "db_nextib: Fetching entry from %s", table);
+ __start_clock(1);
+ LOCK_TABLE(name, RO, res, res->status, name);
+
+ {
+ nisdb_tsd_t fallback, *tsd = __nisdb_get_tsd();
+ __nisdb_retry_t refreshRetry = ldapDBTableMapping.
+ refreshErrorRetry;
+ __nisdb_retry_t retrieveRetry = ldapDBTableMapping.
+ retrieveErrorRetry;
+
+ if (tsd == 0) {
+ logmsg(MSG_NOMEM, LOG_ERR,
+ "%s: No memory for TSD; unable to get full status for DB operation",
+ myself);
+ tsd = &fallback;
+ }
+
+ while (1) {
+ /* Establish default status (success) */
+ tsd->nisPlusStat = NIS_SUCCESS;
+ tsd->ldapStat = LDAP_SUCCESS;
+ dbres = db_next_entry(table, (db_next_desc *)cookie);
+ if (dbres == 0) {
+ break;
+ } else {
+ int rd;
+
+ res->status = map_db_status_to_nis_status(
+ dbres->status);
+ if (res->status == NIS_SUCCESS &&
+ tsd->ldapStat == LDAP_SUCCESS &&
+ tsd->nisPlusStat == NIS_SUCCESS)
+ break;
+ ULOCK_TABLE(name, RO, name);
+ rd = retrieveDone(&refreshRetry,
+ &retrieveRetry,
+ tsd->nisPlusStat,
+ tsd->ldapStat, &res->status);
+ LOCK_TABLE(name, RO, res, res->status, name);
+ if (rd)
+ break;
+ }
+ free_db_result(dbres);
+ }
+ }
+
+ switch (res->status) {
+ case NIS_NOMEMORY:
+ case NIS_NOTFOUND:
+ case NIS_TRYAGAIN:
+ case NIS_UNAVAIL:
+ case NIS_NOSUCHNAME:
+ break;
+ case NIS_BADREQUEST:
+ if ((flags & FN_NOERROR) == 0)
+ syslog(LOG_ERR,
+ "db_nextib: Bad Attribute in Table: '%s'",
+ name);
+ break;
+ case NIS_NOSUCHTABLE:
+ if ((flags & FN_NOERROR) == 0)
+ syslog(LOG_ERR,
+ "db_nextib: Missing table object '%s'",
+ name);
+ break;
+ case NIS_SUCCESS:
+ case NIS_CACHEEXPIRED:
+ /* ASSERT(dbres->objects.objects_len == 1); */
+ olist = cvt2object(name, dbres->objects.objects_val, 1, &got,
+ (flags & FN_MANGLE));
+ if ((olist == NULL) || (got > 1)) {
+ syslog(LOG_ERR, "db_nextib: Database is corrupt.");
+ res->status = NIS_SYSTEMERROR;
+ } else {
+ res->obj = olist->o;
+ if ((flags & FN_NORAGS) == 0)
+ add_cleanup(nis_destroy_object,
+ (char *)res->obj, "nextib object");
+ XFREE(olist);
+ }
+ /* Now clone the nextinfo cookie */
+ res->cookie.n_len = dbres->nextinfo.db_next_desc_len;
+ res->cookie.n_bytes = (char *)XMALLOC(res->cookie.n_len);
+ memcpy(res->cookie.n_bytes, dbres->nextinfo.db_next_desc_val,
+ dbres->nextinfo.db_next_desc_len);
+ free(dbres->nextinfo.db_next_desc_val);
+ if (res->obj == NULL)
+ res->status = NIS_SYSTEMERROR;
+ break;
+ default:
+ if ((flags & FN_NOERROR) == 0)
+ syslog(LOG_ERR,
+ "db_nextib: Unexpected database result %d",
+ dbres->status);
+ break;
+ }
+ /* Don't need this anymore */
+ free_db_result(dbres);
+ if (verbose)
+ syslog(LOG_INFO, "db_nextib: returning status of %s",
+ nis_sperrno(res->status));
+ ULOCK_TABLE(name, RO, name);
+ return (res); /* Return it finally */
+}
+
+/*
+ * db_flush()
+ *
+ * Flush any pending "next" entries from a list returned by firstib.
+ */
+void
+db_flush(
+ nis_name name, /* table name */
+ netobj *cookie) /* The next_desc */
+{
+ char *table;
+ db_result *dbres;
+ char tblbuf[NIS_MAXNAMELEN * 2];
+
+ table = internal_table_name(name, tblbuf);
+ if (! table)
+ return;
+ if (verbose)
+ syslog(LOG_INFO, "db_flush: Flushing queued entries from %s",
+ table);
+ {
+ int trylock = 1;
+ if (__nis_lock_db_table(name, RO, &trylock, name) == 0) {
+ return;
+ }
+ }
+
+ dbres = db_reset_next_entry(table, (db_next_desc *)cookie);
+ if (dbres && dbres->status != DB_SUCCESS) {
+ syslog(LOG_ERR, "Unable to flush '%s'", table);
+ }
+ free_db_result(dbres);
+ ULOCK_TABLE(name, RO, name);
+}
+
+/*
+ * Add an entry to a table, we assume we've already sanitized the
+ * data.
+ */
+static nis_error
+__db_addib_x(
+ nis_name name, /* Name of the table. */
+ int numattr, /* Number of attributes */
+ nis_attr *attrs, /* array of attributes */
+ nis_object *obj, /* Entry to add to the table */
+ int skiplog, /* if true, don't log action */
+ int nosync)
+{
+ entry_obj eo; /* our "fake" entry */
+ entry_col *oec, /* our objects columns */
+ *ec; /* our objects columns */
+ int i, mc, bufsize; /* counters */
+ db_result *dbres;
+ XDR xdrs; /* XDR stream */
+ uchar_t *buf;
+ char *table;
+ nis_error res;
+ struct table_item *te; /* Table cache entry */
+ struct nis_object *tobj; /* Table nis_object */
+ char tblbuf[NIS_MAXNAMELEN * 2]; /* table path buf */
+ char *myself = "__db_addib_x";
+
+ /* Get the number of columns and the pointer to their data */
+ mc = obj->EN_data.en_cols.en_cols_len;
+ oec = obj->EN_data.en_cols.en_cols_val;
+
+ ec = __get_entry_col(mc+1); /* get some temp storage */
+
+ if (ec == NULL) {
+ return (NIS_NOMEMORY);
+ }
+
+ table = internal_table_name(name, tblbuf);
+ if (! table)
+ return (NIS_BADNAME);
+ if (verbose)
+ syslog(LOG_INFO, "__db_addib: Adding an entry to table %s",
+ table);
+
+ LOCK_TABLE(name, WR, res, res, name);
+ te = __nis_find_item_mt(name, table_cache, RO, 0);
+ if (te == NULL) {
+ /* Do a db_lookup() so that cache is populated */
+ nis_db_result *dbres;
+
+ if (((dbres = db_lookup_deferred(name, FALSE)) == NULL) ||
+ (dbres->status != NIS_SUCCESS)) {
+ ULOCK_TABLE(name, WR, name);
+ syslog(LOG_ERR,
+ "__db_addib: could not find table object for %s",
+ name);
+ return (NIS_NOSUCHTABLE); /* XXX: Error number */
+ }
+ tobj = dbres->obj;
+ /* dbres is freed automatically during cleanup */
+ } else
+ tobj = te->ibobj;
+
+ /* Build up our temporary entry object */
+ eo.en_type = NULL; /* used by cvt2obj - denotes non IN_DIRECTORY */
+ eo.en_cols.en_cols_len = mc + 1;
+ eo.en_cols.en_cols_val = ec;
+
+ /* Copy the entry value pointers, offset by 1 */
+ for (i = 0; i < mc; i++)
+ ec[i+1] = oec[i];
+
+ /*
+ * To prevent the XDR function from making a copy of
+ * the entry columns, we set the columns structure to
+ * 0 (ie no column data)
+ */
+ obj->EN_data.en_cols.en_cols_len = 0; /* Neuter it */
+ obj->EN_data.en_cols.en_cols_val = NULL; /* Neuter it */
+
+ /* Make a fetus object from a FULL object */
+ bufsize = xdr_sizeof(xdr_nis_object, obj);
+ buf = __get_xdr_buf(bufsize);
+ if (buf == NULL) {
+ if (te != 0)
+ __nis_release_item(te, table_cache, RO);
+ ULOCK_TABLE(name, WR, name);
+ syslog(LOG_ERR, "__db_addib: out of memory!");
+ return (NIS_NOMEMORY);
+ }
+
+ xdrmem_create(&xdrs, (char *)buf, bufsize, XDR_ENCODE);
+ if (! xdr_nis_fetus_object(&xdrs, obj, tobj)) {
+ if (te != 0)
+ __nis_release_item(te, table_cache, RO);
+ ULOCK_TABLE(name, WR, name);
+ syslog(LOG_ERR, "__db_addib: Failure to encode entry.");
+ return (NIS_SYSTEMERROR);
+ }
+
+ /* Un-neuter it so that it can be properly freed */
+ obj->EN_data.en_cols.en_cols_len = mc;
+ obj->EN_data.en_cols.en_cols_val = oec;
+ ec[0].ENVAL = (char *)buf; /* Point to encoded one */
+ ec[0].ENLEN = xdr_getpos(&xdrs);
+ ec[0].ec_flags = EN_BINARY+EN_XDR;
+
+ {
+ nisdb_tsd_t fallback, *tsd = __nisdb_get_tsd();
+ __nisdb_retry_t storeRetry = ldapDBTableMapping.
+ storeErrorRetry;
+
+ if (tsd == 0) {
+ logmsg(MSG_NOMEM, LOG_ERR,
+ "%s: No memory for TSD; unable to get full status for DB operation",
+ myself);
+ tsd = &fallback;
+ }
+
+ while (1) {
+ /* Establish default status (success) */
+ tsd->nisPlusStat = NIS_SUCCESS;
+ tsd->ldapStat = LDAP_SUCCESS;
+
+ if (skiplog)
+ dbres = __db_add_entry_nolog(table, numattr,
+ attrs, &eo);
+ else if (nosync)
+ dbres = __db_add_entry_nosync(table, numattr,
+ attrs, &eo);
+ else
+ dbres = db_add_entry(table, numattr,
+ attrs, &eo);
+ if (dbres == 0) {
+ res = NIS_NOMEMORY;
+ break;
+ } else {
+ int rd;
+
+ res = map_db_status_to_nis_status(dbres->
+ status);
+ if (res == NIS_SUCCESS &&
+ tsd->ldapStat == LDAP_SUCCESS &&
+ tsd->nisPlusStat == NIS_SUCCESS)
+ break;
+ /* Unlock while we (possibly) sleep */
+ ULOCK_TABLE(name, WR, name);
+ rd = storeDone(&storeRetry, tsd->nisPlusStat,
+ tsd->ldapStat, &res);
+ LOCK_TABLE(name, WR, res, res, name);
+ if (rd)
+ break;
+ }
+ free_db_result(dbres);
+ }
+ }
+
+ switch (res) {
+ case NIS_NOMEMORY:
+ case NIS_NOFILESPACE:
+ case NIS_SYSTEMERROR:
+ case NIS_UNAVAIL:
+ break;
+ case NIS_SUCCESS:
+ (void) mark_for_sync(table);
+ break;
+ default:
+ syslog(LOG_ERR, "__db_addib: Unexpected database result %d.",
+ dbres->status);
+ break;
+ }
+ if (verbose)
+ syslog(LOG_INFO, "__db_addib: done. (%d)", res);
+ free_db_result(dbres);
+ if (res == NIS_SUCCESS) {
+ multival_invalidate(obj);
+ }
+ if (te != 0)
+ __nis_release_item(te, table_cache, RO);
+ ULOCK_TABLE(name, WR, name);
+ return (res);
+}
+
+nis_error
+__db_addib(
+ nis_name name, /* Name of the table. */
+ int numattr, /* Number of attributes */
+ nis_attr *attrs, /* array of attributes */
+ nis_object *obj) /* Entry to add to the table */
+{
+ return (__db_addib_x(name, numattr, attrs, obj, 0, 0));
+}
+
+nis_error
+__db_addib_nolog(
+ nis_name name, /* Name of the table. */
+ int numattr, /* Number of attributes */
+ nis_attr *attrs, /* array of attributes */
+ nis_object *obj) /* Entry to add to the table */
+{
+ return (__db_addib_x(name, numattr, attrs, obj, 1, 0));
+}
+
+nis_error
+__db_addib_nosync(
+ nis_name name, /* Name of the table. */
+ int numattr, /* Number of attributes */
+ nis_attr *attrs, /* array of attributes */
+ nis_object *obj) /* Entry to add to the table */
+{
+ return (__db_addib_x(name, numattr, attrs, obj, 0, 1));
+}
+
+static nis_db_result __no_memory_db_result = {NIS_NOMEMORY, 0, 0};
+
+/*
+ * Add an entry to a table, we assume we've already sanitized the
+ * data.
+ */
+nis_db_result *
+db_addib(
+ nis_name name, /* Name of the table. */
+ int numattr, /* Number of attributes */
+ nis_attr *attrs, /* array of attributes */
+ nis_object *obj, /* Entry to add to the table */
+ nis_object *tobj) /* Table to add it too. */
+{
+ nis_attr *attr_list;
+ int i, mc, na;
+ table_col *tc;
+ entry_col *ec;
+ nis_db_result *res;
+ log_entry le;
+
+ /*
+ * First we create a fully specified entry list, with a
+ * set of attribute/values to go with it.
+ */
+ res = (nis_db_result *)XCALLOC(1, sizeof (nis_db_result));
+ if (res == 0)
+ return (&__no_memory_db_result);
+ add_cleanup((void (*)())XFREE, (char *)res, "db_addib result");
+ memset((char *)&le, 0, sizeof (le));
+ LOCK_TABLE(name, WR, res, res->status, name);
+ mc = tobj->TA_data.ta_cols.ta_cols_len;
+ tc = tobj->TA_data.ta_cols.ta_cols_val;
+ ec = obj->EN_data.en_cols.en_cols_val;
+ attr_list = __get_attrs(mc);
+ if (attr_list == NULL) {
+ ULOCK_TABLE(name, WR, name);
+ res->status = NIS_NOMEMORY;
+ return (res);
+ }
+ for (i = 0, na = 0; i < mc; i++) {
+ if ((tc[i].tc_flags & TA_SEARCHABLE) != 0) {
+ attr_list[na].zattr_ndx = tc[i].tc_name;
+ attr_list[na].ZAVAL = ec[i].ENVAL;
+ attr_list[na].ZALEN = ec[i].ENLEN;
+ na++;
+ }
+ }
+ le.le_name = name;
+ le.le_object = *obj;
+ le.le_attrs.le_attrs_len = na;
+ le.le_attrs.le_attrs_val = attr_list;
+ le.le_type = ADD_IBASE;
+ le.le_time = obj->zo_oid.mtime;
+ add_update(&le);
+ __start_clock(1);
+ res->status = __db_addib(name, na, attr_list, obj);
+ res->ticks = __stop_clock(1);
+ if (res->status != NIS_SUCCESS)
+ syslog(LOG_ERR, "db_addib: unable to add entry to %s", name);
+ ULOCK_TABLE(name, WR, name);
+ return (res);
+}
+
+nis_error
+__db_remib_x(
+ nis_name name,
+ int nattr,
+ nis_attr *attrs,
+ int nosync)
+{
+ nis_error res;
+ db_result *dbres;
+ char *table;
+ char tblbuf[NIS_MAXNAMELEN * 2];
+ char *myself = "__db_remib_x";
+
+ table = internal_table_name(name, tblbuf);
+ if (! table)
+ return (NIS_BADNAME);
+
+ if (verbose)
+ syslog(LOG_INFO,
+ "__db_remib: Removing an entry from table %s", table);
+
+ LOCK_TABLE(name, WR, res, res, name);
+
+ {
+ nisdb_tsd_t fallback, *tsd = __nisdb_get_tsd();
+ __nisdb_retry_t storeRetry = ldapDBTableMapping.
+ storeErrorRetry;
+
+ if (tsd == 0) {
+ logmsg(MSG_NOMEM, LOG_ERR,
+ "%s: No memory for TSD; unable to get full status for DB operation",
+ myself);
+ tsd = &fallback;
+ }
+
+ while (1) {
+ /* Establish default status (success) */
+ tsd->nisPlusStat = NIS_SUCCESS;
+ tsd->ldapStat = LDAP_SUCCESS;
+
+ if (nosync)
+ dbres = __db_remove_entry_nosync(table, nattr,
+ attrs);
+ else
+ dbres = db_remove_entry(table, nattr, attrs);
+ if (dbres == 0) {
+ res = NIS_NOMEMORY;
+ break;
+ } else {
+ int rd;
+
+ res = map_db_status_to_nis_status(dbres->
+ status);
+ if (res == NIS_SUCCESS &&
+ tsd->ldapStat == LDAP_SUCCESS &&
+ tsd->nisPlusStat == NIS_SUCCESS)
+ break;
+ /* Unlock while we (possibly) sleep */
+ ULOCK_TABLE(name, WR, name);
+ rd = storeDone(&storeRetry, tsd->nisPlusStat,
+ tsd->ldapStat, &res);
+ LOCK_TABLE(name, WR, res, res, name);
+ if (rd)
+ break;
+ }
+ free_db_result(dbres);
+ }
+ }
+
+ switch (res) {
+ case NIS_NOMEMORY:
+ case NIS_NOFILESPACE:
+ case NIS_SYSTEMERROR:
+ case NIS_UNAVAIL:
+ break;
+ case NIS_SUCCESS:
+ (void) mark_for_sync(table);
+ break;
+ case NIS_NOTFOUND:
+ /* this occurs when applying log updates */
+ res = NIS_SUCCESS;
+ break;
+ default:
+ syslog(LOG_ERR, "__db_remib: Unexpected database result %d.",
+ dbres->status);
+ break;
+ }
+ if (verbose)
+ syslog(LOG_INFO, "__db_remib: done. (%d)", res);
+ free_db_result(dbres);
+ if (res == NIS_SUCCESS) {
+ nis_db_result *tres;
+
+ tres = db_lookup_deferred(name, FALSE);
+ multival_invalidate(tres->obj);
+ }
+ ULOCK_TABLE(name, WR, name);
+ return (res);
+}
+
+nis_error
+__db_remib(
+ nis_name name,
+ int nattr,
+ nis_attr *attrs)
+{
+ return (__db_remib_x(name, nattr, attrs, 0));
+}
+
+nis_error
+__db_remib_nosync(
+ nis_name name,
+ int nattr,
+ nis_attr *attrs)
+{
+ return (__db_remib_x(name, nattr, attrs, 1));
+}
+
+/*
+ * The remove operation. Since it can remove multiple entries we pass it
+ * the list for the log entries.
+ */
+nis_db_result *
+db_remib(
+ nis_name name,
+ int nattrs,
+ nis_attr *attrs,
+ obj_list *olist,
+ int numo,
+ nis_object *tobj,
+ ulong_t xid_time)
+{
+ nis_attr *attr_list;
+ int i, j, mc, na;
+ table_col *tc;
+ entry_col *ec;
+ nis_db_result *res;
+ log_entry le;
+ int *amap;
+
+ /*
+ * First we create a fully specified entry list, with a
+ * set of attribute/values to go with it.
+ */
+ res = (nis_db_result *)XCALLOC(1, sizeof (nis_db_result));
+ if (res == 0)
+ return (&__no_memory_db_result);
+ add_cleanup((void (*)())XFREE, (char *)res, "db_remib result");
+ LOCK_TABLE(name, WR, res, res->status, name);
+ memset((char *)&le, 0, sizeof (le));
+ mc = tobj->TA_data.ta_cols.ta_cols_len;
+ tc = tobj->TA_data.ta_cols.ta_cols_val;
+ na = 0; /* Actual attrs */
+ attr_list = __get_attrs(mc);
+ /* Cheat and allocate a "static" array */
+ amap = (int *)__get_xdr_buf(sizeof (int) * mc);
+
+ if (attr_list == NULL) {
+ ULOCK_TABLE(name, WR, name);
+ res->status = NIS_NOMEMORY;
+ return (res);
+ }
+ for (i = 0, na = 0; i < mc; i++) {
+ if ((tc[i].tc_flags & TA_SEARCHABLE) != 0) {
+ attr_list[na].zattr_ndx = tc[i].tc_name;
+ amap[na] = i;
+ na++;
+ }
+ }
+
+ /* Add log updates for all of the entries that will be removed */
+ for (i = 0; i < numo; i++) {
+ ec = olist[i].o->EN_data.en_cols.en_cols_val;
+ for (j = 0; j < na; j++) {
+ attr_list[j].ZAVAL = ec[amap[j]].ENVAL;
+ attr_list[j].ZALEN = ec[amap[j]].ENLEN;
+ }
+ le.le_name = name;
+ le.le_object = *(olist[i].o);
+ le.le_attrs.le_attrs_len = na;
+ le.le_attrs.le_attrs_val = attr_list;
+ le.le_type = REM_IBASE;
+ le.le_time = xid_time;
+ add_update(&le);
+ }
+ __start_clock(1);
+ res->status = __db_remib(name, nattrs, attrs);
+ res->ticks = __stop_clock(1);
+ if (res->status != NIS_SUCCESS)
+ syslog(LOG_ERR, "db_remib: unable to remove entry from %s",
+ name);
+ ULOCK_TABLE(name, WR, name);
+ return (res);
+}
+
+
+/*
+ * __create_table()
+ *
+ * Create the underlying database table that supports the current database.
+ */
+static nis_error
+__create_table(
+ char *table,
+ nis_object *obj)
+{
+ table_obj fake, *t; /* Fake table object here. */
+ table_col *tc = NULL; /* Fake columns data */
+ int i;
+
+ /* Assume locking done by caller */
+
+ t = &(obj->TA_data);
+ tc = __get_table_col(t->ta_cols.ta_cols_len+1);
+ if (tc == NULL)
+ return (NIS_NOMEMORY);
+
+ for (i = 0; i < t->ta_cols.ta_cols_len; i++) {
+ if (t->ta_cols.ta_cols_val[i].tc_flags & TA_SEARCHABLE)
+ break;
+ }
+ if (i == t->ta_cols.ta_cols_len) {
+ if (verbose)
+ syslog(LOG_INFO,
+ "Cannot create table: %s: no searchable columns.", table);
+ return (NIS_BADOBJECT);
+ }
+
+ /* Copy the important parts of the table structure over */
+ fake = obj->TA_data;
+ /* Now shift the columns right by 1 */
+ for (i = 0; i < fake.ta_cols.ta_cols_len; i++) {
+ tc[i+1] = fake.ta_cols.ta_cols_val[i];
+ }
+ tc[0].tc_name = NULL; /* NO name for the entry column */
+ tc[0].tc_flags = TA_XDR+TA_BINARY;
+ tc[0].tc_rights = 0; /* only the server sees them */
+ fake.ta_cols.ta_cols_len++; /* Add one to this */
+ fake.ta_cols.ta_cols_val = tc; /* fixup this */
+ return (db_create(table, &fake));
+}
+
+static cp_result mem_err = {NIS_NOMEMORY, 0, 0};
+
+/*
+ * do_checkpoint
+ *
+ * This function checkpoints the table named, or all tables
+ * that the server has if no table is named.
+ *
+ * NB: This can be time consuming so the server should fork
+ * a readonly child to handle requests while we're out here.
+ *
+ * This function returns NIS_SUCCESS if all tables were
+ * checkpointed and return NIS_PARTIAL otherwise.
+ */
+static cp_result *
+do_checkpoint(
+ nis_name name) /* name of directory to checkpoint */
+{
+ int status;
+ cp_result *res;
+ char *table;
+ char tblbuf[NIS_MAXNAMELEN * 2];
+
+ res = (cp_result *) XCALLOC(1, sizeof (cp_result));
+ if (! res)
+ return (&mem_err);
+ add_cleanup((void (*)())XFREE, (char *)res, "do_chkpt result");
+
+ if (name == NULL)
+ table = NULL;
+ else
+ table = internal_table_name(name, tblbuf);
+ if (verbose)
+ syslog(LOG_INFO, "Checkpointing table '%s'",
+ table ? table : "(all)");
+ if (table == 0)
+ name = ".";
+ LOCK_TABLE(name, WR, res, res->cp_status, name);
+
+ __start_clock(1);
+ status = db_checkpoint(table);
+ res->cp_dticks = __stop_clock(1);
+ if (status != DB_SUCCESS) {
+ syslog(LOG_ERR,
+ "db_checkpoint: Unable to checkpoint table '%s' because %s",
+ (table ? table : "(all)"),
+ nis_sperrno(map_db_status_to_nis_status(status)));
+ res->cp_status = NIS_PARTIAL;
+ } else
+ res->cp_status = NIS_SUCCESS;
+
+ ULOCK_TABLE(name, WR, name);
+ return (res);
+}
+
+/*
+ * do_checkpoint_dir
+ *
+ * This function checkpoints all of the tables in a named directory
+ * and the directory itself.
+ * This is accomplished by iterating over the directory and then
+ * checkpointing anything that looks like a table.
+ *
+ * NB: This can be time consuming so the server should have forked
+ * a read only child to handle requests while we're out here.
+ *
+ * This function returns returns NIS_SUCCESS if all tables were
+ * checkpointed and returne NIS_PARTIAL if only some of them were.
+ */
+static cp_result *
+do_checkpoint_dir(
+ nis_name name) /* name of directory to checkpoint */
+{
+ int status, err;
+ cp_result *res;
+ char *table;
+ char tblname[NIS_MAXNAMELEN];
+ netobj cookie;
+ nis_fn_result *fnr;
+ char tblbuf[NIS_MAXNAMELEN * 2];
+
+ if (! name)
+ return (NULL); /* name is required */
+
+ res = (cp_result *) XCALLOC(1, sizeof (cp_result));
+ if (! res)
+ return (&mem_err);
+
+ add_cleanup((void (*)())XFREE, (char *)res, "do_chkpt result");
+
+ if (verbose)
+ syslog(LOG_INFO, "Checkpointing directory '%s'", name);
+
+ err = 0;
+ fnr = db_firstib(name, 0, NULL, FN_NORAGS, NULL);
+ cookie = fnr->cookie;
+ res->cp_dticks = fnr->ticks;
+ /* First, do directory itself. */
+ if (fnr->status == NIS_NOTFOUND || fnr->status == NIS_SUCCESS) {
+ table = internal_table_name(name, tblbuf);
+ __start_clock(1);
+ if (verbose)
+ syslog(LOG_INFO, "Checkpointing directory '%s'",
+ table);
+ LOCK_DIRECTORY(name, WR, res->cp_status, name);
+ if (res->cp_status == NIS_SUCCESS) {
+ status = db_checkpoint(table);
+ res->cp_dticks += __stop_clock(1);
+ if (status != DB_SUCCESS) {
+ syslog(LOG_ERR,
+ "db_checkpoint: Unable to checkpoint '%s' because %s",
+ (name ? name : "(null)"),
+ nis_sperrno(map_db_status_to_nis_status(
+ status)));
+ err++;
+ }
+ } else {
+ res->cp_dticks += __stop_clock(1);
+ syslog(LOG_ERR,
+ "db_checkpoint: Unable to checkpoint '%s' because %s",
+ (name ? name : "(null)"), res->cp_status);
+ err++;
+ }
+ ULOCK_DIRECTORY(name, WR, name);
+ }
+
+ /* Do each table or directory within directory. */
+ while (fnr->status == NIS_SUCCESS) {
+ if (__type_of(fnr->obj) == NIS_TABLE_OBJ ||
+ __type_of(fnr->obj) == NIS_DIRECTORY_OBJ) {
+ sprintf(tblname, "%s.%s", fnr->obj->zo_name,
+ fnr->obj->zo_domain);
+ table = internal_table_name(tblname, tblbuf);
+ __start_clock(1);
+ if (verbose)
+ syslog(LOG_INFO, "Checkpointing table '%s'",
+ table);
+ LOCK_TABLE_NORET(tblname, WR, res->cp_status,
+ tblname);
+ if (res->cp_status == NIS_SUCCESS) {
+ status = db_checkpoint(table);
+ res->cp_dticks += __stop_clock(1);
+ /*
+ * We ignore DB_BADTABLE errors because we might
+ * not serve the contents of the directory.
+ */
+ if (status != DB_SUCCESS &&
+ status != DB_BADTABLE) {
+ syslog(LOG_ERR,
+ "db_checkpoint: Unable to checkpoint '%s' because %s",
+ (tblname ? tblname : "(null)"),
+ nis_sperrno(
+ map_db_status_to_nis_status(
+ status)));
+ err++;
+ }
+ ULOCK_TABLE(tblname, WR, name);
+ } else {
+ res->cp_dticks += __stop_clock(1);
+ syslog(LOG_ERR,
+ "db_checkpoint: Unable to checkpoint '%s' because %s",
+ (tblname ? tblname : "(null)"), res->cp_status);
+ err++;
+ }
+ }
+ nis_destroy_object(fnr->obj);
+ XFREE(fnr);
+ /* note the call to nextib frees the old cookie! */
+ fnr = db_nextib(name, &cookie, FN_NORAGS, NULL);
+ cookie = fnr->cookie;
+ }
+ XFREE(fnr);
+ if (err)
+ res->cp_status = NIS_PARTIAL;
+ else
+ res->cp_status = NIS_SUCCESS;
+ return (res);
+}
+
+/*
+ * nis_checkpoint_svc maintains a list of directories to be
+ * checkpointed, call do_checkpoint_dir on items on that list.
+ * Otherwise, call do_checkpoint(NULL) to checkpoint entire database.
+ */
+
+int
+checkpoint_db()
+{
+ ping_item *cp, *nxt;
+ cp_result *res;
+
+ LOCK_LIST(&checkpoint_list, "checkpoint_db(checkpoint_list)");
+ cp = (ping_item *)(checkpoint_list.first);
+
+ /* No directories specified; checkpoint entire database. */
+
+ if (cp == NULL || checkpoint_all) {
+ res = do_checkpoint(NULL);
+ clear_checkpoint_list_nolock();
+ checkpoint_all = 0;
+ /* res is on cleanup list; no need to free. */
+ ULOCK_LIST(&checkpoint_list, "checkpoint_db(checkpoint_list)");
+ return (1);
+ }
+
+ for (cp; cp; cp = nxt) {
+ nxt = (ping_item *)(cp->item.nxt_item);
+ res = do_checkpoint_dir(cp->item.name);
+ if (res->cp_status != NIS_SUCCESS) {
+ syslog(LOG_WARNING,
+ "checkpoint_db: unable to completely checkpoint %s because %s",
+ (cp->item.name ? cp->item.name : "(null)"),
+ nis_sperrno(res->cp_status));
+ }
+ /* ignore status. Can try checkpoint later if not complete. */
+ (void) nis_remove_item(cp->item.name, &checkpoint_list);
+ XFREE(cp->item.name);
+ if (cp->obj)
+ nis_destroy_object(cp->obj);
+ XFREE(cp);
+ }
+ ULOCK_LIST(&checkpoint_list, "checkpoint_db(checkpoint_list)");
+ return (1);
+}
+
+int
+checkpoint_table(char *name)
+{
+ char *table;
+ db_status status;
+ char tblbuf[NIS_MAXNAMELEN * 2];
+ int ret = 0;
+
+ {
+ nis_error dummy;
+ LOCK_TABLE(name, WR, dummy, ret, name);
+ }
+ table = internal_table_name(name, tblbuf);
+ status = db_checkpoint(table);
+ if (status != DB_SUCCESS) {
+ syslog(LOG_ERR,
+ "checkpoint_table: Unable to checkpoint table %s because %s",
+ (table ? table : "(null)"),
+ nis_sperrno(map_db_status_to_nis_status(status)));
+ } else {
+ ret = 1;
+ }
+ ULOCK_TABLE(name, WR, name);
+ return (ret);
+}
+
+/*
+ * Remove all items from checkpoint list. List must be locked by caller.
+ */
+static int
+clear_checkpoint_list_nolock()
+{
+ ping_item *cp, *nxt;
+
+ for (cp = (ping_item *)(checkpoint_list.first); cp; cp = nxt) {
+ nxt = (ping_item *)(cp->item.nxt_item);
+ (void) nis_remove_item(cp->item.name, &checkpoint_list);
+ XFREE(cp->item.name);
+ if (cp->obj)
+ nis_destroy_object(cp->obj);
+ XFREE(cp);
+ }
+ return (1);
+}
+
+int
+clear_checkpoint_list(void) {
+ int ret;
+
+ LOCK_LIST(&checkpoint_list, "clear_checkpoint_list(checkpoint_list)");
+ ret = clear_checkpoint_list_nolock();
+ ULOCK_LIST(&checkpoint_list, "clear_checkpoint_list(checkpoint_list)");
+
+ return (ret);
+}
+
+/*
+ * Returns NIS_SUCCESS if table exists; NIS_NOSUCHTABLE if it does not exist.
+ * Otherwise, return error returned by database.
+ */
+nis_error
+db_find_table(nis_name name)
+{
+ char tblbuf[NIS_MAXNAMELEN * 2];
+ char *table = internal_table_name(name, tblbuf);
+ db_status dstatus;
+
+ if (!table) {
+ return (NIS_BADNAME);
+ }
+
+ {
+ nis_error ret;
+ LOCK_TABLE(name, RO, ret, ret, name);
+ }
+ dstatus = db_table_exists(table);
+ ULOCK_TABLE(name, RO, name);
+ return (map_db_status_to_nis_status(dstatus));
+}
+
+
+nis_error
+db_create(nis_name name, table_obj *obj)
+{
+ char tblbuf[NIS_MAXNAMELEN * 2];
+ char *table = internal_table_name(name, tblbuf);
+ db_status dstatus;
+ nis_error err;
+ char *myself = "db_create";
+
+ if (! table) {
+ return (NIS_BADNAME);
+ }
+
+ LOCK_TABLE(name, WR, err, err, name);
+
+ {
+ nisdb_tsd_t fallback, *tsd = __nisdb_get_tsd();
+ __nisdb_retry_t storeRetry = ldapDBTableMapping.
+ storeErrorRetry;
+
+ if (tsd == 0) {
+ logmsg(MSG_NOMEM, LOG_ERR,
+ "%s: No memory for TSD; unable to get full status for DB operation",
+ myself);
+ tsd = &fallback;
+ }
+
+ while (1) {
+ int rd;
+
+ /* Establish default status (success) */
+ tsd->nisPlusStat = NIS_SUCCESS;
+ tsd->ldapStat = LDAP_SUCCESS;
+ dstatus = db_create_table(table, obj);
+ err = map_db_status_to_nis_status(dstatus);
+ if (dstatus == DB_SUCCESS &&
+ tsd->ldapStat == LDAP_SUCCESS &&
+ tsd->nisPlusStat == NIS_SUCCESS)
+ break;
+ ULOCK_TABLE(name, WR, name);
+ rd = storeDone(&storeRetry, tsd->nisPlusStat,
+ tsd->ldapStat, &err);
+ LOCK_TABLE(name, WR, err, err, name);
+ if (rd)
+ break;
+ }
+ }
+
+ if (dstatus == DB_SUCCESS)
+ (void) mark_for_sync(table);
+ err = map_db_status_to_nis_status(dstatus);
+ ULOCK_TABLE_REMCOND(name, WR, dstatus != DB_SUCCESS, name);
+ return (err);
+}
+
+nis_error
+db_destroy(nis_name name)
+{
+ char tblbuf[NIS_MAXNAMELEN * 2];
+ char *table = internal_table_name(name, tblbuf);
+ db_status dstatus;
+ nis_error err;
+ char *myself = "db_destroy";
+
+ if (! table) {
+ return (NIS_BADNAME);
+ }
+
+ LOCK_TABLE(name, WR, err, err, name);
+
+ {
+ nisdb_tsd_t fallback, *tsd = __nisdb_get_tsd();
+ __nisdb_retry_t storeRetry = ldapDBTableMapping.
+ storeErrorRetry;
+
+ if (tsd == 0) {
+ logmsg(MSG_NOMEM, LOG_ERR,
+ "%s: No memory for TSD; unable to get full status for DB operation",
+ myself);
+ tsd = &fallback;
+ }
+
+ while (1) {
+ int rd;
+
+ /* Establish default status (success) */
+ tsd->nisPlusStat = NIS_SUCCESS;
+ tsd->ldapStat = LDAP_SUCCESS;
+ dstatus = db_destroy_table(table);
+ err = map_db_status_to_nis_status(dstatus);
+ if (dstatus == DB_SUCCESS &&
+ tsd->ldapStat == LDAP_SUCCESS &&
+ tsd->nisPlusStat == NIS_SUCCESS)
+ break;
+ ULOCK_TABLE(name, WR, name);
+ rd = storeDone(&storeRetry, tsd->nisPlusStat,
+ tsd->ldapStat, &err);
+ LOCK_TABLE(name, WR, err, err, name);
+ if (rd)
+ break;
+ }
+ }
+
+ if (dstatus == DB_SUCCESS)
+ (void) mark_for_sync(table);
+ ULOCK_TABLE_REMCOND(name, WR, dstatus == DB_SUCCESS, name);
+ return (err);
+}
+
+static int
+mark_for_sync(char *table) {
+
+ table_list_entry_t *te;
+ int ret = 0;
+
+ MUTEXLOCK(table_list, "mark_for_sync(table_list)");
+
+ for (te = table_list; te != 0; te = te->next) {
+ if (strcmp(te->table, table) == 0)
+ break;
+ }
+ if (te == 0 && (te = malloc(sizeof (table_list_entry_t)+
+ strlen(table)+1)) != 0) {
+ te->table = (char *)((ulong_t)te + sizeof (*te));
+ (void) strcpy(te->table, table);
+ te->next = table_list;
+ table_list = te;
+ } else if (te == 0) {
+ syslog(LOG_WARNING,
+ "mark_for_sync: could not add sync entry for \"%s\"\n", table);
+ ret = ENOMEM;
+ }
+
+ MUTEXUNLOCK(table_list, "mark_for_sync(table_list)");
+
+ return (ret);
+}
+
+int
+nis_db_sync_log(void) {
+
+ table_list_entry_t *te;
+ int ret = 0;
+ db_status stat;
+
+ MUTEXLOCK(table_list, "nis_db_sync_log(table_list)");
+
+ while ((te = table_list) != 0) {
+ if ((stat = db_sync_log(te->table)) != DB_SUCCESS) {
+ if (verbose)
+ syslog(LOG_INFO,
+ "nis_db_sync_log: error %d syncing \"%s\"",
+ stat, te->table);
+ ret++;
+ }
+ table_list = te->next;
+ free(te);
+ }
+
+ MUTEXUNLOCK(table_list, "nis_db_sync_log(table_list)");
+
+ return (ret);
+}
+
+nis_error
+db_configure(char *table) {
+ db_status stat;
+
+ stat = __db_configure(table);
+ return (map_db_status_to_nis_status(stat));
+}
+
+/*
+ * Given an input NIS+ and LDAP status, perform appropriate actions
+ * according to retrieveError/refreshError. May set '*outStat'.
+ */
+int
+retrieveDone(__nisdb_retry_t *refreshRetry, __nisdb_retry_t *retrieveRetry,
+ int nisPlusStat, int ldapStat, nis_error *outStat) {
+
+ if (ldapStat == LDAP_NO_SUCH_OBJECT) {
+ *outStat = NIS_NOTFOUND;
+ return (1);
+ } else if (ldapStat != LDAP_SUCCESS) {
+ /* Tried LDAP, didn't work => retrieve error */
+ if (ldapDBTableMapping.retrieveError == use_cached) {
+ /* Use refresh error actions */
+ if ((ldapDBTableMapping.refreshError ==
+ continue_using ||
+ ldapDBTableMapping.refreshError ==
+ continue_using_retry) &&
+ nisPlusStat == NIS_CACHEEXPIRED) {
+ *outStat = NIS_SUCCESS;
+ return (1);
+ } else if (ldapDBTableMapping.refreshError ==
+ continue_using ||
+ ldapDBTableMapping.refreshError ==
+ ref_retry ||
+ ldapDBTableMapping.refreshError ==
+ continue_using_retry) {
+ return (!__nis_retry_sleep(refreshRetry, 0));
+ } else if (ldapDBTableMapping.refreshError ==
+ cache_expired) {
+ *outStat = NIS_CACHEEXPIRED;
+ return (1);
+ } else {
+ *outStat = NIS_TRYAGAIN;
+ return (1);
+ }
+ } else if (ldapDBTableMapping.retrieveError == ret_retry) {
+ return (!__nis_retry_sleep(retrieveRetry, 0));
+ } else if (ldapDBTableMapping.retrieveError == try_again) {
+ *outStat = NIS_TRYAGAIN;
+ return (1);
+ } else if (ldapDBTableMapping.retrieveError == ret_unavail) {
+ *outStat = NIS_UNAVAIL;
+ return (1);
+ } else if (ldapDBTableMapping.retrieveError == no_such_name) {
+ *outStat = NIS_NOSUCHNAME;
+ return (1);
+ }
+ } else if (nisPlusStat != NIS_SUCCESS) {
+ if (nisPlusStat == NIS_CACHEEXPIRED &&
+ (ldapDBTableMapping.refreshError ==
+ continue_using ||
+ ldapDBTableMapping.refreshError ==
+ continue_using_retry))
+ *outStat = NIS_SUCCESS;
+ else
+ *outStat = nisPlusStat;
+ return (1);
+ }
+
+ /* Both NIS+ and LDAP status OK => done */
+ return (1);
+}
+
+/*
+ * Given an input NIS+ and LDAP status, perform appropriate actions
+ * according to storeError. May set '*outStat'.
+ */
+int
+storeDone(__nisdb_retry_t *storeRetry, int nisPlusStat, int ldapStat,
+ nis_error *outStat) {
+
+ if (ldapStat != LDAP_SUCCESS) {
+ if (ldapDBTableMapping.storeError == sto_retry) {
+ return (!__nis_retry_sleep(storeRetry, 0));
+ } else if (ldapDBTableMapping.storeError == system_error) {
+ *outStat = NIS_SYSTEMERROR;
+ return (1);
+ } else if (ldapDBTableMapping.storeError == sto_unavail) {
+ *outStat = NIS_UNAVAIL;
+ return (1);
+ }
+ } else if (nisPlusStat != NIS_SUCCESS) {
+ *outStat = nisPlusStat;
+ return (1);
+ }
+
+ /* Both NIS+ and LDAP status OK => done */
+ return (1);
+}
+
+extern int loadAllLDAP(int, void *, db_status *);
+
+/*
+ * Up-/down-loads LDAP data, depending on the value of
+ * ldapConfig.initialUpdate. Returns 0 if the rpc.nisd
+ * can start serving data, 1 if it should exit, -1 if
+ * there was an error during the load.
+ */
+int
+loadLDAPdata(void) {
+ void *prevCookie, *cookie = 0;
+ int stat, rd;
+ db_status dstat;
+ nis_error nerr;
+ __nisdb_retry_t refresh, retrieve, store;
+ char *myself = "loadLDAPdata";
+
+ refresh = ldapDBTableMapping.refreshErrorRetry;
+ retrieve = ldapDBTableMapping.retrieveErrorRetry;
+ store = ldapDBTableMapping.storeErrorRetry;
+
+ while (1) {
+ switch (ldapConfig.initialUpdate) {
+ case ini_none:
+ logmsg(MSG_NOTIMECHECK, LOG_INFO,
+ "No up-/down-load to/from LDAP");
+ return (0);
+ case from_ldap:
+ case from_ldap_update_only:
+ logmsg(MSG_NOTIMECHECK, LOG_INFO,
+ "Down-loading data from LDAP");
+ prevCookie = cookie;
+ stat = loadAllLDAP(1, &cookie, &dstat);
+ nerr = map_db_status_to_nis_status(dstat);
+ rd = retrieveDone(&refresh, &retrieve,
+ nerr, stat, &nerr);
+ break;
+ case to_ldap:
+ case to_ldap_update_only:
+ logmsg(MSG_NOTIMECHECK, LOG_INFO,
+ "Up-loading data to LDAP");
+ prevCookie = cookie;
+ stat = loadAllLDAP(0, &cookie, &dstat);
+ nerr = map_db_status_to_nis_status(dstat);
+ rd = storeDone(&store, nerr, stat, &nerr);
+ break;
+ default:
+ logmsg(MSG_NOTIMECHECK, LOG_ERR,
+ "%s: Unknown initial update option %d",
+ myself, ldapConfig.initialUpdate);
+ return (-1);
+ }
+
+ /*
+ * If we're done (rd != 0), tell the caller if the rpc.nisd
+ * should exit or continue running.
+ */
+ if (rd) {
+ if (stat != LDAP_SUCCESS || dstat != DB_SUCCESS) {
+ logmsg(MSG_NOTIMECHECK, LOG_ERR,
+ "%s: LDAP status 0x%x, DB status %d",
+ myself, stat, dstat);
+ return (-1);
+ }
+ switch (ldapConfig.initialUpdate) {
+ case from_ldap:
+ case to_ldap:
+ logmsg(MSG_NOTIMECHECK, LOG_INFO,
+ "LDAP load done; continuing");
+ return (0);
+ case from_ldap_update_only:
+ case to_ldap_update_only:
+ logmsg(MSG_NOTIMECHECK, LOG_INFO,
+ "LDAP load done; terminating");
+ return (1);
+ default:
+ logmsg(MSG_NOTIMECHECK, LOG_INFO,
+ "LDAP load internal error; terminating");
+ return (-1);
+ }
+ }
+
+ /*
+ * If the value of the cookie has changed, we've managed
+ * to move on to another mapping, so we reset the retry
+ * structures.
+ */
+ if (cookie != prevCookie) {
+ refresh = ldapDBTableMapping.refreshErrorRetry;
+ retrieve = ldapDBTableMapping.retrieveErrorRetry;
+ store = ldapDBTableMapping.storeErrorRetry;
+ }
+ }
+
+ /* NOTREACHED */
+ return (-1);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_ib_proc.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_ib_proc.c
new file mode 100644
index 0000000000..09385ba96c
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_ib_proc.c
@@ -0,0 +1,1799 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 1990-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Ported from
+ * "@(#)nis_ib_proc.c 1.37 91/03/21 Copyr 1990 Sun Micro";
+ *
+ * nis_ib_proc.c
+ *
+ * This module contains information base function of NIS Version 3
+ * 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 <syslog.h>
+#include <stdlib.h>
+#include <rpc/rpc.h>
+#include <rpc/clnt.h>
+#include <rpc/svc.h>
+#include <rpc/auth.h>
+#include <rpc/auth_des.h>
+#include <rpcsvc/nis.h>
+#include <rpcsvc/nis_callback.h>
+#include "nis_proc.h"
+#include "nisdb_mt.h"
+
+#include <string.h>
+#include <netdir.h>
+#include <netconfig.h>
+
+extern bool_t xdr_nis_result();
+extern bool_t xdr_nis_error();
+extern bool_t xdr_nis_object();
+extern void (*_svc_getreqset_proc)();
+
+#define NO_ENTRY_OBJS 96
+
+/*
+ * nis_censor_object[_attr]
+ *
+ * This function enforces the access rights policies for objects.
+ * It is called with a candidate ENTRY object and a principal name.
+ * The result is either NULL (object not readable) or a pointer to
+ * an object that has been "censored" by this code to remove sensitive
+ * columns or encrypt encrypted columns.
+ * nis_censor_object_attr adds a fix which ensures that
+ * users cannot infer information from the database by using special
+ * search criteria.
+ */
+nis_object *
+nis_censor_object_attr(o, tc, p, zn, za)
+ nis_object *o; /* The object to censor */
+ table_col *tc; /* Table column descriptor */
+ nis_name p; /* The principal in question */
+ uint_t zn; /* Number of attributes */
+ nis_attr *za; /* The search attributes */
+{
+ nis_object zo; /* our censored object */
+ int mc; /* Number of columns */
+ int i, j,
+ somedata;
+#define buf __nis_get_tsd()->censor_object_buf
+ entry_col *oec, /* The old columns pointer */
+ *ec; /* Some temporary buffer */
+
+
+ /* copy the object */
+ zo = *o;
+
+ mc = o->EN_data.en_cols.en_cols_len;
+ /* get some temporay space. */
+ ec = (entry_col *)nis_get_static_storage(&buf, sizeof (entry_col), mc);
+ if (! ec)
+ return (NULL);
+
+ /* Use our static version of the columns */
+ zo.EN_data.en_cols.en_cols_val = ec;
+
+ /* Get a pointer to the original columns */
+ oec = o->EN_data.en_cols.en_cols_val;
+ somedata = 0;
+ for (j = 0; j < mc; j++) {
+ ec[j] = oec[j]; /* copy the pointers */
+ ec[j].ec_flags = 0;
+ /* Transfer the flags values */
+ if (tc[j].tc_flags & TA_BINARY)
+ ec[j].ec_flags |= EN_BINARY;
+ if (tc[j].tc_flags & TA_XDR)
+ ec[j].ec_flags |= EN_XDR;
+#ifdef NIS_CRYPT_SUPPORT
+ if (tc[j].tc_flags & TA_CRYPT)
+ ec[j].ec_flags |= EN_CRYPT;
+#endif
+
+ /* Check to see if we can read this column */
+ if (! __can_do(NIS_READ_ACC, tc[j].tc_rights, o, p)) {
+ /* close inference holes */
+ for (i = 0; i < zn; i++)
+ if (za[i].zattr_ndx &&
+ (strcasecmp(tc[j].tc_name, za[i].zattr_ndx) == 0))
+ return (NULL);
+ /* Replace with No Permission value */
+ ec[j].ENLEN = 5;
+ ec[j].ENVAL = "*NP*";
+ } else {
+ somedata++;
+#ifdef NIS_CRYPT_SUPPORT
+ if (ec[j].ec_flags & EN_CRYPT) {
+ /* Encrypt the data using the session key */
+ /* XXX */
+ }
+#endif
+ }
+ }
+ if (somedata == NULL)
+ return (NULL);
+
+ return (nis_clone_object(&zo, 0));
+}
+
+#undef buf
+
+/*
+ * The original function (nis_censor_object) takes
+ * the old arguments, and passes them on with new placeholder arguments to
+ * the new function (nis_censor_object_attr). Only YP compat functions
+ * still call nis_censor_object.
+ */
+nis_object
+*nis_censor_object(o, tc, p)
+ nis_object *o; /* The object to censor */
+ table_col *tc; /* Table column descriptor */
+ nis_name p; /* The principal in question */
+{
+ return (nis_censor_object_attr(o, tc, p, 0, NULL));
+}
+
+/*
+ * This is the free function for the list results that are returned "enmasse"
+ * without a callback.
+ */
+static void
+free_rlist(entries)
+ nis_object *entries;
+{
+ XFREE(entries);
+}
+
+/*
+ * nis_return_list()
+ *
+ * This function converts a full list of entries returned by the database
+ * into a list that can be returned to the user. It has two main functions,
+ * #1) Remove those entries from the list which the client does not
+ * have read access too.
+ * #2) If the client has read access to the entry, censor those
+ * columns which the client does not have read access to.
+ */
+
+nis_object *
+nis_return_list(obj, list, num, p, got, ar, zn, za)
+ nis_object *obj; /* Information base object */
+ obj_list *list; /* Array of objects */
+ int num; /* Size of the array */
+ nis_name p; /* principal making reqst */
+ int *got; /* Number actually returned */
+ int ar; /* All readable flag */
+ uint_t zn; /* All readable flag */
+ nis_attr *za; /* The search attributes */
+{
+ register table_col *tc; /* Table column pointer */
+ nis_object *eo; /* Temporary object */
+ nis_object *entries; /* our result pointer */
+ int i, /* Temporary counters */
+ cur_entry; /* The current entry */
+
+ *got = 0;
+ /* Get some parameters from the table object */
+ if (__type_of(obj) == NIS_TABLE_OBJ)
+ tc = obj->TA_data.ta_cols.ta_cols_val;
+ else
+ tc = tbl_prototype.ta_cols.ta_cols_val;
+
+ /* This may allocate more than we need but it is faster this way */
+ entries = (nis_object *)XCALLOC(num+1, sizeof (nis_object));
+ if (! entries)
+ return (NULL);
+
+ add_cleanup(free_rlist, (char *)entries, "nis_return_list array.");
+
+ if (entries == NULL)
+ return (NULL);
+
+ for (i = 0, cur_entry = 0; i < num; i++) {
+ if (! list[i].o)
+ continue;
+ if (ar ||
+ __can_do(NIS_READ_ACC, list[i].o->zo_access, list[i].o, p)) {
+ entries[cur_entry] = *list[i].o;
+ cur_entry++;
+ } else {
+ eo = nis_censor_object_attr(list[i].o, tc, p, zn, za);
+ if (eo) {
+ *(entries+cur_entry) = *eo;
+ add_cleanup(nis_destroy_object, eo,
+ "nis_censor_object_attr result");
+ cur_entry++;
+ }
+ }
+ }
+
+ *got = cur_entry;
+ return (entries);
+}
+
+/*
+ * __search_ok()
+ *
+ * This function does a sanity check on the request (which has attribute
+ * value pairs) and the table which defines the names of searchable columns.
+ * if an attribute is passed that doesn't match up to a column the request
+ * is rejected.
+ */
+static int
+__search_ok(zn, za, tobj)
+ uint_t zn; /* Number of attributes */
+ nis_attr *za; /* The search attributes */
+ nis_object *tobj; /* A Table object */
+{
+ register table_col *tc;
+ short acm[256]; /* XXX HARD LIMIT on columns XXX */
+ int i, j, mc;
+
+ if (__type_of(tobj) == NIS_TABLE_OBJ) {
+ mc = tobj->TA_data.ta_maxcol;
+ tc = tobj->TA_data.ta_cols.ta_cols_val;
+ } else {
+ /* Can't specify anything for directories */
+ return (zn == 0);
+ }
+ for (i = 0; i < zn; i++) {
+ acm[i] = -1; /* Entry column map */
+ for (j = 0; j < mc; j++) {
+ if ((tc[j].tc_flags & TA_SEARCHABLE) &&
+ (strcasecmp(tc[j].tc_name, za[i].zattr_ndx)
+ == 0)) {
+ if (acm[i] == -1)
+ acm[i] = j;
+ else
+ acm[i] = -2;
+ break;
+ }
+ }
+
+ /*
+ * If we didn't find a searchable column with the name,
+ * check to see if it is a multival column. If it is,
+ * then continue on to the other attributes. We don't
+ * update acm because multival columns can be specified
+ * multiple times.
+ */
+ if (acm[i] == -1) {
+ if (multival_check(tobj, &za[i], tc, mc))
+ continue;
+ }
+
+ /* Didn't match or matched twice */
+ if (acm[i] < 0) {
+ return (0);
+ }
+
+ }
+ return (1);
+}
+
+/*
+ * __nis_listinit()
+ *
+ * This function set's up either a list or first/next operation.
+ * If it is successful, it returns a pointer to the object to be
+ * listed (either a table or directory object), if it is unsuccessful
+ * it returns NULL and fills in the result structure appropriately.
+ */
+static nis_object *
+__nis_listinit(res, argp, princp)
+ nis_result *res;
+ ib_request *argp;
+ nis_name princp;
+{
+ nis_object *d_obj, *ib_obj = NULL;
+ struct ticks t;
+ nis_db_result *dbres;
+ nis_error xx;
+ enum name_pos p;
+
+ NIS_RES_NUMOBJ(res) = 0;
+ p = nis_dir_cmp(argp->ibr_name, __nis_rpc_domain());
+ if ((p != SAME_NAME) && (p != LOWER_NAME)) {
+ res->status = NIS_BADNAME;
+ return (NULL);
+ }
+
+ /* Make sure we serve this directory. */
+ xx = __directory_object(nis_domain_of(argp->ibr_name), &t, 0, &d_obj);
+ res->aticks = t.aticks;
+ res->dticks = t.dticks;
+ res->cticks = t.cticks;
+ res->zticks = t.zticks;
+
+ if ((xx != NIS_SUCCESS) && (xx != NIS_S_SUCCESS)) {
+ /*
+ * Hmm, we didn't find it. Maybe it _is_ our directory
+ * object.
+ */
+ xx = __directory_object(argp->ibr_name, &t, 0, &d_obj);
+ res->aticks += t.aticks;
+ res->dticks += t.dticks;
+ res->cticks += t.cticks;
+ res->zticks += t.zticks;
+ if ((xx != NIS_SUCCESS) && (xx != NIS_S_SUCCESS)) {
+ /* They really blew it */
+ syslog(LOG_INFO, "Can't find directory for %s",
+ argp->ibr_name);
+ res->status = xx;
+ return (NULL);
+ } else {
+ ib_obj = d_obj;
+ }
+ }
+
+ /* Now get the information base object from the database */
+ if (! ib_obj) {
+ dbres = db_lookup(argp->ibr_name);
+ res->dticks += dbres->ticks;
+ if (dbres->status == NIS_NOSUCHTABLE) {
+ /* This means table of directory does not exist. */
+ dbres->status = recover_from_no_table(d_obj,
+ nis_domain_of(argp->ibr_name));
+ /* for sure, argp->ibr_name will not be there */
+ if (dbres->status == NIS_SUCCESS)
+ dbres->status = NIS_NOTFOUND;
+ }
+ if (dbres->status != NIS_SUCCESS) {
+ if (dbres->status == NIS_NOTFOUND)
+ res->status = NIS_NOSUCHTABLE;
+ else
+ res->status = dbres->status;
+ res->zticks += __stop_clock(0);
+ return (NULL);
+ }
+ ib_obj = dbres->obj;
+ } else {
+ /*
+ * If its a directory, make sure we've got a database
+ * for it. (if not create one and synchronize)
+ */
+ nis_error status = db_find_table(argp->ibr_name);
+ switch (status) {
+ case NIS_SUCCESS:
+ break;
+ case NIS_NOSUCHTABLE:
+ res->status = recover_from_no_table(d_obj,
+ argp->ibr_name);
+ if (res->status != NIS_SUCCESS) {
+ return (NULL);
+ }
+ break;
+ default:
+ res->status = status;
+ return (NULL);
+ }
+ }
+
+ switch (__type_of(ib_obj)) {
+ case NIS_TABLE_OBJ:
+ case NIS_DIRECTORY_OBJ:
+ break;
+
+ case NIS_LINK_OBJ:
+ if (verbose)
+ syslog(LOG_INFO, "__nis_listinit: Object is a link.");
+ /*
+ * we return a partial result indicating that the LINK was
+ * found and the client can follow it if they choose to.
+ */
+ res->status = NIS_PARTIAL;
+ NIS_RES_NUMOBJ(res) = 1;
+ NIS_RES_OBJECT(res) = ib_obj;
+ return (NULL);
+
+ default:
+ /*
+ * XXX this hard codes the "kinds" of objects that can be
+ * information bases. This is not nice if we want to support
+ * user designed objects.
+ */
+ res->status = NIS_INVALIDOBJ;
+ if (verbose)
+ syslog(LOG_INFO,
+ "__nis_listinit: Nonsearchable object (type = %d).",
+ __type_of(ib_obj));
+ return (NULL);
+ }
+
+ /* Check to see is we have a valid search criteria */
+ if (!__search_ok(argp->ibr_srch.ibr_srch_len,
+ argp->ibr_srch.ibr_srch_val, ib_obj)) {
+ res->status = NIS_BADATTRIBUTE;
+ if (verbose)
+ syslog(LOG_INFO,
+ "__nis_listinit: Unparsable attribute failure.");
+ return (NULL);
+ }
+
+ res->status = NIS_SUCCESS;
+ return (ib_obj);
+}
+
+/*
+ * Does the callback part of iblist
+ */
+static nis_result *
+nis_iblist_callback(argp, pname, ib_obj, all_read, res)
+ ib_request *argp;
+ char *pname; /* caller's name */
+ nis_object *ib_obj;
+ int all_read; /* all readable flag */
+ nis_result *res;
+{
+ nis_error err;
+ nis_object *d_obj;
+ int pid = -1; /* Process ID of child making call back */
+ int i, queued;
+ CLIENT *cback = NULL; /* Callback client handle */
+ cback_data cbarg; /* Callback arguments */
+ /* An array of object pointers */
+ struct timeval tv; /* Timeout for callback */
+ nis_fn_result *fnr;
+ netobj cookie; /* Cookie storage for first/nxt */
+ enum clnt_stat status = RPC_SUCCESS;
+ nis_error result;
+ table_col *tc;
+ ulong_t flags;
+ char tblbuf[NIS_MAXNAMELEN * 2];
+ char *table;
+ nis_attr *a;
+ int na; /* number of regular attributes */
+ int nm; /* number of multival attributes */
+
+
+ /*
+ * Used to create the callback handle here. We now delay
+ * until we know that we actually intend to perform the callback.
+ */
+
+ table = internal_table_name(argp->ibr_name, tblbuf);
+ if (! table) {
+ res->status = NIS_BADNAME;
+ return (res);
+ }
+
+ na = argp->ibr_srch.ibr_srch_len;
+ a = argp->ibr_srch.ibr_srch_val;
+ err = multival_attr(ib_obj, a, &na, &nm);
+ if (err != NIS_SUCCESS) {
+ res->status = err;
+ return (res);
+ }
+
+ /*
+ * This will force the database to be loaded.
+ */
+ fnr = db_firstib(argp->ibr_name, na, a, FN_MANGLE+FN_NORAGS, table);
+
+ if (fnr->status != NIS_SUCCESS) {
+ if (fnr->status == NIS_NOTFOUND) {
+ res->status = NIS_PARTIAL;
+ res->objects.objects_val = ib_obj;
+ res->objects.objects_len = 1;
+ } else {
+ res->status = fnr->status;
+ res->objects.objects_val = NULL;
+ res->objects.objects_len = 0;
+ }
+ res->dticks += fnr->ticks;
+ res->zticks += __stop_clock(0);
+ XFREE(fnr);
+ fnr = NULL;
+ return (res);
+ }
+
+ if (verbose)
+ syslog(LOG_INFO, "Making callback handle to : %s",
+ argp->ibr_cbhost.ibr_cbhost_val[0].name);
+ if ((strcmp(pname, "nobody") == 0) || (secure_level < 2))
+ flags = ZMH_VC;
+ else
+ flags = ZMH_VC+ZMH_AUTH;
+
+ cback = nis_make_rpchandle(argp->ibr_cbhost.ibr_cbhost_val, 1,
+ CB_PROG, 1, flags, 16384, 16384);
+ /* If we couldn't create a client handle we're hosed */
+ if (! cback) {
+ syslog(LOG_WARNING, "Unable to create callback.");
+ res->status = NIS_NOCALLBACK;
+ res->zticks = __stop_clock(0);
+ XFREE(fnr);
+ fnr = NULL;
+ return (res);
+ }
+
+ /* Create a new thread to perform the callback */
+ {
+ pthread_t tid;
+ pthread_attr_t attr;
+ int stat;
+ callback_thread_arg_t *cbtarg;
+ int attrsize = na * sizeof (cbtarg->a[0]);
+
+ if ((cbtarg = calloc(1, sizeof (*cbtarg))) == 0) {
+ syslog(LOG_WARNING,
+ "nis_iblist_callback: memory allocation failed for %d bytes",
+ sizeof (*cbtarg));
+ auth_destroy(cback->cl_auth);
+ clnt_destroy(cback);
+ res->status = NIS_NOMEMORY;
+ res->zticks = __stop_clock(0);
+ XFREE(fnr);
+ return (res);
+ }
+ cbtarg->fnr = fnr;
+ cbtarg->ib_obj = nis_clone_object(ib_obj, 0);
+ if (na <= (sizeof (cbtarg->a)/sizeof (cbtarg->a[0]))) {
+ cbtarg->na = na;
+ } else {
+ syslog(LOG_WARNING,
+ "nis_iblist_callback: too many attributes; max %d expected, %d found",
+ (sizeof (cbtarg->a)/sizeof (cbtarg->a[0])), na);
+ cbtarg->na = sizeof (cbtarg->a)/sizeof (cbtarg->a[0]);
+#ifdef NIS_MT_DEBUG
+ abort();
+#endif /* NIS_MT_DEBUG */
+ }
+ memcpy(cbtarg->a, a, cbtarg->na * sizeof (cbtarg->a[0]));
+ cbtarg->nm = nm;
+ cbtarg->all_read = all_read;
+ strncpy(cbtarg->pname, pname, sizeof (cbtarg->pname));
+ cbtarg->pname[sizeof (cbtarg->pname) - 1] = '\0';
+ cbtarg->cbarg = cbarg;
+ cbtarg->cback = cback;
+ strncpy(cbtarg->cbhostname,
+ argp->ibr_cbhost.ibr_cbhost_val[0].name,
+ sizeof (cbtarg->cbhostname));
+ cbtarg->cbhostname[sizeof (cbtarg->cbhostname) - 1] = '\0';
+ strncpy(cbtarg->ibr_name,
+ argp->ibr_name, sizeof (cbtarg->ibr_name));
+ cbtarg->ibr_name[sizeof (cbtarg->ibr_name) - 1] = '\0';
+
+ (void) pthread_attr_init(&attr);
+ (void) pthread_attr_setdetachstate(&attr,
+ PTHREAD_CREATE_DETACHED);
+ if ((stat = pthread_create(&tid, &attr, callback_thread,
+ cbtarg)) == 0) {
+ if (verbose)
+ syslog(LOG_INFO,
+ "nis_iblist_callback: created callback thread %d", tid);
+ res->status = NIS_CBRESULTS;
+ } else {
+ if (verbose)
+ syslog(LOG_WARNING,
+ "nis_iblist_callback: callback thread create failed: %d",
+ stat);
+ res->status = NIS_TRYAGAIN;
+ }
+ res->zticks += __stop_clock(0);
+
+ (void) pthread_attr_destroy(&attr);
+
+ res->cookie.n_bytes = (char *)malloc(sizeof (anonid_t));
+ if (res->cookie.n_bytes == NULL) {
+ syslog(LOG_WARNING,
+ "Couldn't allocate memory for callback id");
+ /* what error to return in case of no memory */
+ res->status = NIS_NOMEMORY;
+ } else {
+ anonid_t anonid = tid;
+ add_cleanup((void (*)())XFREE,
+ (char *)res->cookie.n_bytes, "callback id");
+ res->cookie.n_len = sizeof (anonid);
+ memcpy(res->cookie.n_bytes, &anonid,
+ sizeof (anonid));
+ if (verbose)
+ syslog(LOG_INFO,
+ "returning, callback id = %d",
+ *((anonid_t *)res->cookie.n_bytes));
+ }
+ return (res);
+ }
+}
+
+
+/*
+ * __nis_alt_callback_server()
+ *
+ * Return a pointer to a new chunk of memory containing a copy of the
+ * org_endpoint argument, with the rpc_origin address substituted (merged,
+ * since the port number is retained) for the first endpoint whose family
+ * matches an NC_TPI_COTS netconfig entry.
+ *
+ * If (*uaddr != 0), it is assumed to point to a piece of memory of length
+ * uaddrlen, where the caller wants a copy of the uaddr.
+ *
+ * It is the responsibility of the caller to free() the returned endpoint
+ * structure, as well as the uaddr if (*uaddr == 0).
+ */
+endpoint *
+__nis_alt_callback_server(endpoint *org_endpoint,
+ uint_t count,
+ struct netbuf *rpc_origin,
+ char **uaddr,
+ int uaddrlen)
+{
+ endpoint *alt_endpoint;
+ uint_t size, i, j, ei;
+ struct netconfig *nc, *match = 0;
+ struct netbuf *taddr;
+ void *newaddr;
+ void *rpcaddr;
+ int addrlen;
+ sa_family_t af, rpcaf;
+ char *tmpuaddr;
+
+ /* Sanity check arguments */
+ if (org_endpoint == 0 || count == 0 || rpc_origin == 0) {
+ return (0);
+ }
+
+ /* We only know how to deal with IP */
+ rpcaf = ((struct sockaddr *)rpc_origin->buf)->sa_family;
+ if (rpcaf != AF_INET && rpcaf != AF_INET6) {
+ return (0);
+ }
+
+ /* Retrieve a netconfig entry for "inet" that matches an endpoint */
+ for (i = 0; i < count; i++) {
+ if ((nc = __nis_get_netconfig(&org_endpoint[i])) != 0 &&
+ nc->nc_semantics == NC_TPI_COTS_ORD) {
+ if (strcasecmp(nc->nc_protofmly, NC_INET) == 0 &&
+ rpcaf == AF_INET) {
+ af = AF_INET;
+ } else if (strcasecmp(
+ nc->nc_protofmly, NC_INET6) == 0 &&
+ rpcaf == AF_INET6) {
+ af = AF_INET6;
+ } else {
+ continue;
+ }
+ match = nc;
+ ei = i;
+ break;
+ }
+ }
+
+ /* Did we find one ? */
+ if (match == 0) {
+ return (0);
+ }
+
+ /*
+ * Allocate memory for the alternate endpoint array.
+ * Must be freed by caller, if we return successfully.
+ */
+ size = count * sizeof (endpoint);
+ if ((alt_endpoint = malloc(size)) == 0) {
+ return (0);
+ }
+
+ for (j = 0; j < count; j++) {
+ alt_endpoint[j] = org_endpoint[j];
+ }
+
+ /* Get the address suggested by the remote caller */
+ if ((taddr = uaddr2taddr(match, alt_endpoint[ei].uaddr)) == 0) {
+ free(alt_endpoint);
+ return (0);
+ }
+
+ /*
+ * The ugly part: merge port number and RPC source address.
+ * This requires knowledge about the internals of a netbuf.
+ */
+
+ if (af == AF_INET) {
+ struct in_addr dummy;
+ newaddr =
+ &((struct sockaddr_in *)taddr->buf)->sin_addr.s_addr;
+ rpcaddr =
+ &((struct sockaddr_in *)rpc_origin->buf)->sin_addr.s_addr;
+ addrlen = sizeof (dummy.s_addr);
+ } else {
+ struct in6_addr dummy;
+ newaddr =
+ &((struct sockaddr_in6 *)taddr->buf)->sin6_addr.s6_addr;
+ rpcaddr =
+ &((struct sockaddr_in6 *)rpc_origin->buf)->sin6_addr.s6_addr;
+ addrlen = sizeof (dummy.s6_addr);
+ }
+
+ /*
+ * If addresses are the same, there is no need to try an alternate
+ * address, so just pretend that we failed.
+ */
+ if (!memcmp(newaddr, rpcaddr, addrlen)) {
+ free(alt_endpoint);
+ netdir_free(taddr, ND_ADDR);
+ return (0);
+ }
+
+ /* Alternate address differs; worth trying */
+ memcpy(newaddr, rpcaddr, addrlen);
+ if ((tmpuaddr = taddr2uaddr(match, taddr)) == 0) {
+ free(alt_endpoint);
+ netdir_free(taddr, ND_ADDR);
+ return (0);
+ }
+
+ if (*uaddr == 0) {
+ *uaddr = tmpuaddr;
+ } else if (strlen(tmpuaddr) < uaddrlen) {
+ (void) strcpy(*uaddr, tmpuaddr);
+ free(tmpuaddr);
+ } else {
+ free(alt_endpoint);
+ netdir_free(taddr, ND_ADDR);
+ free(tmpuaddr);
+ return (0);
+ }
+
+ /* Clean up */
+ netdir_free(taddr, ND_ADDR);
+
+ alt_endpoint[ei].uaddr = *uaddr;
+
+ return (alt_endpoint);
+}
+
+/*
+ * nis_iblist, this function will take the search criteria passed,
+ * and pass it on to the database. If the callback structure is
+ * null then the results are simply returned, otherwise the server
+ * forks and and begins returning results one by one. In a multithreaded
+ * environment this is handled by a thread. The library will check
+ * a resource limit variable before forking.
+ */
+nis_result *
+nis_iblist_svc(argp, reqstp)
+ ib_request *argp;
+ struct svc_req *reqstp;
+{
+ nis_result *res = NULL;
+ nis_object *ib_obj,
+ *list;
+ int got;
+ char *s;
+ int all_read; /* all readable flag */
+ char pname[1024];
+ nis_db_list_result *ib_list;
+ int zn;
+ nis_attr *za;
+
+ __start_clock(0);
+ if (verbose)
+ syslog(LOG_INFO, "LIST_SVC: Listing table %s",
+ argp->ibr_name);
+
+ res = (nis_result *)XCALLOC(1, sizeof (nis_result));
+ add_cleanup((void (*)())XFREE, (char *)res, "iblist result");
+
+ /* If reqstp == NULL then we're recursing */
+ if (reqstp)
+ nis_getprincipal(pname, reqstp);
+ else
+ pname[0] = '\0';
+
+ ib_obj = __nis_listinit(res, argp, pname);
+
+ if (res->status != NIS_SUCCESS) {
+ res->zticks = __stop_clock(0);
+ return (res);
+ }
+ /* If we have read access on the table, we can read all of it */
+ all_read = __can_do(NIS_READ_ACC, ib_obj->zo_access, ib_obj, pname);
+
+ /*
+ * Now we actually do the list operation. If there is a callback,
+ * then we use the first/next operations to send the objects back
+ * one at a time.
+ */
+ if (argp->ibr_cbhost.ibr_cbhost_len) {
+ /* Process the callback request. */
+ /*
+ * The client 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.
+ */
+ {
+ struct netbuf *rpc_origin;
+ endpoint *org_endpoint, *alt_endpoint;
+ char *uaddr = 0;
+
+ org_endpoint =
+ argp->ibr_cbhost.ibr_cbhost_val->ep.ep_val;
+ rpc_origin = svc_getrpccaller(reqstp->rq_xprt);
+ if ((alt_endpoint = __nis_alt_callback_server(
+ org_endpoint,
+ argp->ibr_cbhost.ibr_cbhost_val->ep.ep_len,
+ rpc_origin,
+ &uaddr, 0)) != 0) {
+ argp->ibr_cbhost.ibr_cbhost_val->ep.ep_val =
+ alt_endpoint;
+ res = nis_iblist_callback(argp, pname, ib_obj,
+ all_read, res);
+ argp->ibr_cbhost.ibr_cbhost_val->ep.ep_val =
+ org_endpoint;
+ free(alt_endpoint);
+ free(uaddr);
+ if (res->status != NIS_NOCALLBACK) {
+ return (res);
+ }
+ }
+ }
+ return (nis_iblist_callback(argp, pname, ib_obj,
+ all_read, res));
+ }
+ /*
+ * If the client didn't ask for a callback, we process the
+ * entire list at once and return it. (implicit else clause)
+ */
+ ib_list = db_list(argp->ibr_name, argp->ibr_srch.ibr_srch_len,
+ argp->ibr_srch.ibr_srch_val);
+
+ res->dticks += ib_list->ticks;
+ if (ib_list->status != NIS_SUCCESS) {
+ if (ib_list->status == NIS_NOTFOUND) {
+ res->status = NIS_PARTIAL;
+ NIS_RES_OBJECT(res) = ib_obj;
+ NIS_RES_NUMOBJ(res) = 1;
+ } else {
+ if (__type_of(ib_obj) == NIS_TABLE_OBJ) {
+ s = "table";
+ } else
+ s = "directory";
+ syslog(LOG_ERR, "Unable to list %s '%s' err=%d",
+ s, argp->ibr_name, ib_list->status);
+ res->status = ib_list->status;
+ NIS_RES_OBJECT(res) = NULL;
+ NIS_RES_NUMOBJ(res) = 0;
+ }
+ res->zticks = __stop_clock(0);
+ if (verbose)
+ syslog(LOG_INFO, "nis_iblist_svc: return status %s",
+ nis_sperrno(res->status));
+ return (res);
+ }
+ /* Now process the list of objects we've got to return */
+ zn = argp->ibr_srch.ibr_srch_len;
+ za = argp->ibr_srch.ibr_srch_val;
+ list = nis_return_list(ib_obj, ib_list->objs, ib_list->numo, pname,
+ &got, all_read,
+ zn, za);
+
+ /* Construct the result */
+ if (got) {
+ res->status = NIS_SUCCESS;
+ NIS_RES_NUMOBJ(res) = got;
+ NIS_RES_OBJECT(res) = list;
+ } else {
+ res->status = NIS_NOTFOUND;
+ NIS_RES_NUMOBJ(res) = 0;
+ NIS_RES_OBJECT(res) = NULL;
+ }
+ res->zticks = __stop_clock(0);
+ return (res);
+}
+
+/*
+ * This is an internal Information Base operations function. It is collected
+ * here for clarity. It makes the actual database calls to manipulate the
+ * namespace.
+ */
+static void
+add_entry(name, zn, za, obj, tbl, flags, princp, xid, res)
+ nis_name name; /* Table name */
+ int zn; /* Number of attributes */
+ nis_attr *za; /* NIS+ attribute list. */
+ nis_object *obj, /* The entry we're adding */
+ *tbl; /* Table we're adding to */
+ ulong_t flags; /* Semantic flags */
+ nis_name princp; /* Principal making request */
+ int *xid; /* XID for transaction */
+ nis_result *res; /* Place for results. */
+{
+ nis_db_list_result *db;
+ nis_db_result *ars;
+ int add_ok, mod_ok;
+ time_t currtime;
+
+ /*
+ * Check to see if we can actually create an entry in this
+ * table.
+ */
+ add_ok = __can_do(NIS_CREATE_ACC, tbl->zo_access, tbl, princp);
+ mod_ok = __can_do(NIS_MODIFY_ACC, tbl->zo_access, tbl, princp);
+ if (auth_verbose) {
+ syslog(LOG_INFO,
+ "add_entry: creation is %s by the table.",
+ (add_ok) ? "ALLOWED" : "DISALLOWED");
+
+ }
+
+ db = db_list(name, zn, za);
+
+ *xid = begin_transaction(princp);
+ if (*xid == 0) {
+ res->status = NIS_TRYAGAIN;
+ return;
+ }
+
+ currtime = time(0); /* Make sure REMOVE/ADD has same time */
+ obj->zo_oid.ctime = (ulong_t)currtime;
+ obj->zo_oid.mtime = obj->zo_oid.ctime;
+ if (db->status == NIS_SUCCESS) {
+ if (db->numo != 1) {
+ res->status = NIS_NOTUNIQUE;
+ } else if ((flags & ADD_OVERWRITE) == 0) {
+ res->status = NIS_NAMEEXISTS;
+ } else if (! mod_ok &&
+ ! __can_do(NIS_MODIFY_ACC, db->objs[0].o->zo_access,
+ db->objs[0].o, princp)) {
+ res->status = NIS_PERMISSION;
+ } else {
+ nisdb_tsd_t *tsd;
+
+ /* Act like a modify on the OID */
+ obj->zo_oid.ctime = db->objs->o->zo_oid.ctime;
+
+ /*
+ * Tell the DB that we're doing a modify, so that
+ * it can merge the remove/add to a single LDAP
+ * update.
+ */
+ tsd = __nisdb_get_tsd();
+ tsd->doingModify = 1;
+
+ /* This updates the log */
+ db_remib(name, zn, za, db->objs, db->numo, tbl,
+ (ulong_t)currtime);
+ /* As does this, a virtual modify operation */
+ ars = db_addib(name, zn, za, obj, tbl);
+
+ tsd->doingModify = 0;
+
+ res->dticks += ars->ticks;
+ res->status = ars->status;
+ }
+ } else if (db->status == NIS_NOTFOUND) {
+ /* Ok, it doesn't exist so lets add it to the table */
+ if (! add_ok) {
+ res->status = NIS_PERMISSION;
+ return;
+ }
+
+ ars = db_addib(name, zn, za, obj, tbl);
+ if ((ars->status == NIS_SUCCESS) && (flags & RETURN_RESULT)) {
+ res->objects.objects_val = nis_clone_object(obj, NULL);
+ res->objects.objects_len = 1;
+ }
+ res->dticks += ars->ticks;
+ res->status = ars->status;
+ } else
+ res->status = db->status;
+}
+
+/*
+ * This is an internal Information Base operations function that implements
+ * the remove operation. It is collected here for clarity.
+ */
+static void
+remove_entry(name, zn, za, obj, tbl, flags, princp, xid, res)
+ nis_name name; /* Table name */
+ int zn; /* Number of attributes */
+ nis_attr *za; /* NIS attribute list. */
+ nis_object *obj, /* The entry we're removing */
+ *tbl; /* Table we're adding to */
+ ulong_t flags; /* Semantic flags */
+ nis_name princp; /* Principal making request */
+ int *xid; /* XID for transaction */
+ nis_result *res; /* Place for results. */
+{
+ nis_db_list_result *db;
+ nis_db_result *rrs;
+ int i, rem_ok;
+
+ /*
+ * Access control, if we have remove access granted at the
+ * table level then we can remove all entries.
+ */
+ rem_ok = __can_do(NIS_DESTROY_ACC, tbl->zo_access, tbl, princp);
+ if (auth_verbose) {
+ syslog(LOG_INFO,
+ "remove_entry: removal is %s by the table.",
+ (rem_ok) ? "ALLOWED" : "DISALLOWED");
+
+ }
+
+
+ db = db_list(name, zn, za);
+ res->dticks += db->ticks;
+
+ /*
+ * Check for errors, first to see if we actually found an
+ * object to remove.
+ */
+ if (db->status != NIS_SUCCESS) {
+ res->status = db->status;
+ return;
+ }
+
+ /*
+ * next to see if more than one object may be removed.
+ */
+ if ((db->numo > 1) && ((flags & REM_MULTIPLE) == 0)) {
+ res->status = NIS_NOTUNIQUE;
+ return;
+ }
+
+ /*
+ * Third to see if we need the same object to remove
+ */
+ if (obj && (! same_oid(obj, db->objs->o))) {
+ res->status = NIS_NOTSAMEOBJ;
+ return;
+ }
+
+ /*
+ * Fourth check to see if we have the right to remove
+ * these entrie(s)
+ */
+ if (! rem_ok) {
+ for (i = 0; i < db->numo; i++)
+ if (! __can_do(NIS_DESTROY_ACC,
+ db->objs[i].o->zo_access,
+ db->objs[i].o, princp))
+ break;
+ if (i < db->numo) {
+ res->status = NIS_PERMISSION;
+ return;
+ }
+ }
+
+ *xid = begin_transaction(princp);
+ if (*xid == 0) {
+ res->status = NIS_TRYAGAIN;
+ return;
+ }
+
+ /*
+ * Finally, remove the object in question.
+ */
+ rrs = db_remib(name, zn, za, db->objs, db->numo, tbl,
+ (ulong_t)(time(0)));
+ res->dticks += rrs->ticks;
+ res->status = rrs->status;
+}
+
+/*
+ * This is the internal Information base function that implements the modify
+ * operation. It is put here for clarity.
+ */
+static void
+modify_entry(name, zn, za, obj, tbl, flags, princp, xid, res)
+ nis_name name; /* Table name */
+ int zn; /* Number of attributes */
+ nis_attr *za; /* NIS attribute list. */
+ nis_object *obj, /* The entry we're removing */
+ *tbl; /* Table we're adding to */
+ ulong_t flags; /* Semantic flags */
+ nis_name princp; /* Principal making request */
+ int *xid; /* XID for transaction */
+ nis_result *res; /* Place for results. */
+{
+#define buf __nis_get_tsd()->modify_entry_buf
+ nis_db_list_result *db;
+ nis_db_result *rrs;
+ int i, mc;
+ entry_col *o_ec, /* Old entry columns */
+ *n_ec; /* New entry columns */
+ nis_object mobj; /* Modified object ... */
+ int clone; /* True if new object is a clone */
+ entry_col *mec; /* ... and it's columns */
+ int mod_ok;
+ table_col *tcol;
+ nis_db_list_result *o_db;
+ nis_attr *test_attr;
+ int na, mod_attrs = 0;
+ time_t currtime;
+ nisdb_tsd_t *tsd;
+
+ /*
+ * Set the global modify access bit for the objects by checking
+ * the table's access rights.
+ */
+ mod_ok = __can_do(NIS_MODIFY_ACC, tbl->zo_access, tbl, princp);
+
+ if (auth_verbose) {
+ syslog(LOG_INFO,
+ "modify_entry: modification is %s by the table.",
+ (mod_ok) ? "ALLOWED" : "DISALLOWED");
+
+ }
+
+ mc = tbl->TA_data.ta_maxcol;
+ tcol = tbl->TA_data.ta_cols.ta_cols_val;
+ mec = (entry_col *)nis_get_static_storage(&buf, sizeof (entry_col), mc);
+ if (! mec) {
+ res->status = NIS_NOMEMORY;
+ return;
+ }
+
+ db = db_list(name, zn, za);
+
+ /* If we didn't find the original just return the error */
+ if (db->status != NIS_SUCCESS) {
+ res->status = db->status;
+ return;
+ }
+
+ /* If the object isn't unique we can't modify it. */
+ if (db->numo > 1) {
+ res->status = NIS_NOTUNIQUE;
+ return;
+ }
+
+ clone = same_oid(obj, db->objs->o);
+
+ if ((flags & MOD_SAMEOBJ) && (! clone)) {
+ res->status = NIS_NOTSAMEOBJ;
+ return;
+ }
+
+ /*
+ * Now check the permissions on the object itself.
+ * If we don't have modify access to the table we check
+ * to see if we have modify access to the object itself,
+ * if that fails we check to see if we have modify access
+ * to the column we are modifying. If that fails we return
+ * a permission error.
+ */
+ o_ec = db->objs->o->EN_data.en_cols.en_cols_val;
+ n_ec = obj->EN_data.en_cols.en_cols_val;
+ if (! mod_ok)
+ mod_ok = __can_do(NIS_MODIFY_ACC, db->objs[0].o->zo_access,
+ db->objs[0].o, princp);
+ if (! mod_ok) {
+ for (i = 0; i < mc; i++)
+ if (((n_ec[i].ec_flags & EN_MODIFIED) != 0) &&
+ ! __can_do(NIS_MODIFY_ACC,
+ tcol[i].tc_rights,
+ db->objs[0].o, princp)) {
+ res->status = NIS_PERMISSION;
+ return;
+ }
+ }
+
+ /*
+ * Check the MOD_EXCLUSIVE flag. If a searchable column has been
+ * modified, the key index will be changed, which may conflict with
+ * an existing entry.
+ */
+
+ if (flags & MOD_EXCLUSIVE) {
+ /* Check for a searchable column that has been modified */
+ for (i = 0; i < mc; i++) {
+ if (mod_attrs = ((tcol[i].tc_flags & TA_SEARCHABLE) &&
+ (n_ec[i].ec_flags & EN_MODIFIED))) {
+ break;
+ }
+ }
+ /*
+ * If key modified, then create new attr list and see if
+ * it matches an existing entry
+ */
+ if (mod_attrs) {
+ test_attr = __get_attrs(mc);
+ if (test_attr == NULL) {
+ res->status = NIS_NOMEMORY;
+ return;
+ }
+ for (i = 0, na = 0; i < mc; i++) {
+ if (tcol[i].tc_flags & TA_SEARCHABLE) {
+ test_attr[na].zattr_ndx =
+ tcol[i].tc_name;
+ if ((n_ec[i].ec_flags & EN_MODIFIED) ==
+ 0) {
+ test_attr[na].ZAVAL = o_ec[i].ENVAL;
+ test_attr[na].ZALEN = o_ec[i].ENLEN;
+ } else {
+ test_attr[na].ZAVAL = n_ec[i].ENVAL;
+ test_attr[na].ZALEN = n_ec[i].ENLEN;
+ }
+ na++;
+ }
+ }
+ o_db = db_list(name, na, test_attr);
+ if (o_db->status == NIS_SUCCESS) {
+ res->status = NIS_NAMEEXISTS;
+ return;
+ }
+ }
+ }
+
+ mobj = *(db->objs->o);
+ currtime = time(0); /* Make sure REMOVE/ADD has same time */
+ mobj.zo_oid.mtime = (ulong_t)currtime;
+
+ /* Now check to see if the attributes need to change */
+ /*
+ * XXX this can fail silently when attributes change
+ * but mod_ok isn't set, we should figure out how to
+ * return a permission error. XXX
+ */
+ if (clone && mod_ok) {
+ mobj.zo_owner = obj->zo_owner;
+ mobj.zo_group = obj->zo_group;
+ mobj.zo_access = obj->zo_access;
+ mobj.zo_ttl = obj->zo_ttl;
+ }
+ mobj.EN_data.en_cols.en_cols_val = mec;
+
+ for (i = 0; i < mc; i++) {
+ if ((n_ec[i].ec_flags & EN_MODIFIED) == 0)
+ mec[i] = o_ec[i];
+ else
+ mec[i] = n_ec[i];
+ }
+
+ *xid = begin_transaction(princp);
+ if (*xid == 0) {
+ res->status = NIS_TRYAGAIN;
+ return;
+ }
+
+ /*
+ * If changes are written through to LDAP, we want to make
+ * just one LDAP update, not one remove and one add, which
+ * causes problems when more than one NIS+ table maps to
+ * one and the same LDAP container. Hence, inform the DB
+ * that this removal is part of a modify operation.
+ */
+ tsd = __nisdb_get_tsd(); /* Never returns NULL */
+ tsd->doingModify = 1;
+
+ /* Remove the old version of the entry */
+ rrs = db_remib(name, zn, za, db->objs, db->numo, tbl,
+ (ulong_t)currtime);
+ res->dticks += rrs->ticks;
+
+ if (res->status != NIS_SUCCESS)
+ syslog(LOG_ERR, "modify_entry: Unable to remove object %s",
+ name);
+ /*
+ * Now do the modify by overwriting the existing
+ * object.
+ */
+ rrs = db_addib(name, zn, za, &mobj, tbl);
+ if ((rrs->status == NIS_SUCCESS) && (flags & RETURN_RESULT)) {
+ res->objects.objects_val = nis_clone_object(&mobj, NULL);
+ res->objects.objects_len = 1;
+ }
+
+ /* Reset modification indication to the DB */
+ tsd->doingModify = 0;
+
+ res->dticks += rrs->ticks;
+ res->status = rrs->status;
+}
+
+#undef buf
+
+/*
+ * __nis_ibops()
+ * This is the common information base ops routine. It implements the
+ * various operations that can be done on an information base. It is
+ * analagous to the nameops() routine above.
+ */
+static nis_result *
+__nis_ibops(op, argp, princp)
+ int op;
+ ib_request *argp;
+ nis_name princp;
+{
+ nis_result *res;
+#define buf __nis_get_tsd()->__ibops_buf
+ nis_object *t_obj, *n_obj, *d_obj;
+ nis_attr *za;
+ int zn;
+ table_col *tc;
+ ulong_t ttime;
+ int i, mc, xid;
+ entry_col *ec;
+ nis_db_result *table;
+ struct ticks t;
+ char optxt[32];
+ nis_error xx;
+
+ res = (nis_result *)XCALLOC(1, sizeof (nis_result));
+ add_xdr_cleanup(xdr_nis_result, (char *)res, "ibops result");
+
+ if (readonly) {
+ res->status = NIS_TRYAGAIN;
+ return (res);
+ }
+
+ if (auth_verbose) {
+ switch (op) {
+ case ADD_OP :
+ strcpy(optxt, "ADD");
+ break;
+ case REM_OP :
+ strcpy(optxt, "REMOVE");
+ break;
+ case MOD_OP :
+ strcpy(optxt, "MODIFY");
+ break;
+ }
+ syslog(LOG_INFO, "Entry operation '%s' for principal %s",
+ optxt, princp);
+ }
+
+ /*
+ * Check to see if we serve the directory this table is in.
+ * NOTE: this could do a NIS+ lookup to the parent.
+ */
+ xx = __directory_object(nis_domain_of(argp->ibr_name), &t, TRUE,
+ &d_obj);
+ res->aticks = t.aticks;
+ res->zticks = t.zticks;
+ res->cticks = t.cticks;
+ res->dticks = t.dticks;
+ if (d_obj == NULL) {
+ syslog(LOG_ERR, "Couldn't locate directory object for %s",
+ argp->ibr_name);
+ res->status = NIS_NOT_ME; /* XXX should we use 'xx'? */
+ return (res);
+ }
+
+ /*
+ * Get the last update time. If the current time is earlier
+ * someone has set the clock back which is very bad as far
+ * as NIS+ is concerned. This will catch almost all possible
+ * situations except the single second when time has finally
+ * caught up with the last entry in the log. Fixing this
+ * would require more radical changes to the whole update
+ * mechanism which is potentially risky and may even require
+ * protocol changes.
+ */
+ if (last_update(d_obj->DI_data.do_name) > time(0)) {
+ syslog(LOG_ERR, "Update rejected because system time is "
+ "earlier than most recent log entry");
+ res->status = NIS_SYSTEMERROR;
+ return (res);
+ }
+
+ /*
+ * POLICY : Should we have to read the directory to
+ * read the table ?
+ * ANSWER : No, if we _know_ the name of the table we
+ * should be able to access it.
+ */
+
+ /* quick pointer to the "new" object */
+ if (argp->ibr_obj.ibr_obj_len)
+ n_obj = argp->ibr_obj.ibr_obj_val;
+ else
+ n_obj = NULL;
+
+ /* Get the table that we will be manipulating. */
+ table = db_lookup(argp->ibr_name);
+ res->dticks += table->ticks;
+ if (table->status != NIS_SUCCESS) {
+ res->status = table->status;
+ return (res);
+ }
+
+ t_obj = table->obj;
+ if (__type_of(t_obj) != NIS_TABLE_OBJ) {
+ res->status = NIS_INVALIDOBJ;
+ return (res);
+ }
+
+ if (! __search_ok(argp->ibr_srch.ibr_srch_len,
+ argp->ibr_srch.ibr_srch_val, t_obj)) {
+ res->status = NIS_BADATTRIBUTE;
+ return (res);
+ }
+
+ /* For ADD/MODify Make sure the types are the same */
+ if ((op == MOD_OP) || (op == ADD_OP)) {
+ if (! n_obj) {
+ res->status = NIS_BADOBJECT;
+ return (res);
+ }
+ }
+
+ /*
+ * For all operations, if there is an object, check that
+ * it matches the table.
+ */
+ if (n_obj) {
+ if (strcasecmp(n_obj->EN_data.en_type,
+ t_obj->TA_data.ta_type) != 0) {
+ res->status = NIS_TYPEMISMATCH;
+ return (res);
+ }
+
+ /* Make sure the number of columns are the same */
+ if (n_obj->EN_data.en_cols.en_cols_len !=
+ t_obj->TA_data.ta_maxcol) {
+ res->status = NIS_TYPEMISMATCH;
+ return (res);
+ }
+ }
+
+ /*
+ * Now we check the attribute list, doing some sanity checking
+ * and then go to the database.
+ */
+
+ tc = t_obj->TA_data.ta_cols.ta_cols_val; /* The Table columns */
+ mc = t_obj->TA_data.ta_maxcol; /* The total columns */
+ if (n_obj) /* The Entry columns */
+ ec = n_obj->EN_data.en_cols.en_cols_val;
+ else
+ ec = NULL;
+
+ if (argp->ibr_srch.ibr_srch_len) {
+ za = argp->ibr_srch.ibr_srch_val; /* The AVA list */
+ zn = argp->ibr_srch.ibr_srch_len; /* The current AVA */
+ } else if (ec) {
+ /* build a search criteria from the passed entry */
+ za = (nis_attr *)nis_get_static_storage(&buf, sizeof (nis_attr),
+ mc);
+ for (i = 0, zn = 0; i < mc; i++) {
+ if ((tc[i].tc_flags & TA_SEARCHABLE) &&
+ (ec[i].ec_value.ec_value_val)) {
+ za[zn].zattr_ndx = tc[i].tc_name;
+ za[zn].ZAVAL = ec[i].ENVAL;
+ za[zn].ZALEN = ec[i].ENLEN;
+ zn++;
+ }
+ }
+ } else {
+ za = NULL;
+ zn = 0;
+ }
+
+ xid = 0;
+
+ switch (op) {
+
+ case ADD_OP :
+ add_entry(argp->ibr_name, zn, za, n_obj, t_obj,
+ argp->ibr_flags, princp, &xid,
+ res);
+ break;
+ case REM_OP :
+ remove_entry(argp->ibr_name, zn, za, n_obj, t_obj,
+ argp->ibr_flags, princp, &xid,
+ res);
+ break;
+ case MOD_OP :
+ modify_entry(argp->ibr_name, zn, za, n_obj, t_obj,
+ argp->ibr_flags, princp, &xid,
+ res);
+ break;
+ }
+
+ if (verbose)
+ syslog(LOG_INFO, "ibops: operation completed.");
+ if (res->status == NIS_SUCCESS) {
+ end_transaction(xid);
+ ttime = last_update(d_obj->DI_data.do_name);
+ if (d_obj->DI_data.do_servers.do_servers_len > 1) {
+ RLOCK(ping_list);
+ add_pingitem(d_obj, ttime, &ping_list);
+ RULOCK(ping_list);
+ }
+ } else if (xid != 0)
+ abort_transaction(xid);
+
+ if (verbose)
+ syslog(LOG_INFO, "ibops: returning...");
+ return (res);
+}
+
+#undef buf
+
+/*
+ * nis_fnops()
+ * This is routine is common to the first and next functions below. Quite
+ * a bit of code is shared so this cuts down on the chance for errors.
+ * The first part is identical to the ibops function above but this
+ * routine needs to return different data so I didn't want to overload
+ * the return value of ibobs() above.
+ */
+static nis_result *
+__nis_fnops(op, argp, princp)
+ int op;
+ ib_request *argp;
+ nis_name princp;
+{
+ nis_object *r_obj, *t_obj, *ib_obj = NULL;
+ nis_fn_result *fnr;
+ table_col *tc;
+ int all_read = 0;
+ nis_result *res;
+ int zn;
+ nis_attr *za;
+
+ res = (nis_result *)XCALLOC(1, sizeof (nis_result));
+ if (! res) {
+ syslog(LOG_ERR, "rpc.nisd: Out of memory");
+ return (NULL);
+ }
+ add_cleanup((void (*)())XFREE, (char *)res, "fnops result");
+
+ ib_obj = __nis_listinit(res, argp, princp);
+
+ if (! ib_obj) {
+ res->zticks = __stop_clock(0);
+ return (res);
+ }
+
+ all_read = __can_do(NIS_READ_ACC, ib_obj->zo_access, ib_obj, princp);
+ if (op == FIRST_OP)
+ fnr = db_firstib(argp->ibr_name, argp->ibr_srch.ibr_srch_len,
+ argp->ibr_srch.ibr_srch_val, FN_MANGLE+FN_NORAGS, NULL);
+ else if (op == NEXT_OP) {
+ res->cookie.n_len = argp->ibr_cookie.n_len;
+ res->cookie.n_bytes = (char *)malloc(res->cookie.n_len);
+ memcpy(res->cookie.n_bytes,
+ argp->ibr_cookie.n_bytes, res->cookie.n_len);
+ fnr = db_nextib(argp->ibr_name, &(res->cookie),
+ FN_MANGLE+FN_NORAGS, NULL);
+ }
+
+ res->dticks += fnr->ticks;
+ if (fnr->status != NIS_SUCCESS) {
+ res->status = fnr->status;
+ XFREE(fnr);
+ return (res);
+ }
+
+ res->cookie = fnr->cookie;
+ res->status = fnr->status;
+ /*
+ * If we have read access to everything or the object then we're done.
+ */
+ if (all_read ||
+ __can_do(NIS_READ_ACC, fnr->obj->zo_access, fnr->obj, princp)) {
+ res->objects.objects_val = fnr->obj;
+ res->objects.objects_len = 1;
+ add_cleanup(nis_destroy_object, (char *)fnr->obj, "fnops obj");
+ add_cleanup((void (*)())XFREE, (char *)fnr->cookie.n_bytes,
+ "fnops descript");
+ XFREE(fnr);
+ return (res);
+ }
+
+ r_obj = fnr->obj;
+ XFREE(fnr); /* free the result structure */
+ if (__type_of(ib_obj) == NIS_TABLE_OBJ)
+ tc = ib_obj->TA_data.ta_cols.ta_cols_val;
+ else
+ tc = tbl_prototype.ta_cols.ta_cols_val;
+ do {
+ if (__can_do(NIS_READ_ACC, r_obj->zo_access, r_obj, princp))
+ t_obj = r_obj;
+ else {
+ zn = argp->ibr_srch.ibr_srch_len;
+ za = argp->ibr_srch.ibr_srch_val;
+ t_obj = nis_censor_object_attr(r_obj, tc, princp,
+ zn, za);
+ nis_destroy_object(r_obj);
+ }
+ if (t_obj) {
+ res->objects.objects_val = t_obj;
+ res->objects.objects_len = 1;
+ add_cleanup(nis_destroy_object, (char *)t_obj,
+ "fnops obj");
+ add_cleanup((void (*)())XFREE,
+ (char *)res->cookie.n_bytes,
+ "fnops descript");
+ return (res);
+ }
+
+ fnr = db_nextib(argp->ibr_name, &(res->cookie),
+ FN_MANGLE+FN_NORAGS, NULL);
+ res->dticks += fnr->ticks;
+ res->status = fnr->status;
+ if (res->status == NIS_SUCCESS)
+ res->cookie = fnr->cookie;
+ r_obj = fnr->obj;
+ XFREE(fnr);
+ } while (res->status == NIS_SUCCESS);
+
+ return (res);
+}
+
+/*
+ * nis_addib, this function adds an entry into an Information Base.
+ * the entry is not "visible" in the namespace, only as a component
+ * of the information base that contains it.
+ */
+nis_result *
+nis_ibadd_svc(argp, reqstp)
+ ib_request *argp;
+ struct svc_req *reqstp;
+{
+ nis_result *res;
+ char pname[1024];
+
+ __start_clock(0);
+ if (verbose)
+ syslog(LOG_INFO, "Entry ADD_SVC: to table %s", argp->ibr_name);
+ /* A quick and easy check... */
+ if ((argp->ibr_obj.ibr_obj_len == 1) &&
+ (__type_of(argp->ibr_obj.ibr_obj_val) != NIS_ENTRY_OBJ)) {
+ return (nis_make_error(NIS_INVALIDOBJ, 0, 0, 0,
+ __stop_clock(0)));
+ }
+ nis_getprincipal(pname, reqstp);
+ res = __nis_ibops(ADD_OP, argp, pname);
+ if (verbose)
+ syslog(LOG_INFO, "Done, exit status %s",
+ nis_sperrno(res->status));
+ res->zticks += __stop_clock(0);
+ return (res);
+}
+
+/*
+ * nis_ibmodify, this function modifys an entry in the information
+ * base by changing the columns that are marked as modified in the
+ * passed entry. Permission checking is done here as well.
+ */
+nis_result *
+nis_ibmodify_svc(argp, reqstp)
+ ib_request *argp;
+ struct svc_req *reqstp;
+{
+ char pname[1024];
+ nis_result *res;
+
+ __start_clock(0);
+ if (verbose)
+ syslog(LOG_INFO, "Entry MODIFY_SVC: to table %s",
+ argp->ibr_name);
+ /* A quick and easy check... */
+ if ((argp->ibr_obj.ibr_obj_len == 1) &&
+ (__type_of(argp->ibr_obj.ibr_obj_val) != NIS_ENTRY_OBJ)) {
+ return (nis_make_error(NIS_INVALIDOBJ, 0, 0, 0,
+ __stop_clock(0)));
+ }
+ nis_getprincipal(pname, reqstp);
+ res = __nis_ibops(MOD_OP, argp, pname);
+ if (verbose)
+ syslog(LOG_INFO, "Done, exit status %s",
+ nis_sperrno(res->status));
+ res->zticks += __stop_clock(0);
+ return (res);
+}
+
+/*
+ * nis_remove, delete an entry from the indicated information base.
+ */
+nis_result *
+nis_ibremove_svc(argp, reqstp)
+ ib_request *argp;
+ struct svc_req *reqstp;
+{
+ char pname[1024];
+ nis_result *res;
+
+ __start_clock(0);
+ if (verbose)
+ syslog(LOG_INFO, "Entry REMOVE_SVC: to table %s",
+ argp->ibr_name);
+ /* A quick and easy check... */
+ if ((argp->ibr_obj.ibr_obj_len == 1) &&
+ (__type_of(argp->ibr_obj.ibr_obj_val) != NIS_ENTRY_OBJ)) {
+ return (nis_make_error(NIS_INVALIDOBJ, 0, 0, 0,
+ __stop_clock(0)));
+ }
+ nis_getprincipal(pname, reqstp);
+ res = __nis_ibops(REM_OP, argp, pname);
+ if (verbose)
+ syslog(LOG_INFO, "Done, exit status %s",
+ nis_sperrno(res->status));
+ res->zticks += __stop_clock(0);
+ return (res);
+}
+
+/*
+ * nis_ibfirst()
+ * This function will return the first entry in an information base.
+ * It is provided primarily for backward compatibility with YP and its
+ * use is not encouraged.
+ */
+nis_result *
+nis_ibfirst_svc(argp, reqstp)
+ ib_request *argp;
+ struct svc_req *reqstp;
+{
+ char pname[1024];
+ nis_result *res;
+
+
+ __start_clock(0);
+ if (verbose)
+ syslog(LOG_INFO, "Entry FIRST_SVC : Fetch from table %s",
+ argp->ibr_name);
+ nis_getprincipal(pname, reqstp);
+ res = __nis_fnops(FIRST_OP, argp, pname);
+ if (verbose)
+ syslog(LOG_INFO, "Done, exit status %s",
+ nis_sperrno(res->status));
+ res->zticks += __stop_clock(0);
+ return (res);
+}
+
+/*
+ * nis_ibnext, return a subsequent entry in the information base.
+ */
+nis_result *
+nis_ibnext_svc(argp, reqstp)
+ ib_request *argp;
+ struct svc_req *reqstp;
+{
+ char pname[1024];
+ nis_result *res;
+
+ __start_clock(0);
+ if (verbose)
+ syslog(LOG_INFO, "Entry NEXT_SVC : Fetch from table %s",
+ argp->ibr_name);
+ nis_getprincipal(pname, reqstp);
+ res = __nis_fnops(NEXT_OP, argp, pname);
+ if (verbose)
+ syslog(LOG_INFO, "Done, exit status %s",
+ nis_sperrno(res->status));
+ res->zticks += __stop_clock(0);
+ return (res);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_log_common.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_log_common.c
new file mode 100644
index 0000000000..001f4ec599
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_log_common.c
@@ -0,0 +1,1318 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+
+/*
+ * nis_log_common.c
+ *
+ * This module contains logging functions that are common to the service and
+ * the log diagnostic utilities.
+ */
+
+#include <syslog.h>
+#include <sys/fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <rpc/types.h>
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpcsvc/nis.h>
+#include <limits.h>
+#include <string.h>
+#include "nis_svc.h"
+#include "nis_proc.h"
+#include "log.h"
+#include "nisdb_mt.h"
+#include "ldap_util.h"
+
+extern void __db_disallowLDAP();
+extern void __db_allowLDAP();
+
+/* string guard, it is always safe to print nilptr(char_pointer) */
+#define nilptr(s) ((s) ? (s) : "(nil)")
+
+#define getpagesize() sysconf(_SC_PAGESIZE)
+
+static int pagesize = 0;
+int in_checkpoint = FALSE;
+int need_checkpoint = 0;
+extern NIS_HASH_TABLE old_stamp_list;
+extern void add_updatetime();
+#define invalid_directory (__nis_get_tsd()->invalid_directory)
+
+static log_upd *last_upd_p; /* tmp ptr used during delta updates */
+static ulong_t upd_cnt; /* tmp cntr used during delta updates */
+
+/*
+ * variable used to assure that nisping warnings both do not flood the log
+ * and also do get sent out periodically (moved to nis_log_common.c).
+ */
+long next_warntime = 0l;
+
+#define CHKPT_WARN_INTERVAL 3600 /* 1 hour interval in seconds */
+
+extern int verbose;
+int __nis_logfd = -1;
+log_hdr *__nis_log = NULL;
+ulong_t __nis_filesize = FILE_BLK_SZ;
+ulong_t __nis_logsize;
+ulong_t __maxloglen = MAXLOGLEN;
+ulong_t __loghiwater = HIWATER;
+
+pid_t master_pid = 0; /* Master PROCESS id */
+ulong_t cur_xid;
+nis_name cur_princp;
+
+/* data and routines for the updatetime cache begin here */
+/*
+ * updatetime_cache is a cache copy of the update timestamp for all
+ * the directories served by this server. It is kept in sync with the
+ * log file by adding time stamps in end_transaction(), and rebuilding
+ * the cache when checkpoint_log has finished. Using the cache is
+ * vital in readonly children, as the log file may be inconsistant. It
+ * is an optimization only otherwise.
+ */
+
+NIS_HASH_TABLE *updatetime_cache = NULL;
+
+void init_updatetime(void); /* forward */
+
+/*
+ * Purges the update timestamp cache
+ * Frees everything, and sets updatetime_cache to NULL.
+ */
+static void
+purge_updatetime(void)
+{
+ LOCK_LIST(updatetime_cache, "purge_updatetime(updatetime_cache)");
+
+ if (updatetime_cache != NULL) {
+ /* clean up the timestamp cache */
+ stamp_item *si;
+ while (si = (stamp_item *)nis_pop_item(updatetime_cache)) {
+ if (si->item.name)
+ free(si->item.name);
+ free(si);
+ }
+ free(updatetime_cache);
+ updatetime_cache = NULL;
+ }
+
+ ULOCK_LIST(updatetime_cache, "purge_updatetime(updatetime_cache)");
+}
+
+static void
+add_updatetime(nis_name name, ulong_t utime)
+{
+ stamp_item *si;
+
+ if (updatetime_cache == NULL) {
+ init_updatetime();
+ if (updatetime_cache == NULL)
+ return;
+ }
+ si = (stamp_item *)(nis_find_item(name, updatetime_cache));
+ if (si) {
+ /* already cached - update the timestamp */
+ si->utime = utime;
+ __nis_release_item((NIS_HASH_ITEM *)si, updatetime_cache, -1);
+ return;
+ }
+ si = (stamp_item *)(XCALLOC(1, sizeof (stamp_item)));
+ if (! si) {
+ syslog(LOG_CRIT, "add_updatetime(): out of memory.");
+ purge_updatetime();
+ return;
+ }
+ si->item.name = (nis_name) XSTRDUP(name);
+ if (! si->item.name) {
+ syslog(LOG_CRIT, "add_updatetime(): out of memory.");
+ XFREE(si);
+ purge_updatetime();
+ return;
+ }
+ si->utime = utime;
+ if (!__nis_insert_item_mt((NIS_HASH_ITEM *)si, updatetime_cache, 0)) {
+ XFREE(si->item.name);
+ XFREE(si);
+ si = (stamp_item *)(nis_find_item(name, updatetime_cache));
+ if (si != 0) {
+ /*
+ * Apparently, another thread just created this item.
+ * Update the utime only if it increases.
+ */
+ if (utime > si->utime)
+ si->utime = utime;
+ __nis_release_item((NIS_HASH_ITEM *)si,
+ updatetime_cache, -1);
+ } else {
+ syslog(LOG_ERR,
+ "add_updatetime(): error inserting %ld for %s",
+ utime, name != 0 ? name : "<NIL>");
+ }
+ }
+}
+
+
+/*
+ * Routine to generate the cache copy of the update timestamp.
+ * This routine would be static, and we would rely on initializing the
+ * updatetime_cache when we tried to add_updatetime() to an empty cache,
+ * except that it can take some time to traverse the log, and the client
+ * could time out.
+ *
+ * We traverse the log from old to new, so that the newer updates will
+ * overwrite the older ones.
+ *
+ * Recursive calls would be possible if init_updatetime called
+ * add_updatetime, which ran out of memory, and freed the cache. Then
+ * the next call to add_updatetime from within init_updatetime would
+ * recursively call init_updatetime. If we run out of room building
+ * the cache, we just free everything and don't build it.
+ */
+static bool_t updating_cache = FALSE;
+void
+init_updatetime(void)
+{
+ log_upd *upd;
+
+ if (updating_cache)
+ return; /* won't do recursive updates */
+
+ LOCK_LIST(updatetime_cache, "init_updatetime(updatetime_cache)");
+
+ if (updatetime_cache != NULL) {
+ ULOCK_LIST(updatetime_cache,
+ "init_updatetime(updatetime_cache)");
+ purge_updatetime();
+ LOCK_LIST(updatetime_cache,
+ "init_updatetime(updatetime_cache)");
+ if (updatetime_cache != 0) {
+ /* OK, some other thread got in before us */
+ ULOCK_LIST(updatetime_cache,
+ "init_updatetime(updatetime_cache)");
+ return;
+ }
+ }
+
+ updatetime_cache = (NIS_HASH_TABLE *)
+ calloc(1, sizeof (NIS_HASH_TABLE));
+ if (! updatetime_cache) {
+ ULOCK_LIST(updatetime_cache,
+ "init_updatetime(updatetime_cache)");
+ syslog(LOG_CRIT, "add_updatetime(): out of memory.");
+ return;
+ }
+
+ ULOCK_LIST(updatetime_cache, "init_updatetime(updatetime_cache)");
+
+ upd = __nis_log->lh_head;
+
+ updating_cache = TRUE;
+
+ while (upd) {
+ add_updatetime(upd->lu_dirname, upd->lu_time);
+ upd = upd->lu_next;
+ }
+
+ /*
+ * We need the code below only when this file is being
+ * compiled for rpc.nisd or nislog. The NEED_DIROBJ flag is
+ * declared in the CCFLAGS for rpc.nisd and nislog.
+ */
+#ifdef NEED_DIROBJ
+ /*
+ * In general, if we're a replica and are mapping from LDAP,
+ * the last update time for a directory isn't necessarily
+ * equal to the master's notion of same. Hence, if available,
+ * we prefer UPD_STAMP:s.
+ *
+ * However, if we're the master, the UPD_STAMP isn't necessarily
+ * up to date, so we still use the last update time.
+ */
+ for (upd = __nis_log->lh_head; upd != 0; upd = upd->lu_next) {
+ XDR xdrs;
+ log_entry le;
+ nis_error err;
+ struct ticks ticks;
+ nis_object *obj = 0;
+
+ /*
+ * Check if we're the master. We do this by asking
+ * __directory_object_msg() for the object, with the
+ * 'is_master' flag set. If we don't get an object,
+ * we aren't the master.
+ */
+ err = __directory_object_msg(upd->lu_dirname, &ticks, 1, &obj,
+ 0);
+ if (err == NIS_SUCCESS && obj != 0)
+ continue;
+
+ xdrmem_create(&xdrs, (char *)upd->lu_data, upd->lu_size,
+ XDR_DECODE);
+ memset((char *)&le, 0, sizeof (le));
+ if (xdr_log_entry(&xdrs, &le)) {
+ if (le.le_type == UPD_STAMP)
+ add_updatetime(upd->lu_dirname, upd->lu_time);
+ xdr_free(xdr_log_entry, (char *)&le);
+ }
+ }
+#endif /* NEED_DIROBJ */
+
+ updating_cache = FALSE;
+}
+
+/* data and routines for the updatetime cache end here */
+
+void
+sync_header()
+{
+ if (! pagesize) /* msync misfeature */
+ pagesize = getpagesize();
+
+ ASSERTWHELD(translog);
+
+ if (msync((caddr_t)__nis_log, pagesize, MS_SYNC)) {
+ perror("msync:");
+ syslog(LOG_CRIT, "Unable to msync LOG HEADER.");
+ abort();
+ }
+}
+
+void
+sync_update(upd)
+ log_upd *upd;
+{
+ ulong_t start, end, len;
+ ulong_t size;
+
+ ASSERTWHELD(translog);
+
+ if (! pagesize) /* msync misfeature */
+ pagesize = getpagesize();
+ /* round start to the nearest page */
+ start = ((ulong_t)upd) & (~(pagesize-1));
+ /* round end to the nearest page */
+ end = (((ulong_t)upd) + XID_SIZE(upd) + (pagesize-1)) &
+ (~(pagesize-1));
+ size = end - start;
+ if (msync((caddr_t)start, size, MS_SYNC)) {
+ perror("msync:");
+ syslog(LOG_CRIT, "Unable to msync LOG UPDATE.");
+ abort();
+ }
+
+}
+
+/*
+ * call msync() on the entire transaction log -- used for fast delta
+ * updates.
+ */
+void sync_log() {
+ ulong_t start, end, len;
+ ulong_t size;
+
+ ASSERTWHELD(translog);
+
+ if (! pagesize) /* msync misfeature */
+ pagesize = getpagesize();
+ /* start at __nis_log */
+ start = (ulong_t)__nis_log;
+ /* round end to the nearest page */
+ end = (((ulong_t)__nis_log->lh_tail) + XID_SIZE(__nis_log->lh_tail)
+ + (pagesize-1)) & (~(pagesize-1));
+ size = end - start;
+ if (msync((caddr_t)start, size, MS_SYNC)) {
+ perror("msync:");
+ syslog(LOG_CRIT, "Unable to msync transaction log.");
+ abort();
+ }
+}
+
+/*
+ * Separate locking/unlocking of trans.log from begin_transaction et al.,
+ * so that we can lock the trans.log without incurring the overhead of
+ * begin_transaction until we really have to.
+ *
+ * If 'wr' is set, a write lock is requested, and in this case, the
+ * 'trylock' parameter is also honored (it's ignored for read locks).
+ */
+int
+lockTransLog(char *msg, int wr, int trylock) {
+ int lstat;
+
+ if (msg == 0)
+ msg = "lockTransLog";
+
+ if (wr) {
+ if (trylock)
+ lstat = TRYWLOCK(translog);
+ else
+ lstat = WLOCK(translog);
+ } else {
+ lstat = RLOCK(translog);
+ }
+ if (lstat != 0) {
+ if (lstat == EBUSY && trylock)
+ logmsg(MSG_NOTIMECHECK, LOG_INFO,
+ "%s: [%d] transaction log busy; try later",
+ msg, pthread_self());
+ else
+ logmsg(MSG_NOTIMECHECK, LOG_ERR,
+ "%s: transaction log lock error %d",
+ msg, lstat);
+ } else {
+ if (verbose)
+ logmsg(MSG_NOTIMECHECK, LOG_INFO,
+ "%s: [%d] holding transaction log %s lock",
+ msg, pthread_self(), wr ? "W" : "R");
+ }
+
+ return (lstat);
+}
+
+void
+unlockTransLog(char *msg, int wr) {
+
+ if (msg == 0)
+ msg = "unlockTransLog";
+
+ if (wr)
+ WULOCK(translog);
+ else
+ RULOCK(translog);
+
+ if (verbose)
+ logmsg(MSG_NOTIMECHECK, LOG_INFO,
+ "%s: [%d] released transaction log %s lock",
+ msg, pthread_self(), wr ? "W" : "R");
+}
+
+/*
+ * This function starts a logged transaction. When the function returns
+ * the log is in the "update" state. If the function returns 0 then
+ * the transaction couldn't be started (NIS_TRYAGAIN) is returned to
+ * the client. Otherwise it returns the XID that should be used in
+ * calls to add update.
+ */
+int
+begin_transaction(princp)
+ nis_name princp;
+{
+ int lstat;
+
+ /* XXX for threads we set up a writer lock */
+
+ if (lockTransLog("begin_transaction", 1, 1) != 0)
+ return (0);
+
+ /*
+ * Synchronization point. At this point we try to get
+ * exclusive access to the log. If this fails we return
+ * 0 so that the client can try again later.
+ */
+ if (in_checkpoint) {
+ unlockTransLog("begin_transaction", 1);
+ return (0);
+ }
+
+ if (__nis_log->lh_state != LOG_STABLE) {
+ unlockTransLog("begin_transaction", 1);
+ return (0);
+ }
+
+ cur_xid = __nis_log->lh_xid + 1;
+ cur_princp = princp;
+ __nis_log->lh_state = LOG_UPDATE;
+ sync_header();
+
+ /* Returning with write lock held */
+
+ return (cur_xid);
+}
+
+/*
+ * end_transaction
+ *
+ * This function does the actual commit, by setting the log header
+ * to be stable, and to have the latest XID present. Further it updates
+ * the time of the last update in the transaction to be 'ttime'. This
+ * is the time that is sent to the replicates so that they will know
+ * when they've picked up all of the updates up to this point.
+ *
+ * When the update was sourced from LDAP, we don't want to change the
+ * cached update time; hence the 'addUpdateTime' parameter.
+ */
+int
+end_transaction_x(int xid, int addUpdateTime) {
+
+ ASSERTWHELD(translog);
+
+ /* Check to see if add_update was called at all */
+ if (__nis_log->lh_tail->lu_xid != __nis_log->lh_xid) {
+ __nis_log->lh_xid++;
+ if (__nis_log->lh_xid != xid) {
+ syslog(LOG_CRIT, "end_transaction: Corrupted log!");
+ abort();
+ }
+ }
+ sync_update(__nis_log->lh_tail);
+
+ __nis_log->lh_state = LOG_STABLE;
+ sync_header();
+
+ /* update the updatetime cache */
+ if (addUpdateTime) {
+ add_updatetime(__nis_log->lh_tail->lu_dirname,
+ __nis_log->lh_tail->lu_time);
+ }
+
+ unlockTransLog("end_transaction", 1);
+
+ return (0);
+}
+
+int
+end_transaction(int xid) {
+ return (end_transaction_x(xid, 1));
+}
+
+/*
+ * Freeze the log tail and the log counter during fast updates.
+ */
+void begin_update() {
+ ASSERTWHELD(translog);
+ last_upd_p = __nis_log->lh_tail;
+ sync_header();
+ upd_cnt = __nis_log->lh_num;
+}
+
+/*
+ * Unfreeze the log tail and the log counter
+ */
+void end_update() {
+ ASSERTWHELD(translog);
+ __nis_log->lh_tail = last_upd_p;
+ last_upd_p = NULL;
+ __nis_log->lh_num = upd_cnt;
+ sync_header();
+}
+
+/*
+ * add_update()
+ *
+ * This function adds an update from the current transaction into
+ * the transaction log. It gets a bit tricky because it "allocates"
+ * all of it's memory from the file. Basically the transactions are
+ * laid down as follows :
+ *
+ * : prev update :
+ * +----------------+
+ * | log_upd header |
+ * +----------------+
+ * | XDR Encoded |
+ * : object from :
+ * | log_entry |
+ * +----------------+
+ * | Directory name |
+ * +----------------+
+ * : next update :
+ *
+ * The macro XID_SIZE calculates the offset to the start of the next
+ * update.
+ * returns 0 if the msync fails, otherwise it returns the log time.
+ *
+ * add_update is now a wrapper for the same code, but without
+ * msync's. replica_update calls add_update_nosync directly (the log tail and
+ * the log counter are then frozen); all others calls are to add_update, when
+ * the tail and the counter are not frozen.
+ */
+
+ulong_t
+add_update(log_entry *le) {
+ log_upd *upd;
+ ulong_t res;
+
+ ASSERTWHELD(translog);
+
+ __nis_logsize = (__nis_log->lh_tail) ? LOG_SIZE(__nis_log) :
+ sizeof (log_hdr);
+ last_upd_p = __nis_log->lh_tail;
+ upd_cnt = __nis_log->lh_num;
+ res = add_update_nosync(le, (void **)&upd);
+ sync_update(upd);
+ __nis_log->lh_tail = last_upd_p;
+ __nis_log->lh_num = upd_cnt;
+ sync_header();
+ return (res);
+}
+
+ulong_t
+add_update_nosync(le, updp)
+ log_entry *le;
+ void **updp;
+{
+ XDR xdrs;
+ log_upd *upd;
+ ulong_t upd_size, total_size;
+ int ret;
+
+ ASSERTWHELD(translog);
+
+ if (in_checkpoint)
+ abort(); /* Can't happen, because begin_trans would fail */
+
+ __nis_logsize = (__nis_log->lh_tail) ? __nis_logsize :
+ sizeof (log_hdr);
+
+ /* need both lines to find update size */
+ if (le->le_princp == 0)
+ le->le_princp = cur_princp;
+ upd_size = xdr_sizeof((xdrproc_t)xdr_log_entry, le);
+
+ /* see diagram above this function */
+ total_size = __nis_logsize + NISRNDUP(sizeof (log_upd) + upd_size +
+ strlen(le->le_object.zo_domain) + 1);
+ if (total_size >= __nis_filesize) {
+ __nis_filesize = ((total_size / FILE_BLK_SZ) + 1) * FILE_BLK_SZ;
+ ret = (int)lseek(__nis_logfd, __nis_filesize, SEEK_SET);
+ if (ret == -1) {
+ syslog(LOG_ERR,
+ "Cannot grow transaction log, error %s",
+ strerror(errno));
+ return (0);
+ }
+ ret = (int)write(__nis_logfd, "+", 1);
+ if (ret != 1) {
+ syslog(LOG_ERR,
+ "Cannot write one character to transaction log, error %s",
+ strerror(errno));
+ return (0);
+ }
+ }
+
+ __nis_logsize = total_size;
+ if (last_upd_p)
+ upd = (log_upd *)
+ (((ulong_t)last_upd_p) + XID_SIZE(last_upd_p));
+ else {
+ upd = (log_upd *)((ulong_t)(__nis_log)+
+ NISRNDUP(sizeof (log_hdr)));
+ }
+
+ if ((ulong_t)(upd) > ((ulong_t)(__nis_log) + __loghiwater)) {
+ struct timeval curtime;
+ if ((gettimeofday(&curtime, 0) != -1) &&
+ (curtime.tv_sec > next_warntime)) {
+ next_warntime = curtime.tv_sec + CHKPT_WARN_INTERVAL;
+ syslog(LOG_CRIT,
+ "NIS+ server needs to be checkpointed. Use \"nisping -C domainname\"");
+ }
+ }
+
+ /*
+ * If this operation is a table entry addition or removal, and the
+ * TSD 'doingModify' flag is set, use a special magic number.
+ */
+ if ((le->le_type == ADD_IBASE || le->le_type == REM_IBASE) &&
+ __nisdb_get_tsd()->doingModify) {
+ upd->lu_magic = LOG_UPD_MODMAG;
+ } else {
+ upd->lu_magic = LOG_UPD_MAGIC;
+ }
+
+ upd->lu_prev = last_upd_p;
+ upd->lu_next = NULL;
+ upd->lu_xid = cur_xid;
+ upd->lu_time = le->le_time;
+ upd->lu_size = upd_size;
+ xdrmem_create(&xdrs, (char *)upd->lu_data, upd->lu_size, XDR_ENCODE);
+ if (!xdr_log_entry(&xdrs, le)) {
+ syslog(LOG_ERR, "add_update: xdr_log_entry failed");
+ return (0);
+ }
+ upd->lu_dirname = (char *)(NISRNDUP((((ulong_t)upd) + sizeof (log_upd)
+ + upd->lu_size)));
+ strcpy(upd->lu_dirname, le->le_object.zo_domain);
+ if (updp)
+ *updp = upd;
+ if (last_upd_p)
+ last_upd_p->lu_next = upd;
+ else
+ __nis_log->lh_head = upd;
+ last_upd_p = upd;
+ upd_cnt++;
+ need_checkpoint = 1;
+ return (1);
+}
+
+/*
+ * make_stamp() - moved from nis_subr_proc.c to this file since
+ * nisbackup also needs this.
+ *
+ * This function adds a "null" entry into the log that indicates either
+ * the directory is stable or gone. When a directory is deleted, a tombstone
+ * (entry with a timestamp of 0) is written to the log. This prevents prior
+ * activity on the log from being confused with current activity. When a
+ * directory is resynchronized with a full dump, timestamp information is
+ * lost so this is used to mark the directory as being stable up to that point.
+ */
+void
+make_stamp(name, stime)
+ nis_name name;
+ ulong_t stime;
+{
+ log_entry le;
+ ulong_t xid;
+
+ memset((char *)&le, 0, sizeof (le));
+ le.le_princp = nis_local_principal();
+ le.le_time = stime;
+ le.le_type = UPD_STAMP;
+ le.le_name = name;
+ __type_of(&(le.le_object)) = NIS_NO_OBJ;
+ le.le_object.zo_name = "";
+ le.le_object.zo_owner = "";
+ le.le_object.zo_group = "";
+ le.le_object.zo_domain = name;
+ if (xid = begin_transaction(le.le_princp)) {
+ if (! add_update(&le))
+ fprintf(stderr, "make_stamp: could not add_update\n");
+ end_transaction(xid);
+ } else {
+ fprintf(stderr,
+ "make_stamp: zero xid from begin_transaction\n");
+ }
+}
+
+/*
+ * nis_cptime()
+ *
+ * This function will ask the indicated replicate for the last
+ * update it has seen to the given directory.
+ */
+ulong_t
+nis_cptime(replica, name)
+ nis_server *replica;
+ nis_name name;
+{
+ return (nis_cptime_msg(replica, name, TRUE, TRUE));
+}
+
+/*
+ * nis_cptime_msg()
+ *
+ * The guts of nis_cptime(). If "domsg" is FALSE, messages are suppressed,
+ * unless verbose mode is enabled.
+ */
+ulong_t
+nis_cptime_msg(replica, name, domsg, tryhard)
+ nis_server *replica;
+ nis_name name;
+ bool_t domsg;
+ bool_t tryhard;
+{
+ CLIENT *clnt;
+ enum clnt_stat status;
+ struct timeval tv;
+ ulong_t res;
+
+ clnt = nis_make_rpchandle(replica, 0, NIS_PROG, NIS_VERSION,
+ ZMH_DG|ZMH_AUTH|(tryhard ? 0 : ZMH_NOFALLBACK), 1024, 512);
+ /* If we can't contact it, return the safe answer */
+ if (! clnt) {
+ if (verbose)
+ syslog(LOG_INFO, "nis_cptime: could not contact %s",
+ replica->name);
+ return (0);
+ }
+
+ tv.tv_sec = 6; /* retry time out */
+ tv.tv_usec = 0;
+ clnt_control(clnt, CLSET_RETRY_TIMEOUT, (void *)&tv);
+ tv.tv_sec = 10;
+ status = clnt_call(clnt, NIS_CPTIME, xdr_nis_name, (char *)&name,
+ xdr_u_long, (char *)&res, tv);
+ if (status != RPC_SUCCESS && (domsg || verbose)) {
+ syslog(LOG_WARNING,
+ "nis_cptime: RPC error srv='%s', dir='%s', err='%s'",
+ replica->name, name, clnt_sperrno(status));
+ res = 0;
+ }
+ if (clnt->cl_auth != 0) {
+ auth_destroy(clnt->cl_auth);
+ clnt->cl_auth = 0;
+ }
+ clnt_destroy(clnt);
+ if (verbose)
+ syslog(LOG_INFO, "nis_cptime: returning %d for %s from %s",
+ res, name, replica->name);
+ return (res);
+}
+
+/*
+ * __make_name()
+ *
+ * This function prints out a nice name for a search entry.
+ */
+char *
+__make_name(le)
+ log_entry *le;
+{
+ static char namestr[2048];
+ int i;
+
+ if (le->le_attrs.le_attrs_len)
+ strcpy(namestr, "[ ");
+ else
+ namestr[0] = '\0';
+
+ for (i = 0; i < le->le_attrs.le_attrs_len; i++) {
+ strcat(namestr, le->le_attrs.le_attrs_val[i].zattr_ndx);
+ strcat(namestr, " = ");
+ if (le->le_attrs.le_attrs_val[i].zattr_val.zattr_val_len)
+ strcat(namestr,
+ le->le_attrs.le_attrs_val[i].zattr_val.zattr_val_val);
+ else
+ strcat(namestr, "(nil)");
+ strcat(namestr, ", ");
+ }
+
+ if (le->le_attrs.le_attrs_len) {
+ namestr[strlen(namestr) - 2] = '\0';
+ strcat(namestr, " ],");
+ }
+ strcat(namestr, le->le_name);
+ return (namestr);
+}
+
+/*
+ * __log_resync()
+ *
+ * This function will relocate the log to its current position in
+ * memory. Errors returned :
+ * 0 success
+ * -1 Illegal Update
+ * -2 Missing Data
+ * -3 Not enough updates
+ *
+ * Private flag (p):
+ * FNISD called from nisd
+ * FNISLOG called from nislog
+ * FCHKPT called from checkpoint_log
+ */
+int
+__log_resync(log, p)
+ log_hdr *log;
+ int p;
+{
+ log_upd *prev, *cur;
+ int i, ret;
+ ulong_t addr_p;
+
+ ASSERTWHELD(translog);
+
+ /*
+ * Resync the hard way. In this section of code we
+ * reconstruct the log pointers by calculating where
+ * the pieces would be placed. Our calcuation is
+ * verified by the presence of the appropriate MAGIC
+ * number at the address we've calculated. If any
+ * magic number isn't present, we note that the log
+ * is corrupt and exit the service. The user will
+ * have to either patch the log, or resync the slaves
+ * by hand. (Forced resync)
+ *
+ */
+
+ if (verbose)
+ syslog(LOG_INFO, "Resynchronizing transaction log.");
+
+ addr_p = NISRNDUP((ulong_t)log + sizeof (log_hdr));
+ prev = NULL;
+
+ /*
+ * if there are any transactions, this is the first one.
+ */
+ if (log->lh_num)
+ log->lh_head = (log_upd *)(addr_p);
+ if (verbose) {
+ syslog(LOG_INFO, "Log has %d transactions in it.", log->lh_num);
+ syslog(LOG_INFO, "Last valid transaction is %d.", log->lh_xid);
+ }
+
+ for (i = 0; i < log->lh_num; i++) {
+ cur = (log_upd *)(addr_p);
+ if (cur->lu_magic != LOG_UPD_MAGIC &&
+ cur->lu_magic != LOG_UPD_MODMAG) {
+ syslog(LOG_ERR,
+ "__log_resync: Transaction #%d bad magic number", i);
+ if (p != FNISLOG) {
+ syslog(LOG_ERR,
+"__log_resync: log truncated, resync to propagate possibly lost changes");
+ } else {
+ printf(
+ "__log_resync: Transaction #%d has a bad magic number\n", i);
+ }
+ break; /* major corruption */
+ } else if (NISRNDUP((ulong_t)cur +
+ sizeof (log_upd) + cur->lu_size) >=
+ ((ulong_t)log + __nis_filesize) ||
+ NISRNDUP((ulong_t)cur +
+ sizeof (log_upd) + cur->lu_size) <=
+ (ulong_t)cur) {
+/*
+ * Verify that lu_size seems reasonable by checking that the address to be used
+ * for cur->lu_dirname falls inside the mapped section from the transaction log.
+ * Note: Test for "<=", since cur+sizeof(log_upd)+cur->lu_size could overflow a
+ * u_long.
+ */
+ syslog(LOG_ERR,
+ "__log_resync: Transaction #%d: bad size %d",
+ i, cur->lu_size);
+ if (p != FNISLOG) {
+ syslog(LOG_ERR,
+"__log_resync: log truncated, resync to propagate possibly lost changes");
+ } else {
+ printf(
+ "__log_resync: Transaction #%d bad size %d\n", i, cur->lu_size);
+ }
+ break; /* also major corruption */
+ }
+
+ /* Fix up the link lists */
+ log->lh_tail = cur; /* Track the current update */
+ if (prev)
+ prev->lu_next = cur;
+ cur->lu_prev = prev;
+ cur->lu_next = NULL;
+ cur->lu_dirname = (char *)(NISRNDUP((((ulong_t)cur) +
+ sizeof (log_upd) +
+ cur->lu_size)));
+ prev = cur;
+
+ /* move to next update in the list */
+ if (verbose) {
+ syslog(LOG_INFO, "Resync'd transaction #%d.", i+1);
+ syslog(LOG_INFO, "Directory was '%s'.",
+ nilptr(cur->lu_dirname));
+ }
+ addr_p += XID_SIZE(cur);
+ }
+ if (i < log->lh_num) {
+ if (verbose)
+ syslog(LOG_INFO, "%d valid transactions.", i);
+ if (prev) {
+ if (prev->lu_xid > log->lh_xid) {
+ syslog(LOG_INFO,
+ "__log_resync: Incomplete last update transaction, removing.");
+ while (log->lh_tail->lu_xid > log->lh_xid) {
+ log->lh_tail = log->lh_tail->lu_prev;
+ i--;
+ if (! log->lh_tail)
+ break;
+ }
+ } else
+ log->lh_tail = prev;
+ log->lh_num = i;
+ } else {
+ log->lh_tail = NULL;
+ log->lh_num = 0;
+ }
+ }
+ __nis_logsize = (log->lh_tail) ? LOG_SIZE(log) : sizeof (log_hdr);
+ if (verbose)
+ syslog(LOG_INFO, "Log size is %d bytes", __nis_logsize);
+
+ if (p == FNISLOG) {
+ /* called from nislog, don't ftruncate() or msync() */
+ return (0);
+ } else if (p == FCHKPT) {
+ /* called from checkpoint, truncate transaction log file */
+ __nis_filesize = (((__nis_logsize / FILE_BLK_SZ) + 1) *
+ FILE_BLK_SZ);
+ ret = ftruncate(__nis_logfd, __nis_filesize);
+ if (ret == -1) {
+ syslog(LOG_ERR,
+ "__log_resync: Cannot truncate transaction log file");
+ return (-1);
+ }
+ ret = (int)lseek(__nis_logfd, 0L, SEEK_CUR);
+ if (ret == -1) {
+ syslog(LOG_ERR,
+ "__log_resync: cannot seek to begining of transaction log file");
+ return (-1);
+ }
+ ret = (int)lseek(__nis_logfd, __nis_filesize, SEEK_SET);
+ if (ret == -1) {
+ syslog(LOG_ERR,
+ "__log_resync: cannot increase transaction log file size");
+ return (-1);
+ }
+ ret = (int)write(__nis_logfd, "+", 1);
+ if (ret != 1) {
+ syslog(LOG_ERR,
+ "__log_resync: cannot write one byte to transaction log file");
+ return (-1);
+ }
+ }
+
+ log->lh_addr = log;
+ ret = msync((caddr_t)log, __nis_logsize, MS_SYNC);
+ /* Could take a while */
+ if (ret == -1) {
+ syslog(LOG_ERR, "msync() error in __log_resync()");
+ abort();
+ }
+ return (0);
+}
+
+/*
+ * map_log()
+ *
+ * This function maps in the logfile into the address space of thenis
+ * server process. After the log is successfully mapped it is "relocated"
+ * so that the pointers in the file are valid for the log's position in
+ * memory. Once relocated, the log is ready for use. This function returns
+ * 0 if the log file is OK and !0 if it is corrupted.
+ *
+ * Error numbers :
+ * -4 File not found.
+ * -5 Cannot MMAP file
+ * -6 Corrupt file, bad magic number in header.
+ * -7 Unknonwn (illegal) state value
+ */
+
+int
+map_log(logname, p)
+ char *logname;
+ int p; /* Private flag: */
+ /* FNISD=called from nisd */
+ /* FNISLOG=from nislog */
+{
+ int error, fd, ret;
+ log_upd *update;
+ log_hdr tmp_hdr;
+ log_entry *entry_1, *entry_2;
+ struct stat st;
+ long log_size;
+
+ if (lockTransLog("map_log", 1, 0) != 0)
+ return (-9);
+
+ sprintf(logname, "%s", LOG_FILE);
+ if (stat(logname, &st) == -1) {
+ if (p == FNISLOG) { /* called from nislog */
+ unlockTransLog("map_log", 1);
+ return (-4);
+ }
+
+ __nis_logfd = open(logname, O_RDWR+O_CREAT, 0600);
+ if (__nis_logfd == -1) {
+ syslog(LOG_ERR, "Unable to open logfile '%s'", logname);
+ unlockTransLog("map_log", 1);
+ return (-4);
+ }
+
+ /* Make this file as it doesn't exist */
+ ret = (int)lseek(__nis_logfd, 0L, SEEK_CUR);
+ if (ret == -1) {
+ syslog(LOG_ERR,
+ "map_log: cannot seek to begining of transaction log file");
+ unlockTransLog("map_log", 1);
+ return (-1);
+ }
+ ret = (int)lseek(__nis_logfd, __nis_filesize, SEEK_SET);
+ if (ret == -1) {
+ syslog(LOG_ERR,
+ "map_log: cannot increase transaction log file size");
+
+ unlockTransLog("map_log", 1);
+ return (-1);
+ }
+ /* writing a / all the time seemed silly */
+ ret = (int)write(__nis_logfd, "+", 1);
+ if (ret != 1) {
+ syslog(LOG_ERR,
+ "map_log: cannot write one byte to transaction log file");
+ unlockTransLog("map_log", 1);
+ return (-1);
+ }
+ fstat(__nis_logfd, &st);
+ } else { /* transaction log file exists */
+ __maxloglen = (st.st_size > __maxloglen) ? st.st_size :
+ __maxloglen;
+ __nis_logfd = open(logname, O_RDWR, 0600);
+ if (__nis_logfd == -1) {
+ syslog(LOG_ERR, "Unable to open logfile '%s'", logname);
+ unlockTransLog("map_log", 1);
+ return (-4);
+ }
+ }
+ __nis_filesize = st.st_size;
+
+
+ if (p == FNISLOG) {
+ /* called from nislog */
+ __nis_log = (log_hdr *)mmap(0, __nis_filesize,
+ PROT_READ+PROT_WRITE, MAP_PRIVATE, __nis_logfd, 0);
+ } else {
+ /* called from nisd */
+ __nis_log = (log_hdr *)mmap(0, __maxloglen,
+ PROT_READ+PROT_WRITE, MAP_SHARED, __nis_logfd, 0);
+ }
+
+ if ((int)(__nis_log) == -1) {
+ syslog(LOG_ERR,
+ "Unable to map logfile of length %ld into address space.",
+ __maxloglen);
+ close(__nis_logfd);
+ unlockTransLog("map_log", 1);
+ return (-5);
+ }
+
+ if (__nis_log->lh_magic != LOG_HDR_MAGIC) {
+ /* If it's NULL then we just created the file */
+ if (__nis_log->lh_magic == 0) {
+ (void) memset(__nis_log, 0, sizeof (log_hdr));
+ __nis_log->lh_state = LOG_STABLE;
+ __nis_log->lh_magic = LOG_HDR_MAGIC;
+ __nis_log->lh_addr = __nis_log;
+ if (p == FNISD)
+ msync((caddr_t)__nis_log, sizeof (log_hdr),
+ MS_SYNC);
+ } else {
+ syslog(LOG_ERR, "Illegal log file, remove and restart");
+ unlockTransLog("map_log", 1);
+ return (-6);
+ }
+
+ }
+ switch (__nis_log->lh_state) {
+ case LOG_STABLE :
+ if (verbose)
+ syslog(LOG_INFO, "Log state is STABLE.");
+ error = __log_resync(__nis_log, p);
+ if (error) {
+ unlockTransLog("map_log", 1);
+ return (error); /* resync failed */
+ }
+ break;
+ case LOG_RESYNC :
+ if (verbose)
+ syslog(LOG_INFO, "Log state is RESYNC.");
+ error = __log_resync(__nis_log, p);
+ if ((error) || (p == FNISLOG)) {
+ unlockTransLog("map_log", 1);
+ return (error);
+ }
+ __nis_log->lh_state = LOG_STABLE;
+ msync((caddr_t)__nis_log, sizeof (log_hdr), MS_SYNC);
+ break;
+ case LOG_UPDATE :
+ if (verbose)
+ syslog(LOG_INFO, "Log state is IN UPDATE.");
+ error = __log_resync(__nis_log, p);
+ if ((error) || (p == FNISLOG)) {
+ unlockTransLog("map_log", 1);
+ return (error);
+ }
+ update = __nis_log->lh_tail;
+ /*
+ * If the last update was a dir invalidator,
+ * a full resync will happen and restore
+ * consistancy.
+ */
+ if (update->lu_time == ULONG_MAX) {
+ invalid_directory = strdup(update->lu_dirname);
+ }
+ /*
+ * Check to see if the update was written to the
+ * log.
+ */
+ if (update->lu_xid == __nis_log->lh_xid) {
+ /* never made it, so we're done. */
+ __nis_log->lh_state = LOG_STABLE;
+ msync((caddr_t)__nis_log,
+ sizeof (log_hdr), MS_SYNC);
+ unlockTransLog("map_log", 1);
+ return (0);
+ }
+ if (update->lu_time == ULONG_MAX) {
+ unlockTransLog("map_log", 1);
+ return (0);
+ }
+ /* Back out the change */
+
+ /*
+ * We're just trying to clean the local DB, and hence
+ * don't want to use the LDAP repository.
+ */
+ __db_disallowLDAP();
+ ret = abort_transaction(0);
+ __db_allowLDAP();
+
+ /*
+ * Although begin_transaction() returns with the
+ * translog lock held, we're executing in a different
+ * process. Since abort_transaction() will unlock,
+ * we must not.
+ */
+ return (ret);
+ break;
+ case LOG_CHECKPOINT :
+ if (verbose)
+ syslog(LOG_INFO, "Log state is IN CHECKPOINT");
+ if (p == FNISLOG) {
+ error = __log_resync(__nis_log, p);
+ unlockTransLog("map_log", 1);
+ return (error);
+ }
+ strcpy(logname, nis_data(BACKUP_LOG));
+ fd = open(logname, O_RDONLY);
+ if (fd == -1) {
+ syslog(LOG_ERR, "map_log: missing backup.");
+ unlockTransLog("map_log", 1);
+ return (-1);
+ }
+ error = read(fd, &log_size, sizeof (long));
+ if (error == -1) {
+ syslog(LOG_ERR,
+ "map_log: unable to read backup");
+ close(fd);
+ unlockTransLog("map_log", 1);
+ return (-1);
+ }
+ error = read(fd, __nis_log, log_size);
+ close(fd);
+ if ((error == -1) || (error != log_size)) {
+ __nis_log->lh_state = LOG_CHECKPOINT;
+ syslog(LOG_ERR,
+ "map_log: Backup log truncated, fatal error.");
+ unlockTransLog("map_log", 1);
+ return (-1);
+ }
+ /* Manually resync the log */
+ error = __log_resync(__nis_log, p);
+ if (! error) {
+ __nis_log->lh_state = LOG_STABLE;
+ msync((caddr_t)__nis_log, sizeof (log_hdr),
+ MS_SYNC);
+ unlink(logname);
+ break;
+ }
+ syslog(LOG_ERR,
+ "map_log: Unable to resync after checkpoint restore.");
+ unlockTransLog("map_log", 1);
+ return (error);
+ break;
+ default :
+ syslog(LOG_ERR,
+ "Illegal log state, aborting.");
+ unlockTransLog("map_log", 1);
+ return (-7);
+ }
+ /* At this point everything should be cool. */
+ unlockTransLog("map_log", 1);
+ return (0);
+}
+
+
+/*
+ * This code is very similar to nis_name_of() in libnsl.
+ * When a server lives in its own domain, its database files are named
+ * relative to the parent domain (that is, where it's credentials are
+ * stored. This version of nis_name_of() will take that into account.
+ *
+ * This is probably not the best file to put this routine into, but
+ * it is shared by rpc.nisd, nisbackup, and nisrestore. All of these
+ * programs need a common routine to determine the name of a database file.
+ */
+
+/*
+ * relative_name()
+ * This internal function will remove from the NIS name, the domain
+ * name of the current server, this will leave the unique part in
+ * the name this becomes the "internal" version of the name. If this
+ * function returns NULL then the name we were given to resolve is
+ * bad somehow.
+ *
+ * A dynamically-allocated string is returned.
+ */
+
+nis_name
+relative_name(s)
+ char *s; /* string with the name in it. */
+{
+ char *d;
+ char *buf;
+ int dl, sl;
+ name_pos p;
+
+ if (s == NULL)
+ return (NULL);
+
+ d = __nis_rpc_domain();
+ if (d == NULL)
+ return (NULL);
+ dl = strlen(d); /* _always dot terminated_ */
+
+ buf = strdup(s);
+ if (buf == NULL)
+ return (NULL);
+ strcpy(buf, s); /* Make a private copy of 's' */
+ sl = strlen(buf);
+
+ if (dl == 1) { /* We're the '.' directory */
+ buf[sl-1] = '\0'; /* Lose the 'dot' */
+ return (buf);
+ }
+
+ p = nis_dir_cmp(buf, d);
+
+ /* 's' is above 'd' in the tree */
+ if ((p == HIGHER_NAME) || (p == NOT_SEQUENTIAL) || (p == SAME_NAME)) {
+ free(buf);
+ return (NULL);
+ }
+
+ /* Insert a NUL where the domain name starts in the string */
+ buf[(sl - dl) - 1] = '\0';
+
+ /* Don't return a zero length name */
+ if (buf[0] == '\0') {
+ free((void *)buf);
+ return (NULL);
+ }
+
+ return (buf);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_log_svc.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_log_svc.c
new file mode 100644
index 0000000000..40a7fdaf00
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_log_svc.c
@@ -0,0 +1,1076 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 1990-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * nis_log_svc.c
+ *
+ * This is the other part of the NIS+ logging facility. It contains functions
+ * used by the service. The other part, nis_log_common.c contains functions
+ * that are shared between the service and nislog the executable.
+ *
+ * These functions are implemented on top of the mmap primitives and
+ * will change when user level threads are available. Mostly they can benefit
+ * from having reader/writer locks in the log itself.
+ *
+ * The normal sequence of events is as follows :
+ * At boot time :
+ * map_log(SHARED);
+ *
+ * For each update :
+ * xid = begin_transaction(who);
+ * add_update(entry);
+ * ...
+ * Then on commit :
+ * end_transaction(xid);
+ * Else on abort :
+ * abort_transaction();
+ */
+
+#include <time.h>
+#include <syslog.h>
+#include <sys/fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <rpc/types.h>
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpcsvc/nis.h>
+#include <limits.h>
+#include "nis_proc.h"
+#include "log.h"
+#include "nis_mt.h"
+#include "nisdb_mt.h"
+
+u_long cur_xid;
+nis_name cur_princp;
+
+/*
+ * in_checkpoint, in_update, updatetime_cache moved to nis_log_common.c,
+ * extern'd here.
+ */
+extern NIS_HASH_TABLE *updatetime_cache;
+extern int in_checkpoint;
+extern int in_update;
+extern log_hdr *__nis_log;
+extern int __nis_logfd;
+extern unsigned long __maxloglen, __loghiwater, __nis_logsize, __nis_filesize;
+
+/*
+ * variable used to assure that nisping warnings both do not flood the log
+ * and also do get sent out periodically (moved to nis_log_common.c).
+ */
+extern long next_warntime;
+
+extern nis_object* get_root_object();
+
+/*
+ * abort_transaction()
+ *
+ * This function backs out a transaction in progress that was interrupted
+ * by a server crash or some other unrecoverable error.
+ * Note that the updates are backed out in reverse order from the way they
+ * were applied. This is currently not strictly required for correctness
+ * but will be required if a gang-modify operation is ever supported.
+ */
+int
+abort_transaction(n_xid)
+ int n_xid;
+{
+ log_hdr *log = __nis_log;
+ XDR xdrs;
+ u_char *data;
+ log_entry le;
+ log_upd *cur, *upd;
+ int error;
+ nis_error stat;
+ nis_db_result *dbres;
+ nis_db_list_result *lres;
+ u_long xid;
+ int prevWasModify, doingModify = 0;
+
+ ASSERTWHELD(translog);
+
+ /*
+ * Check to see if the update was written to the
+ * log.
+ */
+ upd = log->lh_tail;
+ xid = upd->lu_xid;
+ for (cur = upd; cur->lu_xid > log->lh_xid; cur = cur->lu_prev) {
+ xdrmem_create(&xdrs, (char *)cur->lu_data, cur->lu_size,
+ XDR_DECODE);
+ memset((char *)&le, 0, sizeof (le));
+ if (! xdr_log_entry(&xdrs, &le)) {
+ syslog(LOG_ERR,
+ "Unable to decode transaction in log! Data LOST");
+ continue;
+ }
+
+ prevWasModify = doingModify;
+ doingModify = cur->lu_magic == LOG_UPD_MODMAG;
+ /*
+ * Toggle the TSD 'doingModify' flag if necessary.
+ */
+ if (doingModify && !prevWasModify) {
+ __nisdb_get_tsd()->doingModify = 1;
+ } else if (!doingModify && prevWasModify) {
+ __nisdb_get_tsd()->doingModify = 0;
+ }
+
+ switch (le.le_type) {
+ case ADD_NAME :
+ dbres = db_lookup(le.le_name);
+ if (dbres->status == NIS_SUCCESS) {
+ stat = __db_remove(le.le_name,
+ dbres->obj);
+ if (stat != NIS_SUCCESS) {
+ syslog(LOG_CRIT,
+ "abort_transaction: Failed to remove '%s'.", le.le_name);
+ abort();
+ }
+ } else if (dbres->status != NIS_NOTFOUND) {
+ syslog(LOG_CRIT,
+ "abort_transaction: Internal database error (%d)", dbres->status);
+ abort();
+ }
+ /* Successfully backed it out */
+ break;
+
+ case REM_NAME :
+ dbres = db_lookup(le.le_name);
+ if (dbres->status == NIS_NOTFOUND) {
+ stat = __db_add(le.le_name,
+ &(le.le_object), 0);
+ if (stat != NIS_SUCCESS) {
+ syslog(LOG_CRIT,
+ "abort_transaction: Failed to add '%s'.", le.le_name);
+ abort();
+ }
+ } else if (dbres->status != NIS_SUCCESS) {
+ syslog(LOG_CRIT,
+ "abort_transaction: Internal database error.");
+ abort();
+ }
+ /* Successfully backed it out */
+ break;
+
+ /* Skip the new modified object */
+ case MOD_NAME_NEW :
+ break;
+
+ /* Add the old modified object over the new one */
+ case MOD_NAME_OLD :
+ dbres = db_lookup(le.le_name);
+ if (dbres->status != NIS_SUCCESS) {
+ syslog(LOG_CRIT,
+ "abort_transaction: Internal database error.");
+ abort();
+ }
+ if (! same_oid(dbres->obj, &le.le_object)) {
+ stat = __db_add(le.le_name,
+ &(le.le_object), 1);
+ if (stat != NIS_SUCCESS) {
+ syslog(LOG_CRIT,
+ "abort_transaction: Failed to unmodify '%s'.", le.le_name);
+ abort();
+ }
+ }
+ /* Successfully backed it out */
+ break;
+
+ case ADD_IBASE :
+ lres = db_list(le.le_name,
+ le.le_attrs.le_attrs_len,
+ le.le_attrs.le_attrs_val);
+ if (lres->status == NIS_SUCCESS) {
+ if (lres->numo > 1) {
+ syslog(LOG_CRIT,
+ "abort_transaction: Internal error, log entry corrupt (%s).",
+ __make_name(&le));
+ abort();
+ }
+ stat = __db_remib(le.le_name,
+ le.le_attrs.le_attrs_len,
+ le.le_attrs.le_attrs_val);
+ if (stat != NIS_SUCCESS) {
+ syslog(LOG_CRIT,
+ "abort_transaction: Failed to remove '%s'.", __make_name(&le));
+ abort();
+ }
+ } else if (lres->status != NIS_NOTFOUND) {
+ syslog(LOG_CRIT,
+ "abort_transaction: Internal database error.");
+ abort();
+ }
+ /* Successfully backed it out */
+ break;
+ case REM_IBASE :
+ lres = db_list(le.le_name,
+ le.le_attrs.le_attrs_len,
+ le.le_attrs.le_attrs_val);
+ if (lres->status == NIS_NOTFOUND) {
+ stat = __db_addib(le.le_name,
+ le.le_attrs.le_attrs_len,
+ le.le_attrs.le_attrs_val,
+ &(le.le_object));
+ if (stat != NIS_SUCCESS) {
+ syslog(LOG_CRIT,
+ "abort_transaction: Failed to re-add '%s'.", __make_name(&le));
+ abort();
+ }
+ } else if (lres->status == NIS_SUCCESS) {
+ if (lres->numo > 1) {
+ syslog(LOG_CRIT,
+ "abort_transaction: Internal error, log entry corrupt (%s).",
+ __make_name(&le));
+ abort();
+ }
+ } else {
+ syslog(LOG_CRIT,
+ "abort_transaction: Internal database error.");
+ abort();
+ }
+ break;
+ }
+ xdr_free((xdrproc_t)xdr_log_entry, (char *)&le);
+ /* back up one update */
+ log->lh_tail = cur->lu_prev;
+ }
+
+ if (log->lh_tail->lu_xid != log->lh_xid) {
+ unlockTransLog("abort_transaction", 1);
+ return (-1); /* Didn't back them all out! */
+ }
+
+ log->lh_state = LOG_STABLE;
+ sync_header();
+
+ unlockTransLog("abort_transaction", 1);
+
+ return (0);
+}
+
+NIS_HASH_TABLE stamp_list = NIS_HASH_TABLE_MT_INIT;
+NIS_HASH_TABLE old_stamp_list = NIS_HASH_TABLE_MT_INIT;
+
+static int
+add_stamp(name, utime)
+ nis_name name;
+ u_long utime;
+{
+ stamp_item *si;
+
+ si = (stamp_item *)(nis_find_item(name, &stamp_list));
+ if (si) {
+ if (si->utime < utime)
+ si->utime = utime;
+ __nis_release_item((NIS_HASH_ITEM *)si, &stamp_list, -1);
+ return (0);
+ }
+ si = (stamp_item *)(XCALLOC(1, sizeof (stamp_item)));
+ if (! si) {
+ syslog(LOG_CRIT, "add_stamp(): out of memory.");
+ abort();
+ }
+ si->item.name = (nis_name) XSTRDUP(name);
+ if (! si->item.name) {
+ syslog(LOG_CRIT, "add_stamp(): out of memory.");
+ abort();
+ }
+ si->utime = utime;
+ if (__nis_insert_item_mt((NIS_HASH_ITEM *)si, &stamp_list, 0) == 0) {
+ free(si->item.name);
+ free(si);
+ si = __nis_find_item_mt(name, &stamp_list, 0, 0);
+ /*
+ * If we can find it now, then __nis_insert_item_mt()
+ * probably failed because the item already existed, which
+ * is OK. If we can't find it, it's still possible that
+ * everything's fine (item didn't exist when we first tried
+ * to find it, had been created when we tried to insert, and
+ * then had been removed when we tried to find it again),
+ * so we just issue a warning.
+ */
+ if (si == 0) {
+ syslog(LOG_WARNING, "add_stamp(): unable to insert "
+ "item for \"%s\"", name);
+ }
+ }
+ return (1); /* first time we've seen it. */
+}
+
+static void
+add_old_stamp(name, utime, uxid)
+ nis_name name;
+ u_long utime;
+ u_long uxid;
+{
+ old_stamp_item *si;
+
+ si = (old_stamp_item *)(nis_find_item(name, &old_stamp_list));
+ if (si) {
+ if (si->utime < utime)
+ si->utime = utime;
+ __nis_release_item((NIS_HASH_ITEM *)si, &old_stamp_list, -1);
+ return;
+ }
+ si = (old_stamp_item *)(XCALLOC(1, sizeof (old_stamp_item)));
+ if (! si) {
+ syslog(LOG_CRIT, "add_old_stamp(): out of memory.");
+ abort();
+ }
+ si->item.name = (nis_name) XSTRDUP(name);
+ if (! si->item.name) {
+ syslog(LOG_CRIT, "add_old_stamp(): out of memory.");
+ abort();
+ }
+ si->utime = utime;
+ si->stamped = 0;
+ si->xid = uxid;
+ if (__nis_insert_item_mt((NIS_HASH_ITEM *)si, &old_stamp_list, 0) ==
+ 0) {
+ free(si->item.name);
+ free(si);
+ si = __nis_find_item_mt(name, &old_stamp_list, 0, 0);
+ /*
+ * If we can find it now, then __nis_insert_item_mt()
+ * probably failed because the item already existed, which
+ * is OK. If we can't find it, it's still possible that
+ * everything's fine (item didn't exist when we first tried
+ * to find it, had been created when we tried to insert, and
+ * then had been removed when we tried to find it again),
+ * so we just issue a warning.
+ */
+ if (si == 0) {
+ syslog(LOG_WARNING, "add_old_stamp(): unable to insert "
+ "item for \"%s\"", name);
+ }
+ }
+}
+
+
+/*
+ * This routine inserts the specified update timestamp for the specified
+ * directory into the new checkpointed data area. It tries to insert a
+ * timestamp with the xid specified. If this xid is less than or equal
+ * to the last xid appended to the new checkpointed data area, then
+ * increment the offset_xid and use the (last_xid + offset_xid) as the
+ * new xid for the timestamp entry.
+ */
+static void
+insert_chkpt_stamp(name, utime, xid, offset_xid, last_xid, addr_p)
+nis_name name; /* directory name */
+ulong utime; /* last update timestamp */
+ulong xid; /* last xid seen for this directory */
+ulong *offset_xid; /* xid offset which is only incremented */
+ /* when xid to be inserted is <= the last */
+ /* xid in the new checkpoint area. */
+ulong *last_xid; /* last xid for checkpointed transactions */
+ulong *addr_p; /* address pointer where the update */
+ /* timestamp should go. */
+{
+ XDR o_xdrs;
+ log_upd *o_upd;
+ log_entry to_le;
+ u_long o_upd_size, o_le_size;
+
+ ASSERTWHELD(translog);
+
+ if (name == NULL)
+ return;
+ memset((char *)&to_le, 0, sizeof (to_le));
+ to_le.le_princp = nis_local_principal();
+ to_le.le_time = utime;
+ to_le.le_type = UPD_STAMP;
+ to_le.le_name = name;
+ __type_of(&(to_le.le_object)) = NIS_NO_OBJ;
+ to_le.le_object.zo_name = "";
+ to_le.le_object.zo_owner = "";
+ to_le.le_object.zo_group = "";
+ to_le.le_object.zo_domain = name;
+ o_le_size = xdr_sizeof(xdr_log_entry, &to_le);
+ /* value 20 below is a random added for extra padding */
+ if ((o_upd = (log_upd *) malloc(o_le_size + sizeof (log_upd) + 20 +
+ strlen(to_le.le_object.zo_domain))) == NULL) {
+ syslog(LOG_CRIT, "insert_chkpt_stamp(): out of memory.");
+ abort();
+ }
+ o_upd->lu_magic = LOG_UPD_MAGIC;
+ o_upd->lu_prev = NULL;
+ o_upd->lu_next = NULL;
+ if (xid <= *last_xid) {
+ (*offset_xid)++;
+ o_upd->lu_xid = (*last_xid) + (*offset_xid);
+ } else
+ o_upd->lu_xid = xid;
+ *last_xid = o_upd->lu_xid;
+ o_upd->lu_time = utime;
+ o_upd->lu_size = o_le_size;
+ xdrmem_create(&o_xdrs, (char *)o_upd->lu_data, o_upd->lu_size,
+ XDR_ENCODE);
+ if (!xdr_log_entry(&o_xdrs, &to_le)) {
+ syslog(LOG_CRIT,
+ "insert_chkpt_stamp(): xdr_log_entry failed.");
+ abort(); }
+ o_upd->lu_dirname = (char *)(NISRNDUP((((u_long) o_upd) +
+ sizeof (log_upd) + o_upd->lu_size)));
+ strcpy(o_upd->lu_dirname, to_le.le_object.zo_domain);
+ o_upd_size = XID_SIZE(o_upd);
+ memmove((char *)(*addr_p), (char *)o_upd, o_upd_size);
+ *addr_p += o_upd_size;
+
+ if (verbose) {
+ syslog(LOG_INFO,
+ "checkpoint_log: tombstone xid %d, time %d (%s) created",
+ o_upd->lu_xid, o_upd->lu_time, o_upd->lu_dirname);
+ }
+ free(o_upd);
+}
+
+
+/*
+ * make_tombstone creates the timestamp (tombstone) for the name
+ * specified.
+ * If no old timestamp found in old_stamp_list, just stamp it with utime.
+ * If it's already stamped with an old timestamp don't stamp it again.
+ * If it's not already stamped with the old timestamp, stamp it with utime.
+ *
+ * make_tombstone will also remove the item from old_stamp_list.
+ */
+static void
+make_tombstone(name, utime)
+nis_name name;
+ulong utime;
+{
+ old_stamp_item *o_si;
+ log_entry le;
+ u_long xid;
+
+ ASSERTWHELD(translog);
+
+ o_si = (old_stamp_item *)(nis_find_item(name, &old_stamp_list));
+ if ((!o_si) || (!o_si->stamped)) {
+ if (verbose)
+ syslog(LOG_INFO,
+ "make_tombstone: making timestamp %d for %s",
+ utime, name);
+
+ /* set up the log entry */
+ memset((char *)&le, 0, sizeof (le));
+ le.le_princp = nis_local_principal();
+ le.le_time = utime;
+ le.le_type = UPD_STAMP;
+ le.le_name = name;
+ __type_of(&(le.le_object)) = NIS_NO_OBJ;
+ le.le_object.zo_name = "";
+ le.le_object.zo_owner = "";
+ le.le_object.zo_group = "";
+ le.le_object.zo_domain = name;
+ cur_xid = __nis_log->lh_xid + 1;
+ cur_princp = le.le_princp;
+ add_update(&le);
+ __nis_log->lh_xid++;
+ sync_header();
+ }
+ if (o_si) {
+ o_si = nis_remove_item(o_si->item.name, &old_stamp_list);
+ if (o_si != 0) {
+ if (o_si->item.name != 0)
+ free(o_si->item.name);
+ free(o_si);
+ }
+ }
+}
+
+
+/*
+ * checkpoint_log()
+ *
+ * This function removes all transactions up to the indicated time from
+ * the log file.
+ *
+ * It returns 0 on failure, 1 on success.
+ */
+
+int
+checkpoint_log()
+{
+ log_upd *cur, *nxt;
+ u_long addr_p, upd_size;
+ int error;
+ int fd, i, num = 0;
+ XDR xdrs;
+ log_entry le;
+ int first, ret;
+ stamp_item *si;
+ old_stamp_item *o_si;
+ char backup[1024];
+ ulong last_xid = 0, offset_xid = 0;
+
+ if (verbose)
+ syslog(LOG_INFO, "Checkpointing the log.");
+
+ if (lockTransLog("checkpoint_log", 1, 0) != 0)
+ return (0);
+
+ {
+ __nis_hash_item_mt *e;
+ while ((e = __nis_pop_item_mt(&stamp_list)) != 0) {
+ XFREE(e);
+ }
+ }
+
+ if (__nis_log->lh_state != LOG_STABLE) {
+ syslog(LOG_INFO,
+ "checkpoint_log: Unable to checkpoint, log unstable.");
+ unlockTransLog("checkpoint_log", 1);
+ return (0);
+ }
+
+ /* XXX This should be a spin locks for the transaction funcs */
+ in_checkpoint = TRUE;
+ strcpy(backup, nis_data(BACKUP_LOG));
+
+ fd = open(backup, O_WRONLY+O_SYNC+O_CREAT+O_TRUNC, 0600);
+ if (fd == -1) {
+ syslog(LOG_ERR,
+ "checkpoint_log: Unable to checkpoint, can't open backup log (%s).",
+ backup);
+ in_checkpoint = FALSE;
+ unlockTransLog("checkpoint_log", 1);
+ return (0);
+ }
+
+ /*
+ * Make a backup of the log in two steps, write the size of the log
+ * and then a copy of the log.
+ */
+ __nis_logsize = (__nis_log->lh_tail) ? LOG_SIZE(__nis_log) :
+ sizeof (log_hdr);
+ if (write(fd, &__nis_logsize, sizeof (long)) != sizeof (long)) {
+ syslog(LOG_ERR,
+ "checkpoint_log: Unable to checkpoint, disk full.");
+ close(fd);
+ unlink(backup);
+ in_checkpoint = FALSE;
+ unlockTransLog("checkpoint_log", 1);
+ return (0);
+ }
+ if (verbose)
+ syslog(LOG_INFO,
+ "checkpoint_log: Backup log %s created.", backup);
+ /*
+ * Now set the state to RESYNC so if we have to recover and read
+ * this back in, and we get screwed while reading it, we won't
+ * get into major trouble. This still leaves open one window :
+ * a) Start checkpoint
+ * b) Successfully backup to BACKUP
+ * c) Set log state to CHECKPOINT
+ * c) crash.
+ * d) Reboot and start reading in the backup log
+ * e) crash.
+ * f) reboot and now the log appears to be resync'ing without
+ * all of its data.
+ */
+ __nis_log->lh_state = LOG_RESYNC;
+ if (write(fd, __nis_log, __nis_logsize) != __nis_logsize) {
+ syslog(LOG_ERR,
+ "checkpoint_log: Unable to checkpoint, disk full.");
+ close(fd);
+ unlink(backup);
+ __nis_log->lh_state = LOG_STABLE;
+ sync_header();
+ in_checkpoint = FALSE; /* Unlock */
+ unlockTransLog("checkpoint_log", 1);
+ return (0);
+ }
+ if (verbose)
+ syslog(LOG_INFO, "checkpoint_log: Backup log %s written.",
+ backup);
+ close(fd);
+
+ /* If we crash here we're ok since the log hasn't changed. */
+ __nis_log->lh_state = LOG_CHECKPOINT;
+ sync_header();
+
+ addr_p = (u_long)(__nis_log->lh_head);
+ /*
+ * If this transaction hasn't been fully replicated then
+ * leave it in the log. Othersize don't bother moving it.
+ */
+ if (verbose)
+ syslog(LOG_INFO, "checkpoint_log: Checkpointing ...");
+
+ for (cur = __nis_log->lh_head, num = 0; cur; cur = nxt) {
+ nxt = cur->lu_next;
+ xdrmem_create(&xdrs, (char *)cur->lu_data, cur->lu_size,
+ XDR_DECODE);
+ memset((char *)&le, 0, sizeof (log_entry));
+ if (!xdr_log_entry(&xdrs, &le)) {
+ syslog(LOG_ERR,
+ "checkpoint_log: cannot read transaction log entry.");
+ /* free up any malloc'd storage */
+ xdr_free(xdr_log_entry, (char *)&le);
+ unlockTransLog("checkpoint_log", 1);
+ return (0);
+ }
+ /* ignore dummy entries used by delta-update */
+ if (cur->lu_time == ULONG_MAX) {
+ xdr_free(xdr_log_entry, (char *)&le);
+ continue;
+ }
+ first = add_stamp(cur->lu_dirname, cur->lu_time);
+ if (! nis_isstable(&le, first)) {
+ /* check the old stamp list */
+ o_si = (old_stamp_item *)
+ (nis_find_item(cur->lu_dirname,
+ &old_stamp_list));
+ if ((o_si != NULL) && (o_si->stamped == 0)) {
+ /* insert the old timestamp */
+ insert_chkpt_stamp(o_si->item.name, o_si->utime,
+ o_si->xid, &offset_xid, &last_xid,
+ &addr_p);
+ o_si->stamped = 1;
+ num++;
+ } else if ((o_si == NULL) &&
+ (le.le_type == UPD_STAMP)) {
+ /*
+ * if the the trasaction saved is of UPD_STAMP
+ * type, then add it into the old_stamp_list
+ * and mark it as stamped.
+ */
+ add_old_stamp(cur->lu_dirname, cur->lu_time,
+ cur->lu_xid);
+ o_si = (old_stamp_item *)
+ (nis_find_item(cur->lu_dirname,
+ &old_stamp_list));
+ if (o_si != 0)
+ o_si->stamped = 1;
+ }
+ __nis_release_item((NIS_HASH_ITEM *)o_si,
+ &old_stamp_list, -1);
+ cur->lu_xid += offset_xid;
+ last_xid = cur->lu_xid;
+ if (verbose)
+ syslog(LOG_INFO,
+ "checkpoint_log: Transaction %d, time %d (%s) kept",
+ cur->lu_xid, cur->lu_time, cur->lu_dirname);
+
+ upd_size = XID_SIZE(cur);
+ memmove((char *)addr_p, (char *)cur, upd_size);
+ addr_p += upd_size;
+ num++;
+ } else {
+ if (verbose)
+ syslog(LOG_INFO,
+ "checkpoint_log: Transaction %d, time %d (%s) removed",
+ cur->lu_xid, cur->lu_time, cur->lu_dirname);
+ add_old_stamp(cur->lu_dirname, cur->lu_time,
+ cur->lu_xid);
+ }
+ /* free up any malloc'd storage */
+ xdr_free(xdr_log_entry, (char *)&le);
+ }
+
+ if (num == 0) {
+ /* Deleted all of the entries. */
+ if (verbose)
+ syslog(LOG_INFO,
+ "checkpoint_log: all entries removed.");
+ __nis_log->lh_head = NULL;
+ __nis_log->lh_tail = NULL;
+ __nis_log->lh_num = 0;
+ sync_header();
+ ret = ftruncate(__nis_logfd, FILE_BLK_SZ);
+ if (ret == -1) {
+ syslog(LOG_ERR,
+ "checkpoint_log: cannot truncate transaction log file");
+ abort();
+ }
+ __nis_filesize = FILE_BLK_SZ;
+ ret = (int)lseek(__nis_logfd, __nis_filesize, SEEK_SET);
+ if (ret == -1) {
+ syslog(LOG_ERR,
+ "checkpoint_log: cannot increase transaction log file size");
+ abort();
+ }
+ ret = (int)write(__nis_logfd, "+", 1);
+ if (ret != 1) {
+ syslog(LOG_ERR,
+ "cannot write one character to transaction log file");
+ abort();
+ }
+ __nis_logsize = sizeof (log_hdr);
+ if (msync((caddr_t)__nis_log, __nis_logsize, MS_SYNC)) {
+ perror("msync:");
+ syslog(LOG_CRIT, "unable to mysnc() LOG");
+ abort();
+ }
+
+ } else {
+ if (verbose)
+ syslog(LOG_INFO,
+ "checkpoint_log: some entries removed.");
+ __nis_log->lh_xid = last_xid;
+ __nis_log->lh_num = num;
+ sync_header();
+ error = __log_resync(__nis_log, FCHKPT);
+ if (error) {
+ syslog(LOG_CRIT,
+ "checkpoint_log: Checkpoint failed, unable to resync.");
+ abort();
+ }
+ }
+
+ /*
+ * Write back "last update" stamps for all of the directories
+ * we've processed.
+ */
+ LOCK_LIST(&stamp_list, "checkpoint_log(stamp_list)");
+ in_checkpoint = FALSE;
+ while (stamp_list.first) {
+ nis_result *res;
+ nis_object *d_obj;
+ int srv;
+ ulong_t ttime;
+
+ si = (stamp_item *)(stamp_list.first);
+ (void) nis_remove_item(si->item.name, &stamp_list);
+ d_obj = NULL;
+ res = nis_lookup(si->item.name, MASTER_ONLY);
+ if ((res->status != NIS_NOTFOUND) &&
+ (res->status != NIS_NOSUCHNAME)) {
+ d_obj = res->objects.objects_val;
+ if (res->status == NIS_SUCCESS) {
+ if (__type_of(d_obj) != NIS_DIRECTORY_OBJ) {
+ syslog(LOG_WARNING,
+ "checkpoint_log: removed timestamp for %s (not a directory)",
+ si->item.name);
+ } else if ((srv = nis_isserving(d_obj)) != 0) {
+ /*
+ * If we're a replica, we want to keep
+ * the last_update() time as our
+ * UPD_STAMP. We may have LDAP-sourced
+ * updates that are more recent, but
+ * that shouldn't show up to a
+ * NIS_CPTIME.
+ */
+ if (srv == 1) {
+ ttime = si->utime;
+ } else {
+ ttime =
+ last_update(si->item.name);
+ if (ttime == 0)
+ ttime = si->utime;
+ }
+ make_tombstone(si->item.name,
+ ttime);
+ } else {
+ if (verbose)
+ syslog(LOG_INFO,
+ "checkpoint_log: removed timestamp for %s (we no longer serve)",
+ si->item.name);
+ }
+ } else {
+ /*
+ * If nis_lookup() failed, we most likely
+ * aren't the master. Hence, we retain the
+ * existing last_update() time as our
+ * UPD_STAMP
+ */
+ ttime = last_update(si->item.name);
+ if (ttime == 0)
+ ttime = si->utime;
+ make_tombstone(si->item.name, ttime);
+ }
+ } else {
+ if (verbose)
+ syslog(LOG_INFO,
+ "checkpoint_log: removed timestamp for %s (no longer exists)",
+ si->item.name);
+ }
+ XFREE(si->item.name);
+ XFREE(si);
+ nis_freeresult(res);
+ }
+ ULOCK_LIST(&stamp_list, "checkpoint_log(stamp_list)");
+
+ __nis_log->lh_state = LOG_STABLE;
+ sync_header();
+ unlink(backup);
+ next_warntime = 0l;
+ if (verbose)
+ syslog(LOG_INFO, "checkpoint_log: Checkpoint complete.");
+
+ /* re-generate the timestamp cache */
+ init_updatetime();
+ unlockTransLog("checkpoint_log", 1);
+
+ return (1);
+}
+
+/*
+ * last_update()
+ *
+ * This function scans the log backwards for the last timestamp of an update
+ * that occurred on the named directory. If it cannot find an update for that
+ * directory it returns 0L.
+ */
+u_long
+last_update(name)
+ nis_name name;
+{
+ log_upd *upd;
+
+ /*
+ * Last update time for the root object is gotten from the object
+ * itself, or, it that does not exist, 0.
+ */
+ if (root_object_p(name)) {
+ nis_object* robj;
+
+ if (robj = get_root_object()) {
+ u_long ttime;
+ ttime = robj->zo_oid.mtime;
+ nis_destroy_object(robj);
+ return (ttime);
+ } else {
+ return (0L);
+ }
+ }
+
+ /*
+ * Always uses the cached update timestamp. However, if somehow
+ * the cache is not available, it will fallback to the old way of
+ * using the transaction log. In a readonly child, we insist on
+ * using the cache to avoid using corrupted data in the shared
+ * transaction log address space.
+ */
+
+ if (updatetime_cache != NULL) {
+ stamp_item *si;
+ u_long utime;
+
+ si = (stamp_item *)(nis_find_item(name, updatetime_cache));
+ if (si) {
+ utime = si->utime;
+ if (verbose)
+ syslog(LOG_INFO,
+ "last_update: (cached) returning %d for %s.",
+ utime, name);
+ __nis_release_item(si, updatetime_cache, -1);
+ /*
+ * If the update time is ULONG_MAX, the directory
+ * is invalid and should be dumped in its entirety.
+ * That's supposed to be handled during startup
+ * (the 'invalid_directory' code in nis_main.c),
+ * but if we got ULONG_MAX here, we do what we
+ * can to force a full dump by returning zero.
+ */
+ return ((utime != ULONG_MAX) ? utime : 0);
+ }
+ } else {
+ if (verbose)
+ syslog(LOG_INFO, "last_update: No cache.");
+ if (readonly) {
+ if (verbose)
+ syslog(LOG_INFO,
+ "last_update: returning 0 for %s", name);
+ return (0L);
+ }
+ (void) lockTransLog("last_update", 1, 0);
+ upd = __nis_log->lh_tail;
+ while (upd) {
+ if (nis_dir_cmp(name, upd->lu_dirname) == SAME_NAME) {
+ if (verbose)
+ syslog(LOG_INFO,
+ "last_update: (log) returning %d for %s.",
+ upd->lu_time, name);
+ unlockTransLog("last_update", 1);
+ return (upd->lu_time);
+ }
+ upd = upd->lu_prev;
+ }
+ unlockTransLog("last_update", 1);
+ }
+ if (verbose)
+ syslog(LOG_INFO, "last_update: returning 0 for %s", name);
+ return (0L);
+}
+
+/*
+ * entries_since()
+ *
+ * This function returns a malloc'd array of entries for the passed
+ * directory since the given timestamp. This is used when dumping
+ * deltas to the replicas. It returns NULL if it can't find any
+ * entries.
+ *
+ * As part of the fix for bug 4186012, avoid including entries with
+ * a time stamp greater than or equal to the current time. This avoids
+ * distributing information about entries in the current second until
+ * that time has passed. This means that, say, replicas see all the
+ * updates for that second rather than there being a question about
+ * which of the many possible updates with the same time stamp they
+ * last saw. In the event of there being outstanding entries the
+ * directory is added to the ping list so that the master will, in
+ * time, nisping the replicas again.
+ */
+void
+entries_since(dobj, dtime, res)
+ nis_object *dobj;
+ u_long dtime;
+ log_result *res;
+{
+ log_upd *upd;
+ log_upd *first;
+ u_char **updates, **tmp;
+ int n_updates, total_updates, i;
+ XDR xdrs;
+ time_t currtime;
+ char name[NIS_MAXPATH];
+
+ snprintf(name, NIS_MAXPATH, "%s.%s", dobj->zo_name, dobj->zo_domain);
+
+ if (verbose)
+ syslog(LOG_INFO, "entries_since: dir '%s', time %d.",
+ name, dtime);
+
+ res->lr_status = NIS_SUCCESS;
+ (void) lockTransLog("entries_since", 0, 0);
+ first = __nis_log->lh_tail;
+ while (first) {
+ if ((nis_dir_cmp(name, first->lu_dirname) == SAME_NAME) &&
+ (first->lu_time == dtime))
+ break;
+ first = first->lu_prev;
+ }
+
+ if (! first) {
+ syslog(LOG_INFO, "entries_since: Replica of %s is out of date.",
+ name);
+ res->lr_status = NIS_RESYNC;
+ unlockTransLog("entries_since", 0);
+ return;
+ }
+
+ total_updates = 128;
+ updates = (u_char **)XMALLOC(total_updates * sizeof (char *));
+ if (! updates) {
+ if (verbose)
+ syslog(LOG_INFO, "entries_since(): out of memory");
+ res->lr_status = NIS_NOMEMORY;
+ unlockTransLog("entries_since", 0);
+ return;
+ }
+
+ n_updates = 0;
+ currtime = time(0);
+ for (upd = first->lu_next; upd && (upd->lu_xid <= __nis_log->lh_xid);
+ upd = upd->lu_next) {
+ if ((nis_dir_cmp(name, upd->lu_dirname) == SAME_NAME) &&
+ (upd->lu_size > 4)) {
+ if (upd->lu_time >= currtime) {
+ if (verbose)
+ syslog(LOG_INFO,
+ "entries_since: avoiding returning entries updated "
+ "this second (from xid %d)", upd->lu_xid);
+ /*
+ * Make sure we ping the replicas
+ * soon. The master will only ping
+ * replicas once the updateBatchingTimeout
+ * has passed, so we estabtabllish a ping
+ * time that's the current time, minus the
+ * updateBatchingTimeout, plus one. This
+ * ensures the ping doesn't occur this second
+ * but ASAP afterwards.
+ */
+ add_pingitem(dobj,
+ currtime - updateBatchingTimeout() + 1,
+ &ping_list);
+ break;
+ }
+ if (verbose)
+ syslog(LOG_INFO, "entries_since: xid %d",
+ upd->lu_xid);
+ updates[n_updates++] = &(upd->lu_data[0]);
+ if ((n_updates % 128) == 126) {
+ tmp = updates;
+ total_updates += 128;
+ updates = (u_char **)
+ XMALLOC(total_updates*sizeof (u_char*));
+ if (! updates) {
+ syslog(LOG_INFO,
+ "entries_since(): out of memory");
+ res->lr_status = NIS_NOMEMORY;
+ XFREE(tmp);
+ unlockTransLog("entries_since", 0);
+ return;
+ }
+ for (i = 0; i < n_updates; i++)
+ updates[i] = tmp[i];
+ XFREE(tmp);
+ }
+ }
+ }
+ syslog(LOG_INFO, "entries_since: Found %d deltas for dir %s",
+ n_updates, name);
+ if (n_updates == 0) {
+ res->lr_status = NIS_SUCCESS;
+ XFREE(updates);
+ unlockTransLog("entries_since", 0);
+ return;
+ }
+ res->lr_entries.lr_entries_val = (log_entry *)
+ XCALLOC(n_updates, sizeof (log_entry));
+ if (! res->lr_entries.lr_entries_val) {
+ res->lr_status = NIS_NOMEMORY;
+ XFREE(updates);
+ unlockTransLog("entries_since", 0);
+ return;
+ }
+ for (i = 0; i < n_updates; i++) {
+ /*
+ * NOTE : Since we're xdr decoding we don't really care
+ * what the "size" value is, we're only going to decode
+ * one log entry anyway.
+ */
+ xdrmem_create(&xdrs, (char *)updates[i], 0x4000000,
+ XDR_DECODE);
+ if (!xdr_log_entry(&xdrs, (res->lr_entries.lr_entries_val+i))) {
+ res->lr_status = NIS_NOMEMORY;
+ break;
+ }
+ }
+ res->lr_entries.lr_entries_len = n_updates;
+ XFREE(updates);
+ unlockTransLog("entries_since", 0);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_main.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_main.c
new file mode 100644
index 0000000000..49c2170331
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_main.c
@@ -0,0 +1,1385 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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"
+
+/*
+ * nis_main.c
+ *
+ * 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 <rpc/rpc.h>
+#include <syslog.h>
+#include <signal.h>
+#include <ucontext.h>
+#include <time.h>
+#include <string.h>
+#include <wait.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <rpcsvc/nis.h>
+#include <rpcsvc/nis_db.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
+
+FILE *cons = NULL;
+extern int (*__clear_directory_ptr)(nis_name);
+extern int clear_directory(nis_name);
+
+#define invalid_directory (__nis_get_tsd()->invalid_directory)
+
+/* Defined in nis_log_common.c */
+extern int need_checkpoint; /* The log is "dirty" */
+extern pid_t master_pid; /* Master PROCESS id */
+
+int _rpcsvcdirty;
+int max_children = 128; /* Very large number */
+int secure_level = 2; /* Security level 2 = max supported */
+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 */
+CLIENT *resolv_client = NULL; /* Resolv client handle */
+char *resolv_tp = "ticots"; /* Resolv netid used on resolv_setup() */
+struct timeval start_time; /* Time service started running. */
+extern unsigned long __maxloglen; /* maximum transaction log size */
+unsigned int heap_start; /* sbrk(0) at start of time */
+
+extern uint_t next_refresh; /* next time to re-load dot file */
+
+#ifdef DEBUG
+int debug = 1;
+#else
+int debug = 0;
+#endif
+
+struct upd_log *nis_log; /* Pointer to the mmap'd log. */
+
+extern int optind, opterr;
+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 *);
+extern db_status db_defer(nis_name);
+extern db_status db_commit(nis_name);
+extern db_status db_rollback(nis_name);
+
+/*
+ * 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 emulate_yp = FALSE; /* NIS compat mode or not */
+int resolv_flag = 0; /* Resolv on no host match */
+int tflag = FALSE; /* command-line option 't' */
+NIS_HASH_TABLE ping_list = NIS_HASH_TABLE_MT_INIT;
+ /* List of directory names that need to */
+ /* be notified of updates */
+int ping_pid = 0; /* Pinger pid for pinger process */
+NIS_HASH_TABLE upd_list = NIS_HASH_TABLE_MT_INIT;
+ /* List of directory names that have */
+ /* pending updates */
+ulong_t cp_time = 0; /* Time of last checkpoint */
+NIS_HASH_TABLE checkpoint_list = NIS_HASH_TABLE_MT_INIT;
+ /* List of directory names that have */
+ /* pending checkpoint */
+
+static void
+timetodie(proc)
+int proc;
+{
+ hup_received = 1;
+}
+
+void
+check_updaters()
+{
+ ping_item *pp, *nxt;
+ int pid;
+ ulong_t lu, curtime = ~0UL;
+ struct sigaction sigactn;
+ int pending_updates = 0;
+ struct timeval tp;
+ NIS_HASH_TABLE tmpupd;
+ ping_item *tmppp;
+
+ /* Check if any item on the list is due for update */
+ if (gettimeofday(&tp, 0) != -1) {
+ curtime = tp.tv_sec;
+ __nis_init_hash_table(&tmpupd, 0);
+ LOCK_LIST(&upd_list, "check_updaters(upd_list)");
+ for (pp = (ping_item *)(upd_list.first); pp != 0; pp = nxt) {
+ nxt = (ping_item *)(pp->item.nxt_item);
+ if (pp->utime <= curtime) {
+ pending_updates = 1;
+ /* Save a copy on the local list */
+ tmppp = malloc(sizeof (*tmppp));
+ if (tmppp != NULL) {
+ /* Copy pointers OK */
+ *tmppp = *pp;
+ (void) __nis_insert_item_mt(tmppp,
+ &tmpupd, 0);
+ }
+#ifdef NIS_MT_DEBUG
+ else {
+ abort();
+ }
+#endif /* NIS_MT_DEBUG */
+ }
+ }
+ ULOCK_LIST(&upd_list, "check_updaters(upd_list)");
+ if (!pending_updates) {
+ if (verbose)
+ syslog(LOG_INFO,
+ "check_updaters: no pending updates");
+ return;
+ }
+ }
+
+ if (cons)
+ fprintf(cons, "check_updaters : starting resync\n");
+ if (verbose) {
+ syslog(LOG_INFO, "check_updaters: Starting resync.");
+ }
+ for (pp = (ping_item *)(tmpupd.first); pp; pp = nxt) {
+ nxt = (ping_item *)(pp->item.nxt_item);
+ lu = last_update(pp->item.name);
+ if (cons)
+ fprintf(cons, "check_updaters : update %s\n",
+ pp->item.name);
+ if ((pp->mtime > lu) && (pp->utime <= curtime)) {
+ if (replica_update(pp)) {
+ tmppp = nis_remove_item(pp->item.name,
+ &upd_list);
+ if (tmppp != NULL) {
+ XFREE(tmppp->item.name);
+ if (tmppp->obj)
+ nis_destroy_object(tmppp->obj);
+ XFREE(tmppp);
+ }
+ /* The 'tmpupd' list is purged later */
+ } else {
+ /*
+ * No use continuously retrying, so backoff
+ * exponentially.
+ */
+ tmppp = pp;
+ pp = __nis_find_item_mt(pp->item.name,
+ &upd_list, -1, NULL);
+ if (pp != NULL) {
+ if (curtime != ~0UL)
+ pp->utime = curtime + pp->delta;
+ pp->delta *= 2;
+ if (pp->delta > MAX_UPD_LIST_TIME_INCR) {
+ pp->delta = MAX_UPD_LIST_TIME_INCR;
+ if (cons)
+ fprintf(cons,
+ "check_updaters: unable to resync %s\n",
+ pp->item.name);
+ syslog(LOG_WARNING,
+ "check_updaters: unable to resync %s",
+ pp->item.name);
+ }
+ (void) __nis_release_item(pp, &upd_list,
+ -1);
+ }
+ pp = tmppp;
+ }
+ } else if (pp->mtime <= lu) {
+ tmppp = nis_remove_item(pp->item.name, &upd_list);
+ if (tmppp != NULL) {
+ XFREE(tmppp->item.name);
+ if (tmppp->obj)
+ nis_destroy_object(tmppp->obj);
+ XFREE(tmppp);
+ }
+ }
+ }
+ /*
+ * 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.
+ */
+ for (pp = (ping_item *)tmpupd.first; pp != NULL; pp = tmppp) {
+ tmppp = (ping_item *)pp->item.nxt_item;
+ free(pp);
+ }
+}
+
+void
+check_pingers()
+{
+ ping_item *pp, *nxt;
+ struct timeval ctime;
+
+ LOCK_LIST(&ping_list, "check_pingers(ping_list)");
+ if (ping_list.first == NULL) {
+ ULOCK_LIST(&ping_list, "check_pingers(ping_list)");
+ return;
+ }
+
+ gettimeofday(&ctime, 0);
+ if (cons)
+ fprintf(cons, "check_pingers : \n");
+ for (pp = (ping_item *)ping_list.first; pp; pp = nxt) {
+ /* save next pointer in case we remove it */
+ nxt = (ping_item *)(pp->item.nxt_item);
+
+ if ((pp->mtime + updateBatchingTimeout()) > ctime.tv_sec)
+ continue;
+
+ if (verbose && cons)
+ fprintf(cons, "check_pingers: ping %s\n",
+ pp->item.name);
+
+/*
+ * 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.
+ */
+ nis_remove_item(pp->item.name, &ping_list);
+ if (!ping_replicas(pp))
+ nis_insert_item((NIS_HASH_ITEM *)pp, &ping_list);
+ }
+ ULOCK_LIST(&ping_list, "check_pingers(ping_list)");
+}
+
+#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 *
+nis_svc_tp_create(dispatch, prognum, versnum, nconf)
+ void (*dispatch)(); /* Dispatch function */
+ ulong_t prognum; /* Program number */
+ ulong_t versnum; /* Version number */
+ struct netconfig *nconf; /* Netconfig structure for the network */
+{
+ SVCXPRT *xprt;
+ int fd;
+ struct t_info tinfo;
+
+ if (nconf == (struct netconfig *)NULL) {
+ (void) syslog(LOG_ERR,
+ "nis_svc_tp_create: invalid netconfig structure for prog %d vers %d",
+ prognum, versnum);
+ return ((SVCXPRT *)NULL);
+ }
+ fd = RPC_ANYFD;
+ if ((emulate_yp) && (strcmp(nconf->nc_protofmly, NC_INET) == 0 ||
+ strcmp(nconf->nc_protofmly, NC_INET6) == 0)) {
+ fd = t_open(nconf->nc_device, O_RDWR, &tinfo);
+ if (fd == -1) {
+ (void) syslog(LOG_ERR,
+ "nis_svc_tp_create: could not open connection for %s",
+ nconf->nc_netid);
+ return ((SVCXPRT *)NULL);
+ }
+ if (__rpc_bindresvport_ipv6(fd, (struct sockaddr *)NULL,
+ (int *)NULL, 8,
+ nconf->nc_protofmly) == -1) {
+ (void) t_close(fd);
+ fd = RPC_ANYFD;
+ }
+ }
+ xprt = svc_tli_create(fd, nconf, (struct t_bind *)NULL, 0, 0);
+ if (xprt == (SVCXPRT *)NULL) {
+ return ((SVCXPRT *)NULL);
+ }
+ (void) rpcb_unset(prognum, versnum, nconf);
+ if (svc_reg(xprt, prognum, versnum, dispatch, nconf) == FALSE) {
+ (void) syslog(LOG_ERR,
+ "nis_svc_tp_create: Could not register prog %d vers %d on %s",
+ prognum, versnum, nconf->nc_netid);
+ SVC_DESTROY(xprt);
+ return ((SVCXPRT *)NULL);
+ }
+ 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 {
+ SVCXPRT *xprt; /* Server handle */
+ struct xlist *next; /* Next item */
+};
+
+/*
+ * 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 struct xlist *nis_xprtlist = (struct xlist *)NULL;
+
+static int
+nis_svc_create(dispatch, prognum, versnum, target_nc_flag)
+ void (*dispatch)(); /* Dispatch function */
+ ulong_t prognum; /* Program number */
+ ulong_t versnum; /* Version number */
+ unsigned target_nc_flag; /* value of netconfig flag */
+{
+ struct xlist *l;
+ int num = 0;
+ SVCXPRT *xprt;
+ struct netconfig *nconf;
+ NCONF_HANDLE *handle;
+
+ if ((handle = setnetconfig()) == NULL) {
+ (void) syslog(LOG_ERR,
+ "nis_svc_create: could not get netconfig information");
+ return (0);
+ }
+ while (nconf = getnetconfig(handle)) {
+ if (!(nconf->nc_flag & target_nc_flag))
+ continue;
+ for (l = nis_xprtlist; l; l = l->next) {
+ if (strcmp(l->xprt->xp_netid, nconf->nc_netid) == 0) {
+ /* Found an old one, use it */
+ (void) rpcb_unset(prognum, versnum, nconf);
+ if (svc_reg(l->xprt, prognum, versnum,
+ dispatch, nconf) == FALSE)
+ (void) syslog(LOG_ERR,
+ "nis_svc_create: could not register prog %d vers %d on %s",
+ prognum, versnum, nconf->nc_netid);
+ else
+ num++;
+ break;
+ }
+ }
+ if (l == (struct xlist *)NULL) {
+ /* It was not found. Now create a new one */
+ xprt = nis_svc_tp_create(dispatch, prognum,
+ versnum, nconf);
+ if (xprt) {
+ l = (struct xlist *)malloc(sizeof (*l));
+ if (l == (struct xlist *)NULL) {
+ (void) syslog(LOG_ERR,
+ "nis_svc_create: no memory");
+ return (0);
+ }
+ l->xprt = xprt;
+ l->next = nis_xprtlist;
+ nis_xprtlist = l;
+ num++;
+ }
+ }
+ }
+ endnetconfig(handle);
+ /*
+ * 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.
+ */
+db_status
+nis_put_offline(nis_name dirname, bool_t fullDump)
+{
+ db_status stat = DB_SUCCESS;
+
+ switch (ldapConfig.resyncService) {
+
+ case directory_locked:
+ if (!fullDump) {
+ /* Write lock the specified directory */
+ if (__nis_lock_db_directory(dirname, -1, 0, dirname) ==
+ NULL) {
+ if (verbose)
+ syslog(LOG_ERR,
+ "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.
+ */
+ stat = db_defer(dirname);
+ break;
+
+ case from_live:
+ /* Nothing to do */
+ break;
+
+ default:
+#ifdef NIS_MT_DEBUG
+ abort();
+#endif /* NIS_MT_DEBUG */
+ stat = DB_INTERNAL_ERROR;
+ break;
+ }
+
+ return (stat);
+}
+
+/*
+ * Resume full service for specified directory
+ */
+db_status
+nis_put_online(nis_name dirname, __nis_defer_t commitAction, bool_t fullDump)
+{
+ char *dirl, *curdir, *nxtdir;
+ int ret = 1;
+ db_status stat = DB_SUCCESS;
+
+ switch (ldapConfig.resyncService) {
+
+ case directory_locked:
+ if (!fullDump) {
+ /* Unlock the directory */
+ if (!__nis_ulock_db_directory(dirname, -1, 0,
+ dirname)) {
+ if (verbose)
+ syslog(LOG_ERR,
+ "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)
+ stat = db_commit(dirname);
+ else if (commitAction == d_rollback)
+ stat = db_rollback(dirname);
+ 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
+update_cache_data(nis_object* root_obj)
+{
+ /* make sure the cache data is accurate */
+ writeColdStartFile(&(root_obj->DI_data));
+ __nis_CacheRestart();
+}
+
+static void
+print_options()
+{
+ fprintf(stderr, "Options supported by this version :\n");
+ fprintf(stderr, "\th - print this help message.\n");
+ fprintf(stderr, "\tC - open diagnostic channel on /dev/console\n");
+ fprintf(stderr, "\tF - force checkpoint at startup time\n");
+ fprintf(stderr, "\tA - authentication verbose messages\n");
+ fprintf(stderr, "\tL [n] - Max load (n) of child processes\n");
+ fprintf(stderr, "\tf - force registration even if program # in use\n");
+ fprintf(stderr, "\tv - enable verbose mode\n");
+ fprintf(stderr, "\tY - emulate NIS (YP) service\n");
+ fprintf(stderr, "\tB - emulate NIS (YP) dns resolver service\n");
+ fprintf(stderr, "\tt netid - use netid as transport for resolver\n");
+ fprintf(stderr, "\td [dictionary] - user defined dictionary\n");
+ fprintf(stderr, "\tS [n] - Security level (n) 0,1, or 2\n");
+ fprintf(stderr, "\tD - debug mode (don't fork)\n");
+ fprintf(stderr, "\tc - checkpoint time in seconds (ignored)\n");
+ fprintf(stderr, "\tT n - Size of transaction log in megabytes\n");
+ fprintf(stderr, "\tm file - Name of LDAP mapping config file\n");
+ fprintf(stderr, "\tx attr=val - Configuration attribute name/value\n");
+ fprintf(stderr, "\tz n - Maximum rpc record size in bytes (>= %d)\n",
+ RPC_MAXDATASIZE);
+ 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
+set_rpc_gss_svc_names()
+{
+ mechanism_t **mechs;
+
+ if (mechs = __nis_get_mechanisms(FALSE)) {
+ int slen;
+ mechanism_t **mpp;
+ char svc_name[NIS_MAXNAMELEN+1];
+ char *lh = nis_local_host();
+
+ if (! lh) {
+ syslog(LOG_ERR,
+ "can't set RPC GSS service name: can't get local host name");
+ __nis_release_mechanisms(mechs);
+ return;
+ }
+
+ /* '@' + NUL = 2 */
+ if (strlen(lh) + strlen(NIS_SVCNAME_NISD) + 2 >
+ sizeof (svc_name)) {
+ syslog(LOG_ERR,
+ "can't set RPC GSS service name: svc_name bufsize too small");
+ __nis_release_mechanisms(mechs);
+ return;
+ }
+ /* service names are of the form svc@server.dom */
+ (void) sprintf(svc_name, "%s@%s", NIS_SVCNAME_NISD, lh);
+ /* remove trailing '.' */
+ slen = strlen(svc_name);
+ if (svc_name[slen - 1] == '.')
+ svc_name[slen - 1] = '\0';
+
+ for (mpp = mechs; *mpp; mpp++) {
+ mechanism_t *mp = *mpp;
+
+ if (AUTH_DES_COMPAT_CHK(mp))
+ break;
+
+ if (! VALID_MECH_ENTRY(mp)) {
+ syslog(LOG_ERR,
+ "%s: invalid mechanism entry name '%s'",
+ NIS_SEC_CF_PATHNAME,
+ mp->mechname ? mp->mechname : "NULL");
+ continue;
+ }
+
+ if (rpc_gss_set_svc_name(svc_name, mp->mechname,
+ 0, NIS_PROG,
+ NIS_VERSION)) {
+ if (verbose)
+ syslog(LOG_INFO,
+ "RPC GSS service name for mech '%s' set",
+ mp->mechname);
+ } else {
+ if (secure_level > 1) {
+ rpc_gss_error_t err;
+
+ rpc_gss_get_error(&err);
+ syslog(LOG_ERR,
+"can't set RPC GSS svc name '%s' for mech '%s': RPC GSS err = %d, sys err = %d",
+ svc_name, mp->mechname,
+ err.rpc_gss_error,
+ err.system_error);
+ } else {
+ if (verbose)
+ syslog(LOG_INFO,
+ "can't set RPC GSS service name for mech '%s'",
+ mp->mechname);
+ }
+ }
+ }
+ __nis_release_mechanisms(mechs);
+ return;
+ }
+}
+
+void
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int status = 0, i, c;
+ nis_object *rootobj;
+ char buf[80];
+ char logname[80];
+ struct stat s;
+ char *dict = NULL;
+ int pid;
+ int force = 0, mb;
+ struct rlimit rl;
+ int minfd;
+ int open_console = 0;
+ struct sigaction sigactn;
+ bool_t massage_dict;
+ sigset_t new_mask;
+ 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 */
+
+ if (!rpc_control(RPC_SVC_USE_POLLFD, &fdlmtremove)) {
+ syslog(LOG_ERR,
+ "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.
+ */
+ __clear_directory_ptr = &clear_directory;
+
+ /*
+ * 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);
+
+ heap_start = (unsigned int) sbrk(0); /* before any allocs */
+
+ /*
+ * Process the command line arguments
+ */
+ opterr = 0;
+ chdir("/var/nis");
+ while ((c = getopt(argc, argv, "hCFDAL:fvYBS:rd:T:t:p:i:n:m:x:z:")) !=
+ -1) {
+ switch (c) {
+ case 'h' : /* internal help screen */
+ print_options();
+ break;
+ case 'T' :
+ mb = atoi(optarg);
+ if ((mb < 4) || (mb > 129)) {
+ fprintf(stderr, "Illegal log size.\n");
+ exit(1);
+ }
+ __maxloglen = mb * 1024 * 1024;
+ break;
+
+ case 'C' :
+ open_console++;
+ break;
+ case 'F' :
+ force_checkpoint = TRUE;
+ need_checkpoint = TRUE;
+ break;
+ case 'A' :
+ auth_verbose++;
+ break;
+ case 'Y' :
+ emulate_yp = TRUE;
+ break;
+ case 'B' :
+ resolv_flag = TRUE;
+ break;
+ case 't' :
+ tflag = TRUE;
+ resolv_tp = optarg;
+ break;
+ case 'v' :
+ verbose = 1;
+ break;
+ case 'd' :
+ dict = optarg;
+ break;
+ case 'S' :
+ secure_level = atoi(optarg);
+ break;
+ case 'r' :
+ /* obsolete option */
+ root_server = -1;
+ fprintf(stderr,
+"The -r option is obsolete and no longer necessary for root servers.\n");
+ break;
+ case 'f' :
+ force = TRUE;
+ break;
+ case 'p' :
+ cred_cache = atoi(optarg);
+ if ((cred_cache < 128) || (cred_cache > 8192)) {
+ fprintf(stderr,
+ "Illegal credential cache size.\n");
+ exit(1);
+ }
+ break;
+ case 'i' :
+ rpc_irtimeout = atoi(optarg);
+ if (rpc_irtimeout < 0) {
+ fprintf(stderr,
+ "Illegal rpc inter-record timeout.\n");
+ exit(1);
+ }
+ break;
+ case 'L' :
+ max_children = atoi(optarg);
+ if (max_children <= 0) {
+ fprintf(stderr, "Illegal load value\n");
+ exit(1);
+ }
+ break;
+ case 'z' :
+ connmaxrec = atoi(optarg);
+ break;
+ case 'D' :
+ debug = 1;
+ break;
+ case 'n':
+ concurrency = atoi(optarg);
+ break;
+ case 'm':
+ /* Config file name */
+ ldapConfFile = optarg;
+ break;
+ case 'x':
+ /* Attribute assignment */
+ ldapCLA = realloc(ldapCLA,
+ (numLA + 2) * sizeof (ldapCLA[0]));
+ if (ldapCLA == 0) {
+ fprintf(stderr,
+ "Out of memory. realloc(%d) => NULL\n",
+ (numLA+2)*sizeof (ldapCLA[0]));
+ exit(1);
+ }
+ ldapCLA[numLA++] = optarg;
+ ldapCLA[numLA] = 0;
+ break;
+ case '?' :
+ fprintf(stderr,
+ "usage: rpc.nisd [ -ACDFhlv ] [ -Y [ -B [ -t netid ]]]\n");
+ fprintf(stderr,
+ "\t[ -d dictionary ] [ -L load ] [ -S level ]\n");
+ fprintf(stderr, "\t[ -m file ] "
+ "[ -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)
+ emulate_yp = ldapConfig.emulate_yp;
+
+ /* Complete syntax checking now that defaults are processed. */
+ if (resolv_flag == TRUE) {
+ if (emulate_yp == FALSE) {
+ fprintf(stderr,
+ "Option -B requires option -Y also.\n");
+ exit(1);
+ }
+ }
+ if (tflag == TRUE) {
+ if (resolv_flag == FALSE) {
+ fprintf(stderr,
+ "Option -t requires options -Y and -B also.\n");
+ exit(1);
+ }
+ }
+
+ if (! debug) {
+ switch (fork()) {
+ case -1:
+ fprintf(stderr, "Couldn't fork a process exiting.\n");
+ exit(1);
+ case 0:
+ break;
+ default:
+ exit(0);
+ }
+
+ closelog();
+ closefrom(0);
+ (void) open("/dev/null", O_RDONLY);
+ (void) open("/dev/null", O_WRONLY);
+ (void) dup(1);
+ pid = setsid();
+ openlog("nisd", LOG_PID+LOG_NOWAIT, LOG_DAEMON);
+ }
+
+#ifdef MEM_DEBUG
+ init_xmalloc();
+#endif
+ if (open_console == 1)
+ cons = fopen("/dev/console", "w");
+ else if (open_console > 1)
+ cons = stdout;
+ syslog(LOG_INFO, "NIS+ service started.");
+ gettimeofday(&start_time, 0);
+ master_pid = getpid();
+ if (verbose)
+ syslog(LOG_INFO, "verbose mode set.");
+
+ if (!cred_cache)
+ cred_cache = CRED_CACHESZ_DEF;
+ /* set the credential cache size */
+ if (!__rpc_control(CLCR_SET_CRED_CACHE_SZ, &cred_cache))
+ syslog(LOG_ERR,
+ "rpc.nisd: cannot set credential cache size to %d",
+ cred_cache);
+ else
+ if (verbose)
+ syslog(LOG_INFO,
+ "rpc.nisd: credential cache size set to %d",
+ cred_cache);
+
+ /* set RPC inter-record timeout */
+ if (rpc_irtimeout >= 0)
+ if (!rpc_control(RPC_SVC_IRTIMEOUT_SET, &rpc_irtimeout))
+ syslog(LOG_ERR,
+ "rpc.nisd: cannot set ir timeout to %d",
+ rpc_irtimeout);
+ else
+ if (verbose)
+ syslog(LOG_INFO,
+ "rpc.nisd: ir timeout set to %d seconds",
+ rpc_irtimeout);
+
+ /* Parse LDAP mapping config */
+ {
+ int stat = parseConfig(ldapCLA, ldapConfFile);
+ if (stat == 1) {
+ logmsg(MSG_NOTIMECHECK, LOG_INFO,
+ "NIS+/LDAP mapping inactive");
+ } else if (stat != 0) {
+ logmsg(MSG_NOTIMECHECK, LOG_ERR,
+ "Aborting after NIS+/LDAP mapping parse error");
+ exit(1);
+ } else {
+ logmsg(MSG_NOTIMECHECK, LOG_INFO,
+ "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.
+ */
+ getrlimit(RLIMIT_NOFILE, &rl);
+ rl.rlim_cur = RLIM_INFINITY;
+ rl.rlim_max = RLIM_INFINITY;
+ setrlimit(RLIMIT_NOFILE, &rl);
+ minfd = 256;
+ rpc_control(__RPC_CLNT_MINFD_SET, (char *)&minfd);
+
+ if (nis_local_directory() == NULL) {
+ if (debug)
+ fprintf(stderr, "NIS+ Directory not set. Exiting.\n");
+ else
+ syslog(LOG_ERR, "NIS+ Directory not set. Exiting.");
+ exit(1);
+ }
+
+ if (debug) {
+ fprintf(stderr, "NIS+ Server startup.\n");
+ }
+
+ /*
+ * 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)
+ connmaxrec = ldapConfig.maxRPCRecordSize;
+
+ if (connmaxrec < RPC_MAXDATASIZE) {
+ syslog(LOG_WARNING,
+ "Illegal max rpc record size specified %d, "
+ "setting to default %d",
+ connmaxrec, RPC_MAXDATASIZE);
+ connmaxrec = RPC_MAXDATASIZE;
+ }
+ if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &connmaxrec)) {
+ syslog(LOG_WARNING,
+ "unable to set maximum RPC record size of %d",
+ connmaxrec);
+ }
+ }
+
+ /* 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)
+ concurrency = ldapConfig.numberOfServiceThreads;
+
+ /* 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.
+ */
+ errno = ENOTSUP;
+ numprc = sysconf(_SC_NPROCESSORS_ONLN);
+ if (numprc <= 0) {
+ syslog(LOG_WARNING,
+ "Unable to determine number of "
+ "processors on-line; assuming one: %m");
+ numprc = 1;
+ }
+ concurrency = 3 + numprc;
+ }
+
+ if (!rpc_control(RPC_SVC_MTMODE_SET, &mtmode)) {
+ syslog(LOG_WARNING, "Could not set RPC auto MT mode");
+ }
+ if (!rpc_control(RPC_SVC_THRMAX_SET, &concurrency)) {
+ syslog(LOG_WARNING,
+ "Could not set RPC concurrency = %d",
+ concurrency);
+ }
+ }
+
+ /*
+ * 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);
+ (void) sigaddset(&new_mask, SIGCHLD);
+ (void) thr_sigsetmask(SIG_BLOCK, &new_mask, NULL);
+
+ if (debug) {
+ fprintf(stderr, "Database initialization ...\n");
+ }
+
+ /*
+ * We're still single-threaded, so no need to acquire the
+ * 'setup_resolv' lock.
+ */
+ if (resolv_flag)
+ setup_resolv(&resolv_flag, &resolv_pid,
+ &resolv_client, resolv_tp, 0);
+
+ massage_dict = FALSE;
+ if (!dict) {
+ if (stat(nis_old_data(NULL), &s) == -1) { /* No old */
+ if (stat(nis_data(NULL), &s) == -1) { /* No New */
+ if (errno == ENOENT) {
+ strcpy(buf, nis_data(NULL));
+ if (mkdir(buf, 0700)) {
+ perror("rpc.nisd");
+ syslog(LOG_ERR,
+ "rpc.nisd: Unable to create NIS+ directory %s", buf);
+ exit(1);
+ }
+ } else {
+ perror("rpc.nisd");
+ syslog(LOG_ERR,
+ "rpc.nisd: unable to stat NIS+ directory %s.", buf);
+ exit(1);
+ }
+ }
+ strcpy(buf, nis_data(NULL));
+ } else if (stat(nis_data(NULL), &s) != -1) { /* Old and New */
+ /*
+ * 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.
+ */
+ if (strcmp(NIS_DIR,
+ nis_leaf_of(nis_local_host())) == 0) {
+ char oldstr[NIS_MAXNAMELEN];
+ char newstr[NIS_MAXNAMELEN];
+
+ sprintf(oldstr, "%s.log", nis_old_data(NULL));
+ strcpy(newstr, LOG_FILE);
+ if (rename(oldstr, newstr) == -1) {
+ syslog(LOG_ERR,
+ "Unable to rename NIS+ transaction log.");
+ exit(1);
+ }
+ strcpy(buf, nis_data(NULL));
+ dict = buf;
+ /* No need to massage dict */
+ } else {
+ syslog(LOG_ERR,
+ "Old and new dir structures cannot coexist.");
+ exit(1);
+ }
+ } else { /* Old, No New => massage dict. */
+ massage_dict = TRUE;
+ strcpy(buf, nis_old_data(NULL));
+ }
+ strcat(buf, ".dict");
+ dict = buf;
+ }
+ if (debug)
+ fprintf(stderr, "Dictionary is %s\n", buf);
+ status = db_initialize(dict);
+ if (status == 0) {
+ if (debug)
+ fprintf(stderr, "Unable to initialize %s\n", buf);
+ else
+ syslog(LOG_ERR, "Unable to initialize %s", buf);
+ 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 oldbuf[NIS_MAXNAMELEN], newbuf[NIS_MAXNAMELEN];
+ char oldstr[NIS_MAXNAMELEN];
+ char newstr[NIS_MAXNAMELEN];
+ char newdict[NIS_MAXNAMELEN];
+
+
+ /* Massage the dictionary file */
+ sprintf(oldbuf, "/%s/", nis_leaf_of(nis_local_host()));
+ __make_legal(oldbuf);
+ sprintf(newbuf, "/%s/", NIS_DIR);
+ sprintf(newdict, "%s.dict", nis_data(NULL));
+ if (db_massage_dict(newdict, oldbuf, newbuf) != DB_SUCCESS) {
+ syslog(LOG_ERR,
+ "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.
+ */
+ strcpy(oldstr, nis_old_data(NULL));
+ strcpy(newstr, nis_data(NULL));
+ if (rename(oldstr, newstr) == -1) {
+ syslog(LOG_ERR,
+ "Unable to rename directory structure.");
+ exit(1);
+ }
+ sprintf(oldstr, "%s.log", nis_old_data(NULL));
+ strcpy(newstr, LOG_FILE);
+ if (rename(oldstr, newstr) == -1) {
+ syslog(LOG_ERR,
+ "Unable to rename NIS+ transaction log.");
+ exit(1);
+ }
+ /* Now, reinitialize the dictionary */
+ status = db_initialize(newdict);
+ if (status == 0) {
+ if (debug)
+ fprintf(stderr,
+ "Unable to REinitialize %s\n", newdict);
+ else
+ syslog(LOG_ERR, "Unable to initialize %s",
+ 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;
+ fprintf(stderr,
+ "No root object present; running as non-root server.\n");
+ }
+
+ if (root_server) {
+ update_cache_data(rootobj); /* must do after detach */
+ if (verbose)
+ syslog(LOG_INFO, "Service running as root server.");
+ if (we_serve(&(rootobj->DI_data), MASTER_ONLY) &&
+ !verify_table_exists(__nis_rpc_domain()))
+ exit(1);
+ nis_destroy_object(rootobj); /* free; not needed anymore */
+ }
+
+ if (debug) {
+ fprintf(stderr, "... database initialization complete.\n");
+ fprintf(stderr, "Transaction log initialization ...\n");
+ }
+
+ if (! status)
+ syslog(LOG_ERR, "WARNING: Dictionary not initialized!");
+
+ sprintf(logname, "%s", LOG_FILE);
+ if (map_log(logname, FNISD)) {
+ if (debug)
+ fprintf(stderr, "Transaction log corrupt. Exiting.\n");
+ else
+ syslog(LOG_ERR, "Transaction log corrupt. Exiting.");
+ exit(1);
+ }
+
+ /* initialize the timestamp cache table */
+ init_updatetime();
+
+ if (debug) {
+ fprintf(stderr, "... transaction log initialized.\n");
+ }
+
+ /* Initialize in-core list of directories served by this server */
+ (void) nis_server_control(SERVING_LIST, DIR_INITLIST, NULL);
+
+ /*
+ * 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) {
+ nis_object id[1], *invdir = id;
+ struct ticks t[1];
+ ping_item dummy_ping[1];
+ int drastic_measures = 0;
+
+ syslog(LOG_WARNING,
+ "directory %s corrupted during update; attempting recovery",
+ invalid_directory);
+ clear_directory(invalid_directory);
+ /* forge a ping item; fill just enought for replica_update */
+ if (__directory_object(invalid_directory, t, 0, &invdir) !=
+ NIS_SUCCESS) {
+ syslog(LOG_WARNING,
+ "recovery for %s failed; couldn't get directory object",
+ invalid_directory);
+ drastic_measures = 1;
+ } else {
+ dummy_ping->item.name = invalid_directory;
+ dummy_ping->mtime = 0;
+ dummy_ping->obj = invdir;
+ if (!replica_update(dummy_ping)) {
+ syslog(LOG_WARNING,
+ "recovery for %s failed; couldn't resync",
+ invalid_directory);
+ /*
+ * replica_update will also have invalidated
+ * this directory, but just to be sure...
+ */
+ drastic_measures = 1;
+ }
+ }
+
+ if (drastic_measures) {
+ syslog(LOG_WARNING,
+ "Forcing resync by setting update time to 0 for %s",
+ invalid_directory);
+ syslog(LOG_WARNING,
+ "You may need to restore from backup");
+ make_stamp(invalid_directory, 0);
+ } else
+ syslog(LOG_WARNING,
+ "Recovery for %s completed", invalid_directory);
+ }
+
+ /* Up-/down-load data to/from LDAP */
+ i = loadLDAPdata();
+ if (i != 0) {
+ logmsg(MSG_NOTIMECHECK, LOG_WARNING,
+ "Exiting after LDAP data load");
+ exit(i > 0 ? 0 : 1);
+ }
+
+ /* rpc registration */
+ if (debug) {
+ fprintf(stderr, "RPC program registration ...\n");
+ }
+
+ rpcb_unset(NIS_PROG, NIS_VERSION, NULL);
+ if (emulate_yp)
+ {
+ rpcb_unset(YPPROG, YPVERS, NULL);
+ rpcb_unset(YPPROG, YPVERS_ORIG, NULL);
+ }
+ i = nis_svc_create(nis_prog_svc, NIS_PROG, NIS_VERSION, NC_VISIBLE);
+ if (! i)
+ exit(1);
+ else if (verbose)
+ syslog(LOG_INFO, "NIS+ service listening on %d transports.", i);
+ if (emulate_yp) {
+ i = nis_svc_create(ypprog_svc, YPPROG, YPVERS, NC_VISIBLE);
+ if (! i)
+ exit(1);
+ else if (verbose)
+ syslog(LOG_INFO,
+ "NIS service listening on %d transports.", i);
+ i = nis_svc_create(ypprog_1, YPPROG, YPVERS_ORIG, NC_VISIBLE);
+ if (! i)
+ exit(1);
+ else if (verbose)
+ syslog(LOG_INFO, "Created %d YPVERS_ORIG handles.", i);
+ }
+
+ set_rpc_gss_svc_names();
+
+ __svc_nisplus_enable_timestamps();
+
+ if (debug) {
+ fprintf(stderr, "... RPC registration complete.\n");
+ fprintf(stderr, "Service starting.\n");
+ }
+
+ {
+ pthread_t servloop_thread;
+ pthread_attr_t attr;
+ int stat;
+
+ (void) pthread_attr_init(&attr);
+ if ((stat = pthread_create(&servloop_thread, &attr,
+ servloop, 0)) != 0) {
+ syslog(LOG_ERR,
+ "error %d creating servloop thread; exiting",
+ stat);
+ if (cons || debug)
+ fprintf(stderr,
+ "error %d creating servloop thread; exiting\n",
+ stat);
+ exit(1);
+ }
+ (void) pthread_attr_destroy(&attr);
+
+ if (cons || debug)
+ fprintf(stderr,
+ "servloop thread started; ready to roll...\n");
+
+ svc_run();
+ /* Not reached */
+ }
+}
+
+int
+__rpcsec_gss_is_server()
+{
+ return (1);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_mt.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_mt.c
new file mode 100644
index 0000000000..ec7a48817e
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_mt.c
@@ -0,0 +1,159 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <malloc.h>
+#include <syslog.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/nis.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include "nis_proc.h"
+
+DECLRWLOCK(upd_list);
+DECLRWLOCK(ping_list);
+DECLRWLOCK(nisopstats);
+DECLRWLOCK(table_cache);
+DECLRWLOCK(dircachestats);
+DECLRWLOCK(translog);
+
+extern void check_updaters(void);
+extern void check_pingers(void);
+
+/*
+ * We replace the __start_clock() and __stop_clock() functons from libnsl
+ * with our own versions that provide per-thread clocks.
+ */
+int
+__start_clock(int clk) {
+ nis_tsd_t *tsd;
+ if ((clk >= MAXCLOCKS) || (clk < 0) ||
+ ((tsd = __nis_get_tsd())->clocks[clk].tv_sec))
+ return (FALSE);
+
+ (void) gettimeofday(&(tsd->clocks[clk]), 0);
+ return (TRUE);
+}
+
+uint32_t
+__stop_clock(int clk) {
+ struct timeval now;
+ uint32_t secs, mics;
+ nis_tsd_t *tsd;
+
+ if ((clk >= MAXCLOCKS) || (clk < 0) ||
+ (!((tsd = __nis_get_tsd())->clocks[clk].tv_sec)))
+ return (0);
+
+ (void) gettimeofday(&now, 0);
+ secs = (uint32_t)(now.tv_sec - tsd->clocks[clk].tv_sec);
+ mics = (uint32_t)(now.tv_usec - tsd->clocks[clk].tv_usec);
+ if (mics < 0) {
+ mics += 1000000;
+ secs -= 1;
+ }
+ mics += 1000000*secs;
+ tsd->clocks[clk].tv_sec = 0;
+ return (mics);
+}
+
+static nis_tsd_t nis_shared_tsd;
+static pthread_key_t nis_tsd_key;
+
+void
+__nis_tsd_destroy(void *key) {
+
+ nis_tsd_t *tsd = (nis_tsd_t *)key;
+ cleanupblock_t *rb, *next_rb;
+
+ if (tsd != 0) {
+ /* Free memory allocated by nis_get_static_storage() */
+ if (tsd->censor_object_buf.buf != 0)
+ free(tsd->censor_object_buf.buf);
+ /* Purge loose ends */
+ __nis_thread_cleanup(tsd);
+ for (rb = tsd->ragblocks; rb != 0; rb = next_rb) {
+ next_rb = rb->next;
+#ifdef NIS_MT_DEBUG
+ printf("%d: 0x%x freed\n", pthread_self(), rb);
+#endif /* NIS_MT_DEBUG */
+ free(rb);
+ }
+ free(tsd);
+ }
+}
+
+int
+__nis_init_tsd_key(void) {
+
+ return (pthread_key_create(&nis_tsd_key, __nis_tsd_destroy));
+}
+
+#pragma init(__nis_init_tsd_key)
+
+nis_tsd_t *
+__nis_get_tsd(void) {
+ nis_tsd_t *tsd;
+
+ if ((tsd = pthread_getspecific(nis_tsd_key)) == 0) {
+ /* No TSD; create it */
+ if ((tsd = malloc(sizeof (*tsd))) != 0) {
+ /* Initialize TSD */
+ memset(tsd, 0, sizeof (*tsd));
+ /* Register TSD */
+ if (pthread_setspecific(nis_tsd_key, tsd) != 0) {
+ /* Can't store key; abort */
+#ifdef NIS_MT_DEBUG
+ abort();
+#endif /* NIS_MT_DEBUG */
+ free(tsd);
+ tsd = &nis_shared_tsd;
+ }
+ } else {
+ /* No memory ? */
+#ifdef NIS_MT_DEBUG
+ abort();
+#endif /* NIS_MT_DEBUG */
+ tsd = &nis_shared_tsd;
+ }
+ }
+
+ return (tsd);
+}
+
+void
+__nis_thread_cleanup(nis_tsd_t *tsd) {
+
+ if (tsd->looseends != 0) {
+ do_cleanup(tsd->looseends);
+ tsd->looseends = 0;
+ }
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_mt.h b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_mt.h
new file mode 100644
index 0000000000..009bb37442
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_mt.h
@@ -0,0 +1,162 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifndef _NIS_MT_H
+#define _NIS_MT_H
+
+#include <pthread.h>
+#include <synch.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <rpcsvc/nis_callback.h>
+#include <rpcsvc/yp_prot.h>
+
+#include <nisdb_rw.h>
+#include <nis_hashitem.h>
+
+/* RW locks */
+USERWLOCK(upd_list); /* nis_main.c */
+USERWLOCK(ping_list); /* nis_main.c */
+USERWLOCK(nisopstats); /* nis_service.c, nis_xx_proc.c */
+USERWLOCK(table_cache); /* nis_db.c */
+USERWLOCK(dircachestats); /* nis_subr_proc.c */
+USERWLOCK(translog); /* nis_log_common.c, nis_log_svc.c */
+
+/* Functions */
+extern int msleep(ulong_t);
+extern void mark_activity(void);
+extern void *servloop(void *);
+extern void *callback_thread(void *);
+extern void *dumpsvc_thread(void *);
+
+extern void wakeup_servloop(void);
+extern time_t updateBatchingTimeout(void);
+extern void setPingWakeup(time_t);
+
+/*
+ * MAXCLOCKS normally in libnsl/nis/gen/nis_local.h, but we roll our own
+ * thread-specific clocks.
+ */
+#define MAXCLOCKS 16
+
+#define MAXRAGS 1024
+/* Probably don't need MAXRAGS for a single thread. MAXRAGS_THR is a guess */
+#define MAXRAGS_THR (MAXRAGS/16)
+
+/* Keep track of allocated rag blocks */
+typedef struct cleanupblockstruct {
+ struct cleanupblockstruct *next;
+} cleanupblock_t;
+
+/* Statistics structure from nis_subr_proc.c */
+typedef struct {
+ int successes;
+ int errors;
+ int ticks;
+ ulong_t utime;
+} repl_stats_t;
+
+/* yp_all() response structure from yp_ns_proc.c */
+typedef char *string_t;
+struct ypresp_all {
+ long status;
+ string_t table_name;
+ nis_name princp;
+ int key_column_ndx;
+ nis_object *table_zobj;
+};
+
+/* Thread-specific data */
+typedef struct {
+ struct timeval clocks[MAXCLOCKS];
+ struct cleanup *looseends;
+ struct cleanup *rags[MAXRAGS_THR];
+ struct cleanup *free_rags;
+ uint32_t cleanup_tag;
+ cleanupblock_t *ragblocks;
+ nis_name invalid_directory;
+ struct nis_sdata censor_object_buf;
+ struct nis_sdata modify_entry_buf;
+ struct nis_sdata __ibops_buf;
+ nis_db_result db_add_res;
+ nis_db_result db_remove_res;
+ struct nis_sdata local_buf__get_xdr_buf;
+ struct nis_sdata local_buf__get_string_buf;
+ struct nis_sdata local_buf__get_entry_col;
+ struct nis_sdata local_buf__get_table_col;
+ struct nis_sdata local_buf__get_attrs;
+ repl_stats_t repl_stats;
+ uint_t nis_cptime_svc_res;
+ nis_error nis_mkdir_svc_result;
+ nis_error nis_rmdir_svc_result;
+ char yp_ns_proc_record[YPMAXRECORD];
+ char yp_ns_proc_keyval[YPMAXRECORD];
+ bool_t ypproc_domain_svc_isserved;
+ bool_t ypproc_domain_nonack_svc_isserved;
+ struct ypresp_master ypproc_master_svc_resp;
+ char ypproc_master_svc_masterbuf[YPMAXPEER];
+ struct ypresp_val ypproc_match_svc_resp;
+ struct ypresp_key_val ypproc_first_svc_resp;
+ struct ypresp_key_val ypproc_next_svc_resp;
+ struct ypresp_all ypproc_all_svc_resp;
+ struct ypresp_maplist ypproc_maplist_svc_maplist;
+ char xdr_ypresp_all_short_tblnm[YPMAXMAP];
+ char *best_host_address_best_address;
+ char getcaller_inet_buf[256];
+ char map2table_tbl[YPMAXMAP];
+ char map2table_col[NIS_MAXATTRNAME];
+ bool_t nis_callback_svc_res;
+} nis_tsd_t;
+
+extern nis_tsd_t *__nis_get_tsd(void);
+extern void __nis_thread_cleanup(nis_tsd_t *);
+extern void __nis_free_items_mt(nis_tsd_t *);
+
+/* Arguments for thread entry point functions */
+typedef struct {
+ nis_fn_result *fnr;
+ nis_object *ib_obj;
+ nis_attr a[NIS_MAXATTR];
+ int na;
+ int nm;
+ int all_read;
+ char pname[1024];
+ cback_data cbarg;
+ CLIENT *cback;
+ char cbhostname[NIS_MAXNAMELEN];
+ char ibr_name[NIS_MAXNAMELEN];
+} callback_thread_arg_t;
+
+typedef struct {
+ char da_dir[1024];
+ char pname[1024];
+ CLIENT *cback;
+ ulong_t ttime;
+} dumpsvc_thread_arg_t;
+
+#endif /* _NIS_MT_H */
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_multival.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_multival.c
new file mode 100644
index 0000000000..65018ac5f7
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_multival.c
@@ -0,0 +1,857 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 1997-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifndef LINT
+static char SCCSID[] = "%Z%%M% %I% %E% SMI";
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <ctype.h>
+#include <rpcsvc/nis.h>
+#include "nis_proc.h"
+#include "nis_mt.h"
+
+/*
+ * TA_MULTIVAL and TA_SEARCHABLE are mutually exclusive. The
+ * separator character for multivalue searches is stored in the
+ * upper byte of tc_flags and is accessed with TA_MULTISEP.
+ *
+ * XXX these defines should be moved to rpcsvc/nis.h eventually XXX
+ */
+#define TA_MULTIVAL (0xff000000)
+#define TA_MULTISEP(x) (((x) & 0xff000000) >> 24)
+
+#ifndef _REENTRANT
+#define strtok_r(a, b, c) strtok((a), (b))
+#endif /* _REENTRANT */
+
+/*
+ * If FORCE_GROUP is defined, then the members column of the group
+ * table is considered a multival column, even if TA_MULTIVAL
+ * isn't set on the column.
+ */
+#define FORCE_GROUP 1
+
+struct dir_item {
+ NIS_HASH_ITEM item;
+ NIS_HASH_TABLE tables;
+};
+typedef struct dir_item dir_item;
+
+struct tab_item {
+ NIS_HASH_ITEM item;
+ nis_db_list_result *res;
+ NIS_HASH_TABLE columns;
+ dir_item *dir;
+};
+typedef struct tab_item tab_item;
+
+struct col_item {
+ NIS_HASH_ITEM item;
+ int colnum;
+ char sep[2];
+ int case_insensitive;
+ NIS_HASH_TABLE entries;
+ tab_item *tab;
+};
+typedef struct col_item col_item;
+
+struct ent_item {
+ NIS_HASH_ITEM item;
+ nis_object **obj;
+ int nobj;
+ col_item *col;
+};
+typedef struct ent_item ent_item;
+
+static NIS_HASH_TABLE dirs = NIS_HASH_TABLE_MT_INIT;
+
+static tab_item *find_table(nis_object *, int);
+static col_item *find_column(nis_object *, char *, int);
+static ent_item *find_entry(nis_object *, char *, char *, int);
+
+static tab_item *load_table(nis_object *, int);
+static col_item *load_column(tab_item *, table_col *, int, int);
+static ent_item *load_entry(col_item *, nis_object *, int);
+
+static void free_table(tab_item *);
+static void free_column(col_item *);
+static void free_entry(ent_item *);
+
+static void strlower(char *);
+
+#define RW -1
+#define NONE 0
+#define RO 1
+
+/*
+ * The find_table(), find_column(), and find_entry() functions leave the
+ * respective NIS_HASH_TABLE entries locked. The release_*() functions below
+ * unlock those tables in the correct order.
+ */
+static void
+release_directory(dir_item *dir) {
+ if (dir != 0)
+ (void) __nis_release_item(dir, &dirs, __nis_item_access(dir));
+}
+
+static void
+release_table(tab_item *tab, int release_dir) {
+
+ dir_item *dir;
+
+ if (tab != 0 && (dir = tab->dir) != 0) {
+ (void) __nis_release_item(tab, &dir->tables,
+ __nis_item_access(tab));
+ if (release_dir)
+ release_directory(dir);
+ }
+}
+
+static void
+release_column(col_item *col, int release_tab) {
+
+ tab_item *tab;
+
+ if (col != 0 && (tab = col->tab) != 0) {
+ (void) __nis_release_item(col, &tab->columns,
+ __nis_item_access(col));
+ if (release_tab)
+ release_table(tab, 1);
+ }
+}
+
+static void
+release_entry(ent_item *ent, int release_col) {
+
+ col_item *col;
+
+ if (ent != 0 && (col = ent->col) != 0) {
+ (void) __nis_release_item(ent, &col->entries,
+ __nis_item_access(ent));
+ if (release_col)
+ release_column(col, 1);
+ }
+}
+
+/*
+ * Check to see if the attribute in 'attr' is a multival
+ * attribute.
+ */
+int
+multival_check(nis_object *tobj, nis_attr *attr, table_col *cols, int ncols)
+{
+ int i;
+
+#ifdef FORCE_GROUP
+ if (strcasecmp(tobj->TA_data.ta_type, "group_tbl") == 0 &&
+ strcasecmp(attr->zattr_ndx, "members") == 0) {
+ return (1);
+ }
+#endif /* FORCE_GROUP */
+ for (i = 0; i < ncols; i++) {
+ if (strcasecmp(cols[i].tc_name, attr->zattr_ndx) == 0) {
+ /* if column is searchable, then it is not multival */
+ if (cols[i].tc_flags & TA_SEARCHABLE)
+ return (0);
+ if ((cols[i].tc_flags & TA_MULTIVAL) != 0)
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Determine which attributes refer to multival columns. All of
+ * the regular attributes are moved to the beginning of 'attr'
+ * and the multival attributes are moved to the end. The
+ * 'nattr' and 'nmulti' variables are set to the number of each
+ * type of attributes.
+ *
+ * We assume that most tables don't have multival columns, so do
+ * a quick scan and return if this is the case.
+ */
+nis_error
+multival_attr(nis_object *tobj, nis_attr *attr, int *nattr, int *nmulti)
+{
+ int i;
+ int front;
+ int back;
+ table_col *cols;
+ int ncols;
+ int *is_multival;
+ int tmp;
+ nis_attr tmp_attr;
+
+ if (__type_of(tobj) == DIRECTORY_OBJ) {
+ *nmulti = 0;
+ return (NIS_SUCCESS);
+ }
+
+ cols = tobj->TA_data.ta_cols.ta_cols_val;
+ ncols = tobj->TA_data.ta_cols.ta_cols_len;
+ for (i = 0; i < ncols; i++) {
+ if (cols[i].tc_flags & TA_MULTIVAL)
+ break;
+ }
+#ifdef FORCE_GROUP
+ if (strcasecmp(tobj->TA_data.ta_type, "group_tbl") == 0) {
+ i = 3; /* members column number */
+ }
+#endif /* FORCE_GROUP */
+ if (i >= ncols) {
+ *nmulti = 0;
+ return (NIS_SUCCESS);
+ }
+
+ /* determine which elements of attr are multival */
+ is_multival = (int *)malloc(*nattr * sizeof (is_multival[0]));
+ if (is_multival == NULL) {
+ return (NIS_NOMEMORY);
+ }
+ for (i = 0; i < *nattr; i++) {
+ is_multival[i] = multival_check(tobj, &attr[i], cols, ncols);
+ }
+
+ /* partition sort to put regular attributes first, multival last */
+ front = 0;
+ back = *nattr - 1;
+ while (front <= back) {
+ if (!is_multival[front])
+ front += 1;
+ else if (is_multival[back])
+ back -= 1;
+ else {
+ tmp_attr = attr[front];
+ attr[front] = attr[back];
+ attr[back] = tmp_attr;
+ tmp = is_multival[front];
+ is_multival[front] = is_multival[back];
+ is_multival[back] = tmp;
+ front += 1;
+ back -= 1;
+ }
+ }
+
+ if (front < *nattr) {
+ *nmulti = *nattr - front;
+ *nattr = front;
+ } else {
+ *nmulti = 0;
+ }
+
+ free((void *)is_multival);
+ return (NIS_SUCCESS);
+}
+
+/*
+ * Do a multival search on nis_object. If all of the attributes
+ * in 'm' have matches, then the object should not be filtered out.
+ */
+int
+multival_filter(nis_object *tobj, int nm, nis_attr *m, nis_object *obj)
+{
+ int i;
+ int st;
+ int len;
+ char *p;
+ int vlen;
+ char *val;
+ char *last;
+ col_item *col;
+
+ for (i = 0; i < nm; i++) {
+ if ((col = find_column(tobj, m[i].zattr_ndx, RO)) == NULL)
+ return (1);
+ p = ENTRY_VAL(obj, col->colnum);
+ len = ENTRY_LEN(obj, col->colnum);
+ if (p == NULL || len == 0 || p[len-1] != '\0') {
+ release_column(col, 1);
+ return (1);
+ }
+ /* make copy of value because strtok leaves trail of '\0' */
+ p = strdup(p);
+ if (p == NULL) {
+ release_column(col, 1);
+ return (1);
+ }
+
+ val = strtok_r(p, col->sep, &last);
+ while (val) {
+ vlen = strlen(val) + 1; /* include '\0' */
+ if (vlen == m[i].zattr_val.zattr_val_len) {
+ if (col->case_insensitive) {
+ st = strcasecmp(val,
+ m[i].zattr_val.zattr_val_val);
+ } else {
+ st = strcmp(val,
+ m[i].zattr_val.zattr_val_val);
+ }
+ if (st == 0)
+ break;
+ }
+ val = strtok_r(NULL, col->sep, &last);
+ }
+ free((void *)p);
+ release_column(col, 1);
+ if (val == NULL)
+ return (1);
+ }
+
+ /* found a match for all attributes, don't filter this object */
+ return (0);
+}
+
+/*
+ * Search the table specified by 'tobj' for all of the objects that
+ * are selected by the attributes in 'm'. This is basically a
+ * join operation. For the first attribute, we retrieve a list of
+ * objects. For subsequent attributes, we do a join and keep only
+ * the objects that occur in both lists.
+ */
+void
+multival_list(nis_object *tobj, int nm, nis_attr *m, nis_db_list_result *res)
+{
+ int i;
+ int j;
+ int k;
+ int n;
+ char *val;
+ int len;
+ ent_item *ent;
+ obj_list *olist;
+ int count = 0;
+ int nobj = 0;
+ nis_object **objs = NULL;
+
+ for (i = 0; i < nm; i++) {
+ val = m[i].zattr_val.zattr_val_val;
+ len = m[i].zattr_val.zattr_val_len;
+ if (val == NULL || len == 0 || val[len-1] != '\0') {
+ res->status = NIS_BADATTRIBUTE;
+ return;
+ }
+ ent = find_entry(tobj, m[i].zattr_ndx, val, RO);
+ if (!ent) {
+ res->status = NIS_NOTFOUND;
+ free((void *)objs);
+ return;
+ }
+
+ if (objs == NULL) {
+ objs = (nis_object **)malloc(
+ ent->nobj * sizeof (nis_object *));
+ if (objs == NULL) {
+ release_entry(ent, 1);
+ res->status = NIS_NOMEMORY;
+ return;
+ }
+ for (j = 0; j < ent->nobj; j++) {
+ objs[j] = ent->obj[j];
+ }
+ nobj = ent->nobj;
+ count = nobj;
+ } else {
+ for (j = 0; j < nobj; j++) {
+ if (objs[j] == NULL)
+ continue;
+ for (k = 0; k < ent->nobj; k++) {
+ if (objs[j] == ent->obj[k])
+ break;
+ }
+ if (k >= ent->nobj) {
+ objs[j] = NULL;
+ count -= 1;
+ }
+ }
+ }
+ release_entry(ent, 1);
+ }
+
+ if (count == 0) {
+ res->status = NIS_NOTFOUND;
+ free((void *)objs);
+ return;
+ }
+
+ olist = (obj_list *)calloc(count, sizeof (obj_list));
+
+ n = 0;
+ for (i = 0; i < nobj; i++) {
+ if (objs[i]) {
+ olist[n].o = nis_clone_object(objs[i], 0);
+ n++;
+ }
+ }
+ free((void *)objs);
+
+ if (n != count) {
+ syslog(LOG_ERR,
+ "multival_list: miscounted objects (%d, %d)", n, count);
+ }
+
+ res->status = NIS_SUCCESS;
+ res->objs = olist;
+ res->numo = count;
+}
+
+/*
+ * A table has been modified in some way, so we must free up
+ * the entries associated with it.
+ */
+void
+multival_invalidate(nis_object *obj)
+{
+ dir_item *dir;
+ tab_item *tab;
+
+ if (__type_of(obj) != TABLE_OBJ && __type_of(obj) != ENTRY_OBJ)
+ return;
+
+ dir = (dir_item *)nis_find_item_rw(obj->zo_domain, &dirs, RO);
+ if (dir == NULL)
+ return;
+
+ tab = (tab_item *)nis_remove_item(obj->zo_name, &dir->tables);
+ release_directory(dir);
+ if (tab)
+ free_table(tab);
+}
+
+static
+tab_item *
+find_table(nis_object *tobj, int rw)
+{
+ int i;
+ table_col *cols;
+ int ncols;
+ dir_item *dir;
+ tab_item *tab;
+
+ dir = (dir_item *)nis_find_item_rw(tobj->zo_domain, &dirs, RO);
+ if (dir == NULL)
+ return (NULL);
+
+ tab = (tab_item *)nis_find_item_rw(tobj->zo_name, &dir->tables, rw);
+ if (tab != 0)
+ tab->dir = dir;
+ return (tab);
+}
+
+static
+col_item *
+find_column(nis_object *tobj, char *name, int rw)
+{
+ tab_item *tab;
+ col_item *col;
+
+ tab = find_table(tobj, RO);
+ if (tab == NULL) {
+ /*
+ * While loading the table, we need write access (at least
+ * to parts of it).
+ */
+ tab = load_table(tobj, RW);
+ if (tab == NULL) {
+ /* remove any partially loaded table information */
+ multival_invalidate(tobj);
+ return (NULL);
+ }
+ }
+ col = (col_item *)nis_find_item_rw(name, &tab->columns, rw);
+ if (col != 0) {
+ col->tab = tab;
+ } else {
+ release_table(tab, 1);
+ }
+ return (col);
+}
+
+static
+ent_item *
+find_entry(nis_object *tobj, char *key, char *val, int rw)
+{
+ col_item *col;
+ ent_item *ent;
+
+ strlower(key);
+ col = find_column(tobj, key, RO);
+ if (col == NULL)
+ return (NULL);
+ if (col->case_insensitive)
+ strlower(val);
+ ent = (ent_item *)nis_find_item_rw(val, &col->entries, rw);
+ if (ent != 0) {
+ ent->col = col;
+ } else {
+ release_column(col, 1);
+ }
+ return (ent);
+}
+
+static
+col_item *
+load_column(tab_item *tab, table_col *tc, int n, int rw)
+{
+ col_item *col;
+
+ col = (col_item *)calloc(1, sizeof (col_item));
+ if (col == NULL)
+ return (NULL);
+ col->item.name = strdup(tc->tc_name);
+ if (col->item.name == NULL) {
+ free((void *)col);
+ return (NULL);
+ }
+ col->colnum = n;
+ col->sep[0] = TA_MULTISEP(tc->tc_flags);
+ col->sep[1] = '\0';
+ if (tc->tc_flags & TA_CASE)
+ col->case_insensitive = 1;
+ else
+ col->case_insensitive = 0;
+ col->tab = tab;
+ if (!nis_insert_item_rw((NIS_HASH_ITEM *)col, &tab->columns, rw)) {
+ free((void *)col->item.name);
+ free((void *)col);
+ return (NULL);
+ }
+ return (col);
+}
+
+/*
+ * load_entry()
+ *
+ * The col entry val/len should be checked before calling load_entry to make
+ * sure it is non-NULL, non-empty, length > 0, and NUL terminated.
+ */
+static
+ent_item *
+load_entry(col_item *col, nis_object *obj, int rw)
+{
+ char *p;
+ char *val;
+ int len;
+ char *last;
+ ent_item *ent = 0;
+
+ p = ENTRY_VAL(obj, col->colnum);
+ len = ENTRY_LEN(obj, col->colnum);
+ if (p == NULL || len == 0 || p[0] == '\0' ||
+ (len > 0 && p[len-1] != '\0')) {
+ syslog(LOG_ERR,
+ "load_entry: bad entry; empty/NULL or nonNUL terminated");
+ return (NULL);
+ }
+
+ /* make a copy of p because strtok leaves a trail of '\0' */
+ p = strdup(p);
+ if (p == NULL)
+ return (NULL);
+
+ val = strtok_r(p, col->sep, &last);
+ while (val) {
+ /*
+ * We're going to modify the entry, so we need RW access.
+ * XXX We expect the caller to be load_table(), and it
+ * doesn't use the return value other than to check for
+ * success (non-NULL). Thus, we make no attempt to return
+ * the (last created) entry with the requested access.
+ */
+ if (ent != 0) {
+ /* Release entry from previous lap */
+ release_entry(ent, 1);
+ }
+ ent = (ent_item *)nis_find_item_rw(val, &col->entries, RW);
+ if (ent == NULL) {
+ ent = (ent_item *)calloc(1, sizeof (ent_item));
+ if (ent == NULL) {
+ free((void *)p);
+ return (NULL);
+ }
+ ent->item.name = strdup(val);
+ if (ent->item.name == NULL) {
+ free((void *)p);
+ free((void *)ent);
+ return (NULL);
+ }
+ if (col->case_insensitive) {
+ strlower(ent->item.name);
+ }
+ ent->obj = NULL;
+ ent->nobj = 0;
+ ent->col = col;
+ if (!nis_insert_item_rw((NIS_HASH_ITEM *)ent,
+ &col->entries, RW)) {
+ free((void *)p);
+ free((void *)ent->item.name);
+ free((void *)ent);
+ return (NULL);
+ }
+ }
+ ent->obj = (nis_object **)realloc(ent->obj,
+ (ent->nobj + 1) * sizeof (entry_obj *));
+ if (ent->obj == NULL) {
+ ent->nobj = 0;
+ free((void *)p);
+ release_entry(ent, 1);
+ return (NULL);
+ }
+ ent->obj[ent->nobj] = obj;
+ ent->nobj += 1;
+
+ val = strtok_r(NULL, col->sep, &last);
+ }
+
+ free((void *)p);
+ return (ent);
+}
+
+static
+tab_item *
+load_table(nis_object *tobj, int rw)
+{
+ int i;
+ int j;
+ int multival;
+ table_col *cols;
+ table_col *col;
+ table_col colbuf;
+ dir_item *dir;
+ tab_item *tab;
+ int ncols;
+ col_item **collist;
+ char name[NIS_MAXNAMELEN];
+ char tblbuf[NIS_MAXNAMELEN * 2];
+ char *table;
+ nis_db_list_result *res;
+ obj_list *objs;
+ int nobj;
+ ent_item *tmpent = 0;
+
+ /*
+ * Get directory item. Create it if it doesn't exist.
+ */
+ dir = (dir_item *)nis_find_item_rw(tobj->zo_domain, &dirs, RO);
+ if (dir == NULL) {
+ dir = (dir_item *)calloc(1, sizeof (dir_item));
+ if (dir == NULL)
+ return (NULL);
+ dir->item.name = strdup(tobj->zo_domain);
+ if (dir->item.name == NULL) {
+ free((void *)dir);
+ return (NULL);
+ }
+ if (!nis_insert_item_rw((NIS_HASH_ITEM *)dir, &dirs, RO)) {
+ free((void *)dir->item.name);
+ free((void *)dir);
+ return (NULL);
+ }
+ }
+
+ /* create table item and add to directory item */
+ tab = (tab_item *)calloc(1, sizeof (tab_item));
+ if (tab == NULL) {
+ __nis_release_item(dir, &dirs, RO);
+ return (NULL);
+ }
+ tab->item.name = strdup(tobj->zo_name);
+ if (tab->item.name == NULL) {
+ free((void *)tab);
+ __nis_release_item(dir, &dirs, RO);
+ return (NULL);
+ }
+ tab->dir = dir;
+ if (!nis_insert_item_rw((NIS_HASH_ITEM *)tab, &dir->tables, rw)) {
+ free((void *)tab->item.name);
+ free((void *)tab);
+ __nis_release_item(dir, &dirs, RO);
+ return (NULL);
+ }
+
+ cols = tobj->TA_data.ta_cols.ta_cols_val;
+ ncols = tobj->TA_data.ta_cols.ta_cols_len;
+
+ /*
+ * We keep a copy of each of the columns in an array so that
+ * we can load the entries for each one. Each element of the
+ * array is also stored in 'tab'.
+ */
+ collist = (col_item **)calloc(ncols, sizeof (col_item *));
+ if (collist == NULL) {
+ release_table(tab, 1);
+ return (NULL);
+ }
+
+ for (i = 0; i < ncols; i++) {
+ multival = 0;
+ if ((cols[i].tc_flags & TA_SEARCHABLE) == 0 &&
+ (cols[i].tc_flags & TA_MULTIVAL) != 0) {
+ multival = 1;
+ col = &cols[i];
+ }
+#ifdef FORCE_GROUP
+ if (!multival) {
+ if (i == 3 &&
+ strcasecmp(tobj->TA_data.ta_type,
+ "group_tbl") == 0) {
+ multival = 1;
+ colbuf = cols[i];
+ colbuf.tc_flags |= ((',' << 24) & 0xff000000);
+ col = &colbuf;
+ }
+ }
+#endif /* FORCE_GROUP */
+ if (multival) {
+ collist[i] = load_column(tab, col, i, RO);
+ if (collist[i] == NULL) {
+ int c;
+
+ for (c = 0; c++; c < i)
+ release_column(collist[c], 0);
+ release_table(tab, 1);
+ free((void *)collist);
+ return (NULL);
+ }
+ }
+ }
+
+ sprintf(name, "%s.%s", tobj->zo_name, tobj->zo_domain);
+ table = internal_table_name(name, tblbuf);
+ if (table == NULL) {
+ for (i = 0; i < ncols; i++)
+ release_column(collist[i], 0);
+ release_table(tab, 1);
+ free((void *)collist);
+ return (NULL);
+ }
+
+ res = db_list_flags(name, 0, NULL, FN_NORAGS);
+ if (res == 0 ||
+ (res->status != NIS_SUCCESS && res->status != NIS_NOTFOUND)) {
+ for (i = 0; i < ncols; i++) {
+ release_column(collist[i], 0);
+ }
+ release_table(tab, 1);
+ free((void *)collist);
+ if (res != 0)
+ free_db_list(res);
+ return (NULL);
+ }
+
+ tab->res = res;
+ objs = tab->res->objs;
+ for (i = 0; i < tab->res->numo; i++) {
+ for (j = 0; j < ncols; j++) {
+ if (collist[j]) {
+ char *p;
+ int len;
+
+ /*
+ * Make sure we have a valid column string
+ * before calling load_entry.
+ */
+ p = ENTRY_VAL(objs[i].o,
+ (collist[j])->colnum);
+ len = ENTRY_LEN(objs[i].o,
+ (collist[j])->colnum);
+ if (p == NULL || len == 0 || p[0] == '\0' ||
+ (len > 0 && p[len-1] != '\0'))
+ continue;
+
+ if (!(tmpent = load_entry(collist[j],
+ objs[i].o, RO))) {
+ int c;
+
+ for (c = 0; c < ncols; c++)
+ release_column(collist[c], 0);
+ release_table(tab, 1);
+ free((void *)collist);
+ return (NULL);
+ }
+ release_entry(tmpent, 0);
+ }
+ }
+ }
+
+ for (i = 0; i < ncols; i++)
+ release_column(collist[i], 0);
+
+ free((void *)collist);
+
+ return (tab);
+}
+
+static
+void
+free_table(tab_item *tab)
+{
+ col_item *col;
+
+ while ((col = (col_item *)nis_pop_item(&tab->columns)) != NULL) {
+ free_column(col);
+ }
+ free((void *)tab->item.name);
+ free_db_list(tab->res);
+ free((void *)tab);
+}
+
+static
+void
+free_column(col_item *col)
+{
+ ent_item *ent;
+
+ while ((ent = (ent_item *)nis_pop_item(&col->entries)) != NULL) {
+ free_entry(ent);
+ }
+ free((void *)col->item.name);
+ free((void *)col);
+}
+
+static
+void
+free_entry(ent_item *ent)
+{
+ free((void *)ent->item.name);
+ free((void *)ent->obj);
+ free((void *)ent);
+}
+
+static
+void
+strlower(char *s)
+{
+ while (*s) {
+ if (isupper(*s))
+ *s = tolower(*s);
+ s++;
+ }
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_ns_proc.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_ns_proc.c
new file mode 100644
index 0000000000..6401e7af87
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_ns_proc.c
@@ -0,0 +1,1038 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 1990-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Ported from :
+ * "@(#)nis_ns_proc.c 1.15 91/03/01 Copyr 1990 Sun Micro";
+ *
+ * nis_ns_proc.c
+ *
+ * This module contains the actual implementation of NIS version 3.
+ * 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.
+ *
+ * This module contains the Namespace manipulation procedures.
+ */
+
+#include <stdio.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <rpc/rpc.h>
+#include <rpc/clnt.h>
+#include <rpc/svc.h>
+#include <rpc/auth.h>
+#include <rpc/auth_des.h>
+#include <rpcsvc/nis.h>
+#include "nis_proc.h"
+
+extern bool_t xdr_nis_result();
+
+extern nis_object* get_root_object();
+extern int update_root_object(nis_name, nis_object *);
+extern void add_pingitem_with_name(char *buf, nis_object *dir,
+ u_long ptime, NIS_HASH_TABLE *tbl);
+extern nis_name __nis_local_root();
+extern char *relative_name();
+
+/*
+ * Handle the case in which we have the directory object but not its table.
+ * It is called when we try to lookup an item and get back a NIS_NOSUCHTABLE.
+ * On master, table should always exist; missing due to a mkdir failure
+ * master: create table & return (item) not found
+ * master & readonly: return system error
+ * On replica: indicates we have not seen directory's update yet.
+ * replica: add to pinglist and return NIS_NOT_ME
+ * replica & readonly: return NIS_NOT_ME
+ */
+nis_error
+recover_from_no_table(nis_object* d_obj, nis_name dirname)
+{
+ if (we_serve(&(d_obj->DI_data), MASTER_ONLY)) {
+ if (readonly)
+ return (NIS_TRYAGAIN);
+ else {
+ /* create table */
+ nis_error status;
+
+ if ((status = db_create(dirname, &tbl_prototype)) !=
+ NIS_SUCCESS) {
+ syslog(LOG_ERR,
+ "Unable to create table %s: %s.",
+ dirname, nis_sperrno(status));
+ return (status);
+ } else
+ return (NIS_SUCCESS);
+ }
+ } else {
+ /* replica */
+ if (!readonly) /* force update */
+ add_pingitem(d_obj, time(0), &upd_list);
+ return (NIS_NOT_ME);
+ }
+}
+
+/*
+ * nis_lookup is the basic function used by clients of the name service.
+ * This function translates the request into an access of the local
+ * database.
+ */
+nis_result *
+nis_lookup_svc(argp, reqstp)
+ ns_request *argp;
+ struct svc_req *reqstp;
+{
+ nis_result *res;
+ nis_db_result *dbres;
+ nis_object *d_obj = 0;
+ char pname[1024];
+ struct ticks t;
+ int read_dir, /* read access on directory */
+ read_obj; /* read access on an object */
+ nis_error xx;
+ char *p;
+
+ if (verbose)
+ syslog(LOG_INFO, "LOOKUP_SVC : '%s'", argp->ns_name);
+ __start_clock(0);
+ res = (nis_result *)XCALLOC(1, sizeof (nis_result));
+ add_cleanup((void (*)()) XFREE, (char *)(res), "ns_lookup result");
+
+ if (reqstp)
+ nis_getprincipal(pname, reqstp);
+ else
+ pname[0] = '\0';
+
+ if ((p = relative_name(argp->ns_name)) == NULL) {
+ int want_root;
+ nis_name root_dir = __nis_local_root();
+
+ want_root = root_dir &&
+ (nis_dir_cmp(argp->ns_name, root_dir) == SAME_NAME);
+ if (!want_root)
+ res->status = NIS_NOT_ME;
+ else {
+ if (! root_server) {
+ /* maybe we have been added */
+ xx = __directory_object(argp->ns_name,
+ &t, 0, &d_obj);
+ res->aticks = t.aticks;
+ res->dticks = t.dticks;
+ res->zticks = t.zticks;
+ res->cticks = t.cticks;
+ if (d_obj == NULL)
+ res->status = NIS_NOT_ME;
+ else {
+ /* make copy to be returned */
+ nis_object* clone;
+
+ clone = nis_clone_object(d_obj, 0);
+ d_obj = clone;
+ }
+ } else {
+ d_obj = get_root_object();
+ if (d_obj == NULL)
+ res->status = NIS_NAMEUNREACHABLE;
+ }
+
+ if (d_obj) {
+ read_dir = __can_do(NIS_READ_ACC,
+ d_obj->zo_access,
+ d_obj, pname);
+ if (! read_dir) {
+ res->status = NIS_PERMISSION;
+ nis_destroy_object(d_obj);
+ } else {
+ NIS_RES_OBJECT(res) = d_obj;
+ NIS_RES_NUMOBJ(res) = 1;
+ res->status = NIS_SUCCESS;
+ add_cleanup(nis_destroy_object,
+ (char *)(d_obj),
+ "ns_lookup objects");
+ }
+ }
+ }
+ res->zticks = __stop_clock(0);
+ return (res);
+ }
+ free((void *)p);
+
+ /*
+ * Make sure we are allowed to READ this directory.
+ *
+ * POLICY: servers _must_ be able to read their own directory
+ * objects.
+ */
+ xx = __directory_object(nis_domain_of(argp->ns_name), &t, 0, &d_obj);
+ res->aticks = t.aticks;
+ res->dticks = t.dticks;
+ res->zticks = t.zticks;
+ res->cticks = t.cticks;
+ if (d_obj == NULL) {
+ res->status = xx; /* status from __directory_object call */
+ res->zticks = __stop_clock(0);
+ return (res);
+ }
+
+ read_dir = __can_do(NIS_READ_ACC, d_obj->zo_access, d_obj, pname);
+
+ /* Look it up in the data base */
+ dbres = db_lookup(argp->ns_name);
+ if (dbres->status != NIS_SUCCESS) {
+ if (dbres->status == NIS_NOSUCHTABLE) {
+ /* This means table of domain does not exist */
+ dbres->status =
+ recover_from_no_table(d_obj,
+ nis_domain_of(argp->ns_name));
+ /* for sure, argp->ns_name will not be there */
+ if (dbres->status == NIS_SUCCESS)
+ dbres->status = NIS_NOTFOUND;
+ }
+ if (read_dir) {
+ res->status = dbres->status;
+ res->dticks = dbres->ticks;
+ } else {
+ res->status = NIS_PERMISSION;
+ res->dticks = 0;
+ }
+ res->zticks = __stop_clock(0);
+ if (verbose)
+ syslog(LOG_INFO, "nis_lookup : exit status %s",
+ nis_sperrno(res->status));
+ return (res);
+ }
+
+ read_obj = __can_do(NIS_READ_ACC, dbres->obj->zo_access,
+ dbres->obj, pname);
+
+ if (! read_dir && ! read_obj) {
+ res->status = NIS_PERMISSION;
+ res->zticks = __stop_clock(0);
+ if (verbose)
+ syslog(LOG_INFO,
+ "nis_lookup : exit status PERMISSION DENIED");
+ return (res);
+ }
+
+ res->status = NIS_SUCCESS;
+ res->objects.objects_len = 1;
+ res->objects.objects_val = dbres->obj;
+ res->dticks = dbres->ticks;
+ res->zticks = __stop_clock(0);
+
+ if (verbose)
+ syslog(LOG_INFO, "nis_lookup : exit status %s",
+ nis_sperrno(res->status));
+
+ return (res);
+}
+
+/*
+ * Internal function that adds an object into the namespace.
+ */
+static void
+add_object(name, obj, dobj, princp, res)
+ nis_name name;
+ nis_object *obj,
+ *dobj;
+ nis_name princp;
+ nis_result *res;
+{
+ directory_obj *d;
+ nis_db_result *dbres;
+ u_long xid, ttime;
+ int i;
+ int add_ok = 0;
+
+ /*
+ * d points at the directory specific information
+ */
+ d = &(dobj->DI_data);
+
+ dbres = db_lookup(name);
+ res->dticks += dbres->ticks;
+ if (dbres->status != NIS_NOTFOUND) {
+ if (dbres->status == NIS_SUCCESS)
+ res->status = NIS_NAMEEXISTS;
+ else
+ res->status = dbres->status;
+ return;
+ }
+
+ /*
+ * If we are adding a directory, make sure that it is a legitimate
+ * name, i.e. the new directory must be a descendant of the domain
+ * whose server is making this request. If we are in the root domain,
+ * then we can add a new directory object in the same domain.
+ */
+ if (__type_of(obj) == NIS_DIRECTORY_OBJ) {
+ switch (nis_dir_cmp(obj->DI_data.do_name, dobj->DI_data.do_name)) {
+ case LOWER_NAME:
+ break;
+ case SAME_NAME:
+ if (root_server) break;
+ default:
+ res->status = NIS_BADNAME;
+ return;
+ }
+ }
+
+ /*
+ * Verify access rights. Check first to see if the directory object
+ * grants the right. If not, then check to see if the object access
+ * rights for that type of object grant the right. And if that
+ * doesn't then abort with PERMISSION denied.
+ */
+ add_ok = __can_do(NIS_CREATE_ACC, dobj->zo_access, dobj, princp);
+ if (! add_ok) {
+ if (auth_verbose) {
+ syslog(LOG_INFO,
+ "add_object : create DENIED by directory %s.%s",
+ dobj->zo_name, dobj->zo_domain);
+ }
+ for (i = 0; i < d->do_armask.do_armask_len; i++) {
+ if (obj->zo_data.zo_type == OATYPE(d, i)) {
+ add_ok = __can_do(NIS_CREATE_ACC,
+ OARIGHTS(d, i), dobj, princp);
+ if (auth_verbose) {
+ syslog(LOG_INFO,
+ "add_object : create %s by object type.",
+ (add_ok) ? "ALLOWED" : "DENIED");
+ }
+ break;
+ }
+ }
+ } else if (auth_verbose) {
+ syslog(LOG_INFO,
+ "add_object : create is ALLOWED by the directory.");
+ }
+
+ if (! add_ok) {
+ res->status = NIS_PERMISSION;
+ return;
+ }
+
+ /*
+ * Start the transaction, if we can't get an XID abort
+ */
+ xid = begin_transaction(princp);
+ if (! xid) {
+ res->status = NIS_TRYAGAIN;
+ return;
+ }
+
+ /* Do the add operation */
+ obj->zo_oid.ctime = (unsigned long)time(0);
+ obj->zo_oid.mtime = obj->zo_oid.ctime;
+ dbres = db_add(name, obj, 0);
+ res->dticks += dbres->ticks;
+ res->status = dbres->status;
+ if (res->status != NIS_SUCCESS)
+ abort_transaction(xid);
+ else {
+ ttime = (u_long)(time(0));
+ end_transaction(xid); /* COMMIT */
+ /* Notify replicates of the change. */
+ if (dobj->DI_data.do_servers.do_servers_len > 1)
+ add_pingitem(dobj, ttime, &ping_list);
+ }
+ return;
+
+}
+
+/*
+ * Internal function that removes an object from the namespace.
+ */
+static void
+remove_object(name, obj, dobj, princp, res)
+ nis_name name;
+ nis_object *obj,
+ *dobj;
+ nis_name princp;
+ nis_result *res;
+{
+ directory_obj *d;
+ nis_object *old;
+ nis_db_result *dbres;
+ u_long xid, ttime;
+ int i, rem_ok;
+
+ /*
+ * d points at the directory specific information
+ */
+ d = &(dobj->DI_data);
+
+ dbres = db_lookup(name);
+ res->dticks += dbres->ticks;
+ if (dbres->status != NIS_SUCCESS) {
+ res->status = dbres->status;
+ return;
+ }
+
+ /* This is the existing object in the database */
+ old = dbres->obj;
+
+ if (obj && ! same_oid(obj, old)) {
+ res->status = NIS_NOTSAMEOBJ;
+ return;
+ }
+
+ /*
+ * Verify delete access rights. Check first to see if the directory
+ * object grants the right. If not, then check to see if the object
+ * itself grants the right, then finally check to see of we have
+ * this access right for this type of object.
+ */
+ rem_ok = __can_do(NIS_DESTROY_ACC, dobj->zo_access, dobj, princp);
+ if (auth_verbose) {
+ syslog(LOG_INFO,
+ "remove_object : destroy %s by directory %s.%s",
+ (rem_ok) ? "ALLOWED" : "DENIED",
+ dobj->zo_name, dobj->zo_domain);
+ }
+ if (! rem_ok) {
+ rem_ok = __can_do(NIS_DESTROY_ACC, old->zo_access, old, princp);
+ if (auth_verbose) {
+ syslog(LOG_INFO,
+ "remove_object : destroy %s by object %s.%s",
+ (rem_ok) ? "ALLOWED" : "DENIED",
+ old->zo_name, old->zo_domain);
+ }
+ }
+
+ if (! rem_ok) {
+ for (i = 0; i < d->do_armask.do_armask_len; i++) {
+ if (old->zo_data.zo_type == OATYPE(d, i)) {
+ rem_ok = __can_do(NIS_DESTROY_ACC,
+ OARIGHTS(d, i), dobj, princp);
+ break;
+ }
+ }
+ }
+ if (! rem_ok) {
+ res->status = NIS_PERMISSION;
+ if (auth_verbose) {
+ syslog(LOG_INFO,
+ "remove_object : destroy DENIED by object type.");
+ }
+ return;
+ } else if (auth_verbose) {
+ syslog(LOG_INFO,
+ "remove_object : destroy ALLOWED by object type.");
+ }
+
+ /*
+ * Start the transaction, if we can't get an XID abort
+ */
+ xid = begin_transaction(princp);
+ if (! xid) {
+ res->status = NIS_TRYAGAIN;
+ return;
+ }
+
+ /* Do the remove operation */
+ dbres = db_remove(name, old, time(0));
+ res->dticks += dbres->ticks;
+ res->status = dbres->status;
+ if (res->status != NIS_SUCCESS)
+ abort_transaction(xid);
+ else {
+ ttime = (u_long)(time(0));
+ end_transaction(xid); /* COMMIT */
+ /* Notify replicates of the change. */
+ if (dobj->DI_data.do_servers.do_servers_len > 1)
+ add_pingitem(dobj, ttime, &ping_list);
+ /*
+ * No need to flush the caches - they have been flushed
+ * inside db_remove
+ */
+ }
+}
+
+/* macro to get to the table column structures */
+#define TABLE_COL(o, n) o->TA_data.ta_cols.ta_cols_val[n]
+
+/*
+ * Internal function that modifies an object in the namespace.
+ */
+static void
+modify_object(name, obj, dobj, princp, res)
+ nis_name name;
+ nis_object *obj,
+ *dobj;
+ nis_name princp;
+ nis_result *res;
+{
+ directory_obj *d;
+ nis_object *old;
+ nis_db_result *dbres;
+ u_long xid, ttime;
+ int i, mod_ok;
+ log_entry le;
+ nis_object mod_obj;
+
+ /*
+ * d points at the directory specific information
+ */
+ d = &(dobj->DI_data);
+
+ dbres = db_lookup(name);
+ res->dticks += dbres->ticks;
+ if (dbres->status != NIS_SUCCESS) {
+ res->status = dbres->status;
+ return;
+ }
+
+ /* This is the existing object in the database */
+ old = dbres->obj;
+
+ /*
+ * Verify modify access rights. Check first to see if the directory
+ * object grants the right. If not, then check to see if the object
+ * itself grants the right, then finally check to see if we have
+ * this access right for this type of object.
+ */
+ mod_ok = __can_do(NIS_MODIFY_ACC, dobj->zo_access, dobj, princp);
+ if (auth_verbose) {
+ syslog(LOG_INFO,
+ "modify_object : modify %s by directory %s.%s",
+ (mod_ok) ? "ALLOWED" : "DENIED",
+ dobj->zo_name, dobj->zo_domain);
+ }
+ if (! mod_ok) {
+ mod_ok = __can_do(NIS_MODIFY_ACC, old->zo_access, old, princp);
+ if (auth_verbose) {
+ syslog(LOG_INFO,
+ "remove_object : modify %s by object %s.%s",
+ (mod_ok) ? "ALLOWED" : "DENIED",
+ old->zo_name, old->zo_domain);
+ }
+ }
+
+ if (! mod_ok) {
+ for (i = 0; i < d->do_armask.do_armask_len; i++) {
+ if (old->zo_data.zo_type == OATYPE(d, i)) {
+ if (! __can_do(NIS_MODIFY_ACC, OARIGHTS(d, i),
+ dobj, princp)) {
+ res->status = NIS_PERMISSION;
+ if (auth_verbose) {
+ syslog(LOG_INFO,
+ "modify_object : modify DENIED by object type.");
+ }
+ return;
+ } else {
+ if (auth_verbose) {
+ syslog(LOG_INFO,
+ "modify_object : modify ALLOWED by object type.");
+ }
+ mod_ok = 1;
+ break;
+ }
+ }
+ }
+ }
+ /*
+ * if no one allows modify then fail.
+ */
+ if (! mod_ok) {
+ res->status = NIS_PERMISSION;
+ return;
+ }
+
+ /*
+ * POLICY : Allow changing type ?
+ *
+ * ANSWER : No, only changing the meta data
+ * such as access etc is allowed.
+ */
+ if (__type_of(old) != __type_of(obj)) {
+ res->status = NIS_BADOBJECT;
+ return;
+ }
+
+ if (nis_dir_cmp(old->zo_domain, obj->zo_domain) != SAME_NAME) {
+ res->status = NIS_BADNAME;
+ return;
+ }
+
+ /*
+ * Start the transaction, if we can't get an XID abort
+ */
+ xid = begin_transaction(princp);
+ if (! xid) {
+ res->status = NIS_TRYAGAIN;
+ return;
+ }
+
+ /*
+ * Now we generate a log entry with the old value so that if
+ * we crash, the transaction system can restore the object
+ * to its pre-modified state.
+ */
+ memset((char *)&le, 0, sizeof (le));
+ le.le_type = MOD_NAME_OLD;
+ le.le_time = (u_long) (time(0));
+ le.le_name = name;
+ le.le_attrs.le_attrs_len = 0;
+ le.le_object = *old;
+ add_update(&le);
+
+ mod_obj = *old;
+ if (same_oid(old, obj)) {
+ mod_obj.zo_owner = obj->zo_owner;
+ mod_obj.zo_group = obj->zo_group;
+ mod_obj.zo_access = obj->zo_access;
+ mod_obj.zo_ttl = obj->zo_ttl;
+ } else if (obj->zo_oid.mtime || obj->zo_oid.ctime) {
+ res->status = NIS_NOTSAMEOBJ;
+ abort_transaction(xid);
+ return;
+ }
+
+ /*
+ * Data replacement of the variant part.
+ * NOTE: We play games with TABLE objects because we can't
+ * allow their schema to change on the fly. But there are
+ * fields in the table data that _can_ change such as the
+ * path of the table, separator character, and access rights.
+ *
+ * As in the case of ENTRY objects, we let the 'same oid' test be
+ * the signal that it is ok to overwrite these variables. This
+ * is overloading the OID value but I can't think of any other
+ * way to signal this operation without changing the protocol at
+ * this point.
+ */
+ if (__type_of(old) != NIS_TABLE_OBJ)
+ mod_obj.zo_data = obj->zo_data;
+ else if (same_oid(old, obj)) {
+ mod_obj.TA_data.ta_path = obj->TA_data.ta_path;
+ mod_obj.TA_data.ta_sep = obj->TA_data.ta_sep;
+ mod_obj.TA_data.ta_type = obj->TA_data.ta_type;
+ for (i = 0; i < mod_obj.TA_data.ta_maxcol; i++) {
+ if (TABLE_COL(obj, i).tc_flags & TA_MODIFIED)
+ TABLE_COL((&mod_obj), i).tc_rights =
+ TABLE_COL(obj, i).tc_rights;
+ }
+ }
+
+ mod_obj.zo_oid.mtime = (unsigned long)time(0);
+ /*
+ * Now we add the object over the previous
+ * one. This instructs the database to
+ * discard the current one and replace it
+ * with our modified one. (And it's atomic)
+ */
+ dbres = db_add(name, &mod_obj, 1);
+ res->dticks += dbres->ticks;
+ res->status = dbres->status;
+ if (res->status != NIS_SUCCESS)
+ abort_transaction(xid);
+ else {
+ nis_taglist taglist;
+ nis_tag tags;
+
+ ttime = (u_long)(time(0));
+ end_transaction(xid); /* COMMIT */
+
+ /* Notify replicates of the change. */
+ if (dobj->DI_data.do_servers.do_servers_len > 1)
+ add_pingitem(dobj, ttime, &ping_list);
+
+ /*
+ * No need to flush the caches - they have been flushed
+ * inside db_remove
+ */
+ if (__type_of(old) == NIS_DIRECTORY_OBJ) {
+ /*
+ * Send all the servers that serve this particular
+ * directory object a flush_cache message.
+ * We dont do this for group and table caches because
+ * they are served by those servers; and we can
+ * just hope that they get the updates real fast.
+ * For Directory objects, since we dont want the servers
+ * to throw out the cached directory objects and then go
+ * and refresh it with the stale copies, we will use
+ * TAG_DCACHE_ONE_REFRESH which will
+ * get a fresh copy of the object from the master.
+ *
+ * XXX: Since we are sending this message to all
+ * servers (including MASTER), this will lead to
+ * multiple calls to the master. One could have
+ * perhaps passed a modified directory object
+ * without the master.
+ */
+ tags.tag_type = TAG_DCACHE_ONE_REFRESH;
+ tags.tag_val = old->DI_data.do_name;
+ taglist.tags.tags_len = 1;
+ taglist.tags.tags_val = &tags;
+ (void) nis_mcast_tags(&old->DI_data, &taglist);
+ }
+ }
+}
+
+/*
+ * If prim and sec contain the same servers, return 0.
+ * If sec contains different servers than prim, result is set to:
+ * <all prim servers> <sec servers that are not in prim's list>
+ */
+static int
+merge_srv_list(directory_obj* prim, directory_obj* sec, nis_server**result)
+{
+ nis_server* answer = 0;
+ int psize = prim->do_servers.do_servers_len;
+ int ssize = sec->do_servers.do_servers_len;
+ nis_server* prim_server = prim->do_servers.do_servers_val;
+ nis_server* sec_server = sec->do_servers.do_servers_val;
+ int i, j, diff = 0, newsize = 0, newadd;
+
+ /* First count how many names in sec are different */
+ for (i = 0; i < ssize; i++) {
+ for (j = 0; j < psize; j++) {
+ if (nis_dir_cmp(prim_server[j].name,
+ sec_server[i].name) == SAME_NAME)
+ break;
+ }
+ if (j == psize)
+ ++diff;
+ }
+
+ if (diff == 0)
+ return (0);
+
+ /* different names were found on sec_server list */
+ newsize = psize+diff;
+ answer = (nis_server*)malloc(newsize*sizeof (nis_server));
+
+ if (answer == 0)
+ return (0);
+
+ /* Copy prim first */
+ for (i = 0; i < psize; i++)
+ answer[i] = prim_server[i];
+
+ newadd = i;
+ for (i = 0; i < ssize; i++) {
+ for (j = 0; j < psize; j++) {
+ if (nis_dir_cmp(prim_server[j].name,
+ sec_server[i].name) == SAME_NAME)
+ break;
+ }
+ if (j == psize)
+ answer[newadd++] = sec_server[i];
+ }
+
+ *result = answer;
+ return (newsize);
+}
+
+static void
+modify_root_object(nis_name name,
+ nis_object *obj,
+ nis_name princp,
+ nis_result *res)
+{
+ nis_object* oldroot = get_root_object();
+ nis_object newroot;
+ u_long ttime;
+
+ if (! oldroot) {
+ syslog(LOG_ERR, "Cannot read %s!", ROOT_OBJ);
+ res->status = NIS_SYSTEMERROR;
+ return;
+ }
+
+ if (__can_do(NIS_MODIFY_ACC, oldroot->zo_access, oldroot, princp)) {
+ newroot = *oldroot; /* copy all fields from old object */
+
+ if (same_oid(oldroot, obj)) {
+ /* updating non-data portion as well. */
+ newroot.zo_owner = obj->zo_owner;
+ newroot.zo_group = obj->zo_group;
+ newroot.zo_access = obj->zo_access;
+ newroot.zo_ttl = obj->zo_ttl;
+ } else if (obj->zo_oid.mtime || obj->zo_oid.ctime) {
+ /* somehow got an outdated copy of object. */
+ res->status = NIS_NOTSAMEOBJ;
+ nis_destroy_object(oldroot);
+ return;
+ } /* else, only interested in changing data portion. */
+
+ newroot.DI_data = obj->DI_data; /* always update data */
+ ttime = (u_long)(time(0));
+ newroot.zo_oid.mtime = ttime;
+ if (update_root_object(name, &newroot)) {
+ nis_server* merged_srvs = 0;
+ int howmany;
+
+ howmany = merge_srv_list(&(newroot.DI_data),
+ &(oldroot->DI_data),
+ &merged_srvs);
+ /* howmany is 0 if new and old list are the same */
+ if (howmany > 0) {
+ directory_obj *dobj = &(newroot.DI_data);
+ dobj->do_servers.do_servers_val = merged_srvs;
+ dobj->do_servers.do_servers_len = howmany;
+ }
+ if (newroot.DI_data.do_servers.do_servers_len > 1)
+ add_pingitem_with_name(ROOT_OBJ,
+ &newroot,
+ ttime,
+ &ping_list);
+ if (merged_srvs)
+ free(merged_srvs);
+ res->status = NIS_SUCCESS;
+ } else
+ res->status = NIS_MODFAIL;
+ } else
+ res->status = NIS_PERMISSION;
+
+ /* newroot just copies contents of other objects; need not be freed */
+ nis_destroy_object(oldroot);
+}
+
+/*
+ * __nis_nameops()
+ *
+ * This code actually implements the operation that is requested.
+ * It is collected here in one place.
+ */
+static nis_result *
+nis_nameops(op, name, obj, princp)
+ int op; /* operation to perform */
+ nis_name name; /* Object's name */
+ nis_object *obj; /* Object to use (NULL for rem) */
+ nis_name princp; /* Principal making the request */
+{
+ nis_result *res;
+ nis_object *d_obj;
+ struct ticks t;
+ nis_error xx;
+ char optxt[32];
+
+ res = (nis_result *)XCALLOC(1, sizeof (nis_result));
+ add_cleanup((void (*)())XFREE, (char *)res, "nameops result");
+ if (readonly) {
+ res->status = NIS_TRYAGAIN;
+ return (res);
+ }
+
+ if (auth_verbose) {
+ switch (op) {
+ case ADD_OP :
+ strcpy(optxt, "ADD");
+ break;
+ case REM_OP :
+ strcpy(optxt, "REMOVE");
+ break;
+ case MOD_OP :
+ strcpy(optxt, "MODIFY");
+ break;
+ }
+ syslog(LOG_INFO, "Object operation '%s' for principal %s",
+ optxt, princp);
+ }
+
+ if (((op == ADD_OP) || (op == MOD_OP)) && (obj == NULL)) {
+ res->status = NIS_BADOBJECT;
+ return (res);
+ }
+
+ /*
+ * don't allow an object to be added without an owner.
+ */
+ if ((op == ADD_OP) && (strlen(obj->zo_owner) < 2)) {
+ res->status = NIS_BADOBJECT;
+ return (res);
+ }
+
+ /*
+ * We have to handle the root object specially if we want to
+ * manipulate it using the NIS code.
+ */
+ if (root_server &&
+ (nis_dir_cmp(name, __nis_rpc_domain()) == SAME_NAME)) {
+
+ /* All we allow is a modify */
+ if (op != MOD_OP) {
+ res->status = NIS_BADREQUEST;
+ return (res);
+ }
+ modify_root_object(name, obj, princp, res);
+ return (res);
+ }
+
+ /*
+ * Fetch the directory object for this domain.
+ */
+ xx = __directory_object(nis_domain_of(name), &t, TRUE, &d_obj);
+ res->aticks = t.aticks;
+ res->dticks = t.dticks;
+ res->zticks = t.zticks;
+ res->cticks = t.cticks;
+ if ((xx != NIS_SUCCESS) && (xx != NIS_S_SUCCESS)) {
+ res->status = xx;
+ return (res);
+ }
+
+ /*
+ * Get the last update time. If the current time is earlier
+ * someone has set the clock back which is very bad as far
+ * as NIS+ is concerned. This will catch almost all possible
+ * situations except the single second when time has finally
+ * caught up with the last entry in the log. Fixing this
+ * would require more radical changes to the whole update
+ * mechanism which is potentially risky and may even require
+ * protocol changes.
+ */
+ if (last_update(d_obj->DI_data.do_name) > time(0)) {
+ syslog(LOG_ERR, "Update rejected because system time is "
+ "earlier than most recent log entry");
+ res->status = NIS_SYSTEMERROR;
+ return (res);
+ }
+
+ switch (op) {
+ case ADD_OP :
+ add_object(name, obj, d_obj, princp, res);
+ break;
+ case REM_OP :
+ remove_object(name, obj, d_obj, princp, res);
+ break;
+ case MOD_OP :
+ modify_object(name, obj, d_obj, princp, res);
+ break;
+ }
+
+ return (res);
+}
+
+/*
+ * nis_add, this function will add a name to the name space if the
+ * permissions are allowed. The object that is about to be added to
+ * must allow "CREATE" access to the principal making the request.
+ */
+nis_result *
+nis_add_svc(argp, reqstp)
+ ns_request *argp;
+ struct svc_req *reqstp;
+{
+ nis_result *res;
+ char pname[1024];
+ char *p;
+
+ __start_clock(0); /* Timing information */
+ if (verbose)
+ syslog(LOG_INFO, "ADD_SVC : '%s'", argp->ns_name);
+ if ((p = relative_name(argp->ns_name)) == NULL)
+ return (nis_make_error(NIS_BADNAME, 0, 0, 0, __stop_clock(0)));
+ free((void *)p);
+
+ nis_getprincipal(pname, reqstp);
+ res = nis_nameops(ADD_OP, argp->ns_name,
+ argp->ns_object.ns_object_val, pname);
+ if (verbose)
+ syslog(LOG_INFO, "nis_add : exit status %s",
+ nis_sperrno(res->status));
+
+ res->zticks += __stop_clock(0);
+ return (res);
+}
+
+/*
+ * nis_modify, this function modifies the contents of the object that
+ * are named. It checks the MODIFY access right in the existing object
+ * and then does the operation.
+ */
+nis_result *
+nis_modify_svc(argp, reqstp)
+ ns_request *argp;
+ struct svc_req *reqstp;
+{
+ nis_result *res;
+ char pname[1024];
+ char *p = NULL;
+
+ __start_clock(0);
+ if (verbose)
+ syslog(LOG_INFO, "MODIFY_SVC : '%s'", argp->ns_name);
+ if (! root_server && ((p = relative_name(argp->ns_name)) == NULL))
+ return (nis_make_error(NIS_BADNAME, 0, 0, 0, __stop_clock(0)));
+ if (p)
+ free((void *)p);
+
+ nis_getprincipal(pname, reqstp);
+ res = nis_nameops(MOD_OP, argp->ns_name,
+ argp->ns_object.ns_object_val, pname);
+ if (verbose)
+ syslog(LOG_INFO, "nis_modify : exit status %s",
+ nis_sperrno(res->status));
+ res->zticks += __stop_clock(0);
+ return (res);
+}
+
+/*
+ * nis_remove removes and object from the name space. You will notice it
+ * shares a lot of code with the Add and remove functions.
+ */
+nis_result *
+nis_remove_svc(argp, reqstp)
+ ns_request *argp;
+ struct svc_req *reqstp;
+{
+ nis_result *res;
+ char *principal, pname[1024];
+ char *p;
+
+ __start_clock(0);
+ if (verbose)
+ syslog(LOG_INFO, "REMOVE_SVC : '%s'", argp->ns_name);
+ if ((p = relative_name(argp->ns_name)) == NULL)
+ return (nis_make_error(NIS_BADNAME, 0, 0, 0, __stop_clock(0)));
+ free((void *)p);
+
+ /*
+ * If 'reqstp' is NULL, we're being called internally, and supply
+ * the local principal.
+ */
+ if (reqstp != 0) {
+ nis_getprincipal(pname, reqstp);
+ principal = pname;
+ } else {
+ principal = nis_local_principal();
+ }
+ res = nis_nameops(REM_OP, argp->ns_name,
+ argp->ns_object.ns_object_val,
+ principal);
+ if (verbose)
+ syslog(LOG_INFO, "nis_remove : exit status %s",
+ nis_sperrno(res->status));
+ res->zticks += __stop_clock(0);
+ return (res);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_opacc.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_opacc.c
new file mode 100644
index 0000000000..79f8d7ee31
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_opacc.c
@@ -0,0 +1,400 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 1998-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <malloc.h>
+#include <syslog.h>
+#include <string.h>
+#include <rpc/rpc.h>
+#include <rpc/svc.h>
+#include <rpcsvc/nis.h>
+#include "nis_proc.h"
+
+
+typedef enum {STRING, EMPTY, NIL, NONE} __match_class;
+
+static bool_t __nis_dir_op_access(char *, char *, nis_name, char *);
+static __match_class __subop_match_class(char *);
+static __match_class __entry_match_class(char *, entry_obj *);
+
+#define nil(x) (x)?(x):"<NULL>"
+
+#define OP_ACC_TABLE "proto_op_access"
+#define OP_COL "op"
+#define SUBOP_COL "subop"
+#define SUBOP_COL_NUM 1
+#define MAX_OPNAME "NIS_FINDDIRECTORY"
+#define MAX_SUBOPNAME "Make sure this is longer than the longest tag etc."
+
+/*
+ * Verify access to the specified NIS+ protocol operation (and, optionally,
+ * sub-operation). Parameters:
+ *
+ * op Required. Name of operation. Example: "NIS_PING".
+ *
+ * subop Optional. Name of sub-operation. Example: "TAG_DEBUG"
+ * for the "NIS_STATUS" operation.
+ *
+ * dir Optional. NIS+ directory for which check is performed.
+ * If NULL, all directories served by this rpc.nisd are
+ * checked.
+ *
+ * pname Optional. Name of principal. If NULL, this routine
+ * derives the principal name from the reqstp argument.
+ *
+ * reqstp Optional and ignored unless pname == NULL. The RPC
+ * request.
+ */
+bool_t
+nis_op_access(char *op, char *subop, nis_name dir, char *pname,
+ struct svc_req *reqstp) {
+
+ char pnamebuf[1024];
+ char *dirl;
+
+
+ /* No check at security levels 0 and 1 */
+ if (secure_level < 2)
+ return (TRUE);
+
+ /* Sanity check arguments */
+ if (op == 0 ||
+ (pname == 0 && reqstp == 0) ||
+ (strlen(op) > sizeof (MAX_OPNAME)) ||
+ (subop != 0 && strlen(subop) > sizeof (MAX_SUBOPNAME)))
+ return (FALSE);
+
+ /* Get the principal name */
+ if (pname == 0) {
+ pname = pnamebuf;
+ nis_getprincipal(pname, reqstp);
+ }
+
+#ifdef OPACCDEBUG
+ printf("nis_op_access(%s, %s, %s, %s, 0x%x)\n",
+ op, (subop!=0)?subop:"<NULL>", (dir!=0)?dir:"<NULL>",
+ pname, reqstp);
+#endif /* OPACCDEBUG */
+
+ if (dir != 0 && dir[0] != '\0') {
+ return (__nis_dir_op_access(op, subop, dir, pname));
+ } else {
+ char *curdir, *nxtdir;
+
+ if (nis_server_control(SERVING_LIST, DIR_GETLIST, &dirl) == 0)
+ return (FALSE);
+
+#ifdef OPACCDEBUG
+ printf("serving list = \"%s\"\n", dirl);
+#endif /* OPACCDEBUG */
+
+ for (curdir = nxtdir = dirl; *curdir != '\0'; curdir = nxtdir) {
+ while (*nxtdir != ' ' && *nxtdir != '\0')
+ nxtdir++;
+ if (*nxtdir != '\0')
+ *nxtdir++ = '\0';
+ if (!__nis_dir_op_access(op, subop, curdir, pname)) {
+ free(dirl);
+ return (FALSE);
+ }
+ }
+ free(dirl);
+ }
+
+ return (TRUE);
+}
+
+
+static
+bool_t __nis_dir_op_access(char *op, char *subop, nis_name dir, char *pname) {
+
+ char lookup[NIS_MAXNAMELEN + /* dir name */
+ sizeof (OP_ACC_TABLE) + /* table name */
+ sizeof (MAX_OPNAME) + /* search ... */
+ sizeof (MAX_SUBOPNAME)+ /* ... criteria */
+ sizeof ("[=,=]. ")]; /* syntax + NUL */
+ ib_request req;
+ nis_error err;
+ nis_result *res;
+ int i;
+ __match_class required_match, best_entry, be_match, tmp;
+ bool_t ret = TRUE;
+
+
+ if (subop == 0)
+ sprintf(lookup, "[%s=%s]%s.%s", OP_COL, op, OP_ACC_TABLE, dir);
+ else
+ sprintf(lookup, "[%s=%s,%s=%s]%s.%s",
+ OP_COL, op, SUBOP_COL, subop, OP_ACC_TABLE, dir);
+
+ err = nis_get_request(lookup, 0, 0, &req);
+ if (err != NIS_SUCCESS)
+ return (TRUE);
+
+ res = __nis_local_lookup(&req, 0, 1, 0, 0);
+
+ nis_free_request(&req);
+
+#ifdef OPACCDEBUG
+ printf("nis_local_lookup(%s) => 0x%x, status = %d\n",
+ lookup, res, (res!=0)?res->status:-1);
+#endif /* OPACCDEBUG */
+
+ /* No result at all or no such table => assume access OK */
+ if (res == 0)
+ return (TRUE);
+ else if (res->status == NIS_NOSUCHTABLE) {
+ nis_freeresult(res);
+ return (TRUE);
+ }
+
+ /*
+ * If we didn't find any entries, then one of two situations apply:
+ *
+ * (1) We were looking for 'op' only, and if it isn't in the
+ * the table, we allow access.
+ *
+ * (2) We were looking for both 'op' and 'subop'. It's possible
+ * that there's an entry for 'op' only, so try again.
+ */
+ if (res->status == NIS_NOTFOUND || res->status == NIS_PARTIAL) {
+ nis_freeresult(res);
+ if (subop == 0) {
+ return (TRUE);
+ } else {
+ return (__nis_dir_op_access(op, 0, dir, pname));
+ }
+ } else if (res->status != NIS_SUCCESS) {
+ /*
+ * XXX Should we succeed or fail ?
+ * For maximum backward compatibility, we declare success
+ */
+#ifdef OPACCDEBUG
+ for (i = 0; i < NIS_RES_NUMOBJ(res); i++) {
+ printf("\t------------- %d --------------\n", i);
+ nis_print_object(&(NIS_RES_OBJECT(res)[i]));
+ }
+#endif /* OPACCDEBUG */
+ nis_freeresult(res);
+ return (TRUE);
+ }
+
+ /*
+ * If there was more than one result, look for one that:
+ *
+ * (1) has a matching sub-operation, or
+ *
+ * (2) has an empty string in the subop field, or
+ *
+ * (3) has a NIL subop field, or
+ *
+ * (4) has no subop field
+ *
+ * in that order.
+ */
+ if (NIS_RES_NUMOBJ(res) == 1) {
+#ifdef OPACCDEBUG
+ printf("\tone matching entry\n");
+#endif /* OPACCDEBUG */
+ ret = __can_do(NIS_READ_ACC, NIS_RES_OBJECT(res)->zo_access,
+ NIS_RES_OBJECT(res), pname);
+ } else if (NIS_RES_NUMOBJ(res) > 1) {
+ required_match = __subop_match_class(subop);
+ best_entry = 0;
+ be_match = __entry_match_class(subop,
+ &(NIS_RES_OBJECT(res)->EN_data));
+#ifdef OPACCDEBUG
+ printf("\t%d matching entries\n", NIS_RES_NUMOBJ(res));
+#endif /* OPACCDEBUG */
+ for (i = 1; i < NIS_RES_NUMOBJ(res); i++) {
+#ifdef OPACCDEBUG
+ printf("\t%s\n", NIS_RES_OBJECT(res)[i].zo_name);
+#endif /* OPACCDEBUG */
+ tmp = __entry_match_class(subop,
+ &(NIS_RES_OBJECT(res)[i].EN_data));
+ if (tmp <= be_match) {
+ be_match = tmp;
+ best_entry = i;
+ if (be_match <= required_match)
+ break;
+ }
+ }
+#ifdef OPACCDEBUG
+ printf("\trequired_match = %d, be_match = %d, best_entry = %d\n",
+ required_match, be_match, best_entry);
+ printf("\t\t%s %s\n",
+ ENTRY_VAL(&(NIS_RES_OBJECT(res)[best_entry]), 0),
+ nil(ENTRY_VAL(&(NIS_RES_OBJECT(res)[best_entry]), 1)));
+#endif /* OPACCDEBUG */
+ ret = __can_do(NIS_READ_ACC,
+ NIS_RES_OBJECT(res)[best_entry].zo_access,
+ &(NIS_RES_OBJECT(res)[best_entry]), pname);
+ }
+
+ nis_freeresult(res);
+#ifdef OPACCDEBUG
+ printf("\t%s\n", ret?"OK":"NO ACCESS");
+#endif /* OPACCDEBUG */
+ return (ret);
+}
+
+
+/* Map callback pid/tid to anonymous number */
+static callback_id *callback_id_list = NULL;
+static anonid_t callback_anonid = 0;
+static DECLMUTEXLOCK(anonid);
+
+/*
+ * Add id to callback list
+ */
+anonid_t
+nis_add_callback_id(pthread_t id, nis_name pname) {
+ callback_id *entry = malloc(sizeof(callback_id));
+ callback_id *cp;
+ anonid_t anonid;
+
+ if (entry == NULL) {
+ syslog(LOG_WARNING, "nis_add_callback_pid: unable to malloc");
+ return(NOANONID);
+ }
+
+ entry->id = id;
+ entry->pname = strdup(pname);
+
+ MUTEXLOCK(anonid, "nis_add_callback_id");
+
+ /*
+ * In the MT case, we use the thread id as the anonymous id. We do
+ * this because we want to avoid race conditions by having the thread
+ * itself both insert and remove its entry on the callback_id_list.
+ * Hence, since the id is returned by the parent of the thread,
+ * the implication is that the anonymous id must be something known
+ * to both, and the thread id fits that requirement.
+ */
+ entry->anonid = id;
+ cp = callback_id_list;
+ entry->next = cp;
+ callback_id_list = entry;
+ MUTEXUNLOCK(anonid, "nis_add_callback_id");
+
+ return(anonid);
+}
+
+/*
+ * Remove callback id
+ */
+void
+nis_delete_callback_id(pthread_t id) {
+ callback_id *entry, **prev;
+
+ if (id == INV_PTHREAD_ID)
+ return;
+
+ MUTEXLOCK(anonid, "nis_delete_callback_id");
+ for (prev = &callback_id_list, entry = *prev; entry != NULL;
+ prev = &(entry->next), entry = entry->next) {
+ if (entry->id == id) {
+ *prev = entry->next;
+ free(entry->pname);
+ free(entry);
+ break;
+ }
+ }
+ MUTEXUNLOCK(anonid, "nis_delete_callback_id");
+
+ return;
+}
+
+/*
+ * Return the id and principal name corresponding to an anonymous id
+ */
+pthread_t
+nis_get_callback_id(anonid_t anonid, nis_name pname, int pnamelen) {
+
+ callback_id *entry;
+ pthread_t id = INV_PTHREAD_ID;
+
+ MUTEXLOCK(anonid, "nis_get_callback_id");
+ for (entry = callback_id_list; entry != NULL; entry = entry->next) {
+ if (entry->anonid == anonid) {
+ id = entry->id;
+ if (pname != 0) {
+ strncpy(pname, entry->pname, pnamelen);
+ }
+ break;
+ }
+ }
+ MUTEXUNLOCK(anonid, "nis_get_callback_id");
+
+ return(id);
+}
+
+/* Classify a subop string */
+__match_class
+__subop_match_class(char *subop) {
+
+ if (subop == 0) {
+#ifdef OPACCDEBUG
+ printf("\t\t\t<NIL>\n");
+#endif /* OPACCDEBUG */
+ return (NIL);
+ } else if (*subop == '\0') {
+#ifdef OPACCDEBUG
+ printf("\t\t\t<EMPTY>\n");
+#endif /* OPACCDEBUG */
+ return (EMPTY);
+ } else {
+#ifdef OPACCDEBUG
+ printf("\t\t\t%s\n", subop);
+#endif /* OPACCDEBUG */
+ return (STRING);
+ }
+}
+
+/*
+ * Classify the subop in an entry. If it's a string, compare to the
+ * subop argument.
+ */
+__match_class
+__entry_match_class(char *subop, entry_obj *entry) {
+
+ __match_class ret;
+ char *entry_subop;
+
+ if (entry == 0 || entry->en_cols.en_cols_len <= SUBOP_COL_NUM) {
+ ret = NONE;
+ } else {
+ entry_subop =
+ entry->en_cols.en_cols_val[SUBOP_COL_NUM].ec_value.ec_value_val;
+ ret = __subop_match_class(entry_subop);
+ if (ret == STRING &&
+ (subop == 0 || strcmp(subop, entry_subop) != 0))
+ ret = NONE;
+ }
+ return (ret);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_proc.h b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_proc.h
new file mode 100644
index 0000000000..4ef2185f88
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_proc.h
@@ -0,0 +1,472 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _NIS_PROC_H
+#define _NIS_PROC_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <pthread.h>
+#include <nis_servlist.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * nis_proc.h
+ *
+ * This module contains definitions used by the NIS service.
+ *
+ */
+
+/*
+ * Structure definitions used by the various functions
+ */
+struct nis_db_result {
+ nis_error status; /* Status of the result */
+ nis_object *obj; /* Pointer to the object returned */
+ unsigned long ticks; /* Number of clock ticks used */
+};
+
+typedef struct nis_db_result nis_db_result;
+
+struct obj_list {
+ u_char r; /* readable flag */
+ nis_object *o; /* object */
+};
+
+typedef struct obj_list obj_list;
+struct nis_db_list_result {
+ nis_error status; /* return status */
+ obj_list *objs; /* Objects returned */
+ int numo; /* Number of objects */
+ unsigned long ticks; /* Database ticks */
+};
+
+typedef struct nis_db_list_result nis_db_list_result;
+
+struct nis_fn_result {
+ nis_error status; /* Return status */
+ nis_object *obj; /* The object returned */
+ netobj cookie; /* The magic cookie (db state) */
+ unsigned long ticks; /* the obligatory ticks value */
+};
+
+typedef struct nis_fn_result nis_fn_result;
+
+struct limits {
+ int max_attrval; /* Maximum length of 1 attribute */
+ int max_attr; /* Maximum number of attributes */
+ int max_columns; /* Maximum number of columns */
+};
+
+struct ticks {
+ u_long aticks;
+ u_long dticks;
+ u_long zticks;
+ u_long cticks;
+};
+
+struct object_list {
+ int num;
+ nis_object *objs;
+};
+
+struct cleanup {
+ void (*func)();
+ void *data;
+ char *tag;
+ int id;
+ struct cleanup *next;
+};
+typedef struct cleanup cleanup;
+
+struct xdr_clean_data {
+ bool_t (*xdr_func)();
+ char *xdr_data;
+};
+typedef struct xdr_clean_data xdr_clean_data;
+
+
+#define INIT_UPD_LIST_TIME_INCR 120
+#define MAX_UPD_LIST_TIME_INCR 1920
+
+/*
+ * "nis_mt.h" needs some of the declarations above (notably nis_fn_result),
+ * but also redefines some items below, so it needs to be included here.
+ */
+#include "nis_mt.h"
+/*
+ * We include <nisdb_ldap.h> and "nis_ldap.h" here as a convenience for
+ * the rpc.nisd source files.
+ */
+#include <nisdb_ldap.h>
+#include "nis_ldap.h"
+
+struct ping_item {
+ NIS_HASH_ITEM item; /* Item struct for hash functions */
+ u_long mtime; /* Time of the update */
+ nis_object *obj; /* Directory object. */
+ u_long utime; /* Time we will retry this entry */
+ u_long delta; /* Current value of time increment */
+};
+
+typedef struct ping_item ping_item;
+
+struct sdata {
+ void *buf; /* Memory allocation pointer */
+ u_long size; /* Size of allocated data */
+};
+
+/* operations statistics vector */
+struct ops_stats {
+ u_long op; /* Operation (procedure number) */
+ u_long calls; /* Times called */
+ u_long errors; /* Error encountered in call */
+ int cursamp; /* current sample index */
+ u_long tsamps[16]; /* last 16 time measurements */
+};
+
+/*
+ * Used for caching table objects
+ */
+struct table_item {
+ NIS_HASH_ITEM ti_item; /* Generic ITEM tag */
+ nis_object *ibobj; /* Table object */
+};
+
+/*
+ * Map callback pid/tid to an anonymous number
+ */
+typedef pid_t anonid_t;
+struct callback_id {
+ pthread_t id;
+ anonid_t anonid;
+ nis_name pname;
+ struct callback_id *next;
+};
+typedef struct callback_id callback_id;
+
+/*
+ * Forward reference declarations for functions in the service and in
+ * the nis library
+ */
+
+#ifdef __STDC__
+/* in nis_db.c */
+extern void __make_legal(char *);
+extern nis_db_result *db_add(nis_name, nis_object *, int);
+extern nis_error __db_add(nis_name, nis_object *, int);
+extern nis_db_result *db_remove(nis_name, nis_object *, u_long);
+extern nis_error __db_remove(nis_name, nis_object *);
+extern nis_db_result *db_lookup(nis_name);
+extern nis_db_result *db_lookup_deferred(nis_name, bool_t);
+extern nis_db_list_result *db_list(nis_name, int, nis_attr *);
+extern nis_db_list_result *db_list_flags(nis_name, int, nis_attr *, u_long);
+extern void free_db_list(nis_db_list_result *);
+extern nis_db_result *db_addib(nis_name, int, nis_attr *, nis_object *,
+ nis_object *);
+extern nis_error __db_addib(nis_name, int, nis_attr *, nis_object *);
+extern nis_error __db_addib_nolog(nis_name, int, nis_attr *,
+ nis_object *);
+extern nis_error __db_addib_nosync(nis_name, int, nis_attr *,
+ nis_object *);
+extern nis_db_result *db_remib(nis_name, int, nis_attr *, obj_list *, int,
+ nis_object *, u_long);
+extern nis_error __db_remib(nis_name, int, nis_attr *);
+extern nis_error __db_remib_nosync(nis_name, int, nis_attr *);
+extern nis_fn_result *db_firstib(nis_name, int, nis_attr *, int, char *);
+extern nis_fn_result *db_nextib(nis_name, netobj *, int, char *);
+extern nis_error db_find_table(nis_name);
+extern nis_error db_create(nis_name, table_obj*);
+extern nis_error db_destroy(nis_name);
+extern void db_flush(nis_name, netobj *);
+extern void add_checkpoint(nis_name);
+extern int checkpoint_db();
+extern nis_name __table_name(nis_name);
+extern char *internal_table_name(nis_name, char *);
+extern int nis_db_sync_log(void);
+extern nis_error db_configure(char *);
+extern int loadLDAPdata(void);
+
+/* in nis_subr_proc.c */
+extern int nis_strcasecmp(char *, char *);
+extern int nis_isserving(nis_object *);
+extern nis_error __directory_object(nis_name, struct ticks *, int,
+ nis_object **);
+extern nis_error __directory_object_msg(nis_name, struct ticks *, int,
+ nis_object **, int);
+extern void nis_getprincipal(char *, struct svc_req *);
+extern nis_error get_object_safely(nis_name, u_long, nis_object **);
+extern nis_result *svc_lookup(nis_name, u_long);
+extern int __can_do(u_long, u_long, nis_object *, nis_name);
+extern u_char *__get_xdr_buf(int);
+extern char *__get_string_buf(int);
+extern entry_col *__get_entry_col(int);
+extern table_col *__get_table_col(int);
+extern nis_attr *__get_attrs(int);
+extern int nis_isstable(log_entry *, int);
+extern void flush_tablecache(nis_name);
+extern u_long nis_cptime(nis_server *, nis_name);
+extern u_long nis_cptime_msg(nis_server *, nis_name, bool_t, bool_t);
+extern void make_stamp(nis_name, u_long);
+extern int replica_update(ping_item *);
+extern int ping_replicas(ping_item *);
+extern void add_pingitem(nis_object *, u_long, NIS_HASH_TABLE *);
+extern void do_cleanup(cleanup *);
+extern void add_cleanup(void (*)(), void *, char *);
+extern void do_xdr_cleanup(xdr_clean_data *);
+extern void add_xdr_cleanup(bool_t (*)(), char *, char *);
+extern nis_result *__nis_local_lookup(ib_request *, u_long, int, void *,
+ int (*)());
+
+/* in nis_ib_proc.c */
+extern nis_object *nis_censor_object_attr(nis_object *, table_col *,
+ nis_name, u_int, nis_attr *);
+extern nis_object *nis_censor_object(nis_object *, table_col *, nis_name);
+extern endpoint *__nis_alt_callback_server(endpoint *, u_int,
+ struct netbuf *, char **, int);
+
+/* in nis_log_svc.c */
+extern u_long last_update(nis_name);
+extern int apply_transaction(log_entry *);
+extern int checkpoint_log(void);
+extern u_long add_update(log_entry *);
+extern u_long add_update_nosync(log_entry *, void **);
+extern int abort_transaction(int);
+extern void entries_since(nis_object *, u_long, log_result *);
+
+/* in nis_log_common.c */
+extern int begin_transaction(nis_name);
+extern int end_transaction_x(int, int);
+extern int end_transaction(int);
+extern int lockTransLog(char *, int, int);
+extern void unlockTransLog(char *, int);
+extern int map_log(char *, int);
+extern void sync_log();
+
+/* nis_opacc.c */
+extern bool_t nis_op_access(char *, char *, nis_name, char *,
+ struct svc_req *);
+extern void nis_delete_callback_id(pthread_t);
+extern pthread_t nis_get_callback_id(anonid_t, nis_name, int);
+extern anonid_t nis_add_callback_id(pthread_t, nis_name);
+
+#else
+
+/* in nis_db.c */
+extern void __make_legal();
+extern nis_db_result *db_add();
+extern nis_error __db_add();
+extern nis_db_result *db_remove();
+extern nis_error __db_remove();
+extern nis_db_result *db_lookup();
+extern nis_db_result *db_lookup_deferred();
+extern nis_db_list_result *db_list();
+extern nis_db_list_result *db_list_flags();
+extern void free_db_list();
+extern nis_db_result *db_addib();
+extern nis_error *__db_addib();
+extern nis_error *__db_addib_nolog();
+extern nis_error *__db_addib_nosync();
+extern nis_db_result *db_remib();
+extern nis_error __db_remib();
+extern nis_error __db_remib_nosync();
+extern nis_fn_result *db_firstib();
+extern nis_fn_result *db_nextib();
+extern nis_error db_find_table();
+extern nis_error db_create();
+extern nis_error db_destroy();
+extern void db_flush();
+extern void add_checkpoint();
+extern int checkpoint_db();
+extern nis_name __table_name();
+extern int nis_db_sync_log();
+extern nis_error db_configure();
+extern int loadLDAPdata();
+
+/* in nis_subr_proc.c */
+extern int nis_strcasecmp();
+extern int nis_isserving();
+extern nis_error __directory_object();
+extern nis_error __directory_object_msg();
+extern void nis_getprincipal();
+extern nis_error get_object_safely();
+extern nis_result *svc_lookup();
+extern int __can_do();
+extern u_char *__get_xdr_buf();
+extern char *__get_string_buf();
+extern entry_col *__get_entry_col();
+extern table_col *__get_table_col();
+extern nis_attr *__get_attrs();
+extern int nis_isstable();
+extern void flush_tablecache();
+extern u_long nis_cptime();
+extern void make_stamp();
+extern void replica_update();
+extern void add_pingitem();
+extern void do_cleanup();
+extern void add_cleanup();
+extern void do_xdr_cleanup();
+extern void add_xdr_cleanup();
+extern nis_result *__nis_local_lookup();
+
+/* in nis_ib_proc.c */
+extern nis_object *nis_censor_object_attr();
+extern nis_object *nis_censor_object();
+extern endpoint *__nis_alt_callback_server();
+
+
+/* in nis_log_svc.c */
+extern u_long last_update();
+extern int apply_transaction();
+extern int checkpoint_log();
+extern u_long add_update();
+extern u_long add_update_nosync();
+extern int abort_transaction();
+extern void entries_since();
+
+/* in nis_log_common.c */
+extern int begin_transaction();
+extern int end_transaction_x();
+extern int end_transaction();
+extern int lockTransLog();
+extern void unlockTransLog();
+extern int map_log();
+extern void sync_log();
+extern int __log_resync();
+extern char *__make_name();
+
+/* nis_opacc.c */
+extern bool_t nis_op_access();
+extern void nis_delete_callback_id();
+extern pid_t nis_get_callback_id();
+extern anonid_t nis_add_callback_id();
+#endif
+
+/*
+ * Declarations for global state used by the service.
+ */
+extern int root_server; /* set by the -r switch */
+extern int verbose; /* set by the -v switch */
+extern int static_root; /* set by network partition */
+extern int secure_level; /* set by the -S switch */
+extern NIS_HASH_TABLE upd_list; /* added during a ping. */
+extern NIS_HASH_TABLE ping_list; /* added during an update. */
+extern NIS_HASH_TABLE checkpoint_list; /* added for a checkpoint. */
+extern int checkpoint_all;
+extern NIS_HASH_TABLE *table_cache; /* cache of table objects */
+extern int ping_pid, upd_pid; /* Processes doing the work */
+
+/*
+ * MACRO definitions for the various source modules.
+ */
+#define STATUS(r) r.status
+#define OBJECT(r) r.objects.objects_val
+#define NUMOBJ(r) r.objects.objects_len
+#define CBDATA(r) r.cookie
+#define COOKIE(r) r.cookie
+#define same_oid(o1, o2) (((o1)->zo_oid.mtime == (o2)->zo_oid.mtime) &&\
+ ((o1)->zo_oid.ctime == (o2)->zo_oid.ctime))
+
+#define ROOT_OBJ "root.object"
+#define PARENT_OBJ "parent.object"
+
+#define ENVAL ec_value.ec_value_val
+#define ENLEN ec_value.ec_value_len
+#define ZAVAL zattr_val.zattr_val_val
+#define ZALEN zattr_val.zattr_val_len
+
+#define FN_NOMANGLE 0 /* don't mangle objects on first/next calls */
+#define FN_MANGLE 1 /* mangle objects on first/next calls */
+#define FN_NORAGS 2 /* don't put malloc data on the rag list */
+#define FN_NOERROR 4 /* don't generate error on missing table */
+
+#define ADD_OP 1
+#define REM_OP 2
+#define MOD_OP 3
+#define FIRST_OP 4
+#define NEXT_OP 5
+
+#define GETSERVER(o, n) (((o)->DI_data.do_servers.do_servers_val) + n)
+#define MAXSERVER(o) (o)->DI_data.do_servers.do_servers_len
+#define MASTER(o) GETSERVER(o, 0)
+
+#define CHILDPROC (getpid() != master_pid)
+
+#define DIR_IDLE_TIME 120
+
+#define NOANONID (anonid_t)-1
+
+/* Global variables defined in nis_log_common.c */
+extern pid_t master_pid;
+
+/* Global variables defined in nis_main.c */
+extern cleanup *looseends; /* Data that needs to be freed */
+extern cleanup *free_rags; /* free cleanup structs that are available */
+extern int children; /* Server load control */
+extern int max_children; /* Max number of forked children */
+extern int readonly; /* When true the service is read only */
+extern int need_checkpoint; /* When true the log should be checkpointed */
+extern int force_checkpoint; /* When true the log WILL be checkpointed */
+extern int auth_verbose; /* verbose authorization messages */
+extern table_obj tbl_prototype; /* directory "table" format */
+
+/*
+ * A set of defines for tracking memory problems. A simple memory allocator
+ * and checker is part of nis_malloc.c and when DEBUG is defined and this
+ * file is linked in, we generate messages on allocs/frees and check for
+ * freeing free memory, etc.
+ */
+#ifdef MEM_DEBUG
+#define XFREE xfree
+#define XMALLOC xmalloc
+#define XCALLOC xcalloc
+#define XSTRDUP xstrdup
+#ifdef __STDC__
+extern void xfree(void *);
+extern char *xstrdup(char *);
+extern char *xmalloc(int);
+extern char *xcalloc(int, int);
+#else
+extern void xfree();
+extern char *xstrdup();
+extern char *xmalloc();
+extern char *xcalloc();
+#endif /* __STDC__ */
+#else
+#define XFREE free
+#define XMALLOC malloc
+#define XCALLOC calloc
+#define XSTRDUP strdup
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _NIS_PROC_H */
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_service.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_service.c
new file mode 100644
index 0000000000..191c104a70
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_service.c
@@ -0,0 +1,656 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * nis_service.c
+ *
+ * This module contains the dispatch functions for the NIS+ service. At one
+ * time it was generated by rpcgen, however, due to the requirement that it
+ * be able to compile to a 4.1/sockets version or a 5.0/tli version and the
+ * desire to collect statistics about the time spent in the service functions,
+ * it is now "real" source. Changes to the .x file will have to be reconciled
+ * with this file.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h> /* getenv, exit */
+#include <syslog.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <memory.h>
+#include <stropts.h>
+#include <netconfig.h>
+#include <sys/resource.h> /* rlimit */
+#include <rpc/rpc.h>
+#include <rpc/svc.h>
+#include <rpcsvc/nis.h>
+#include <rpcsvc/yp_prot.h>
+#include <string.h>
+#include <netdir.h>
+
+#include "nis_proc.h"
+#include "nis_svc.h"
+
+#include <nisdb_mt.h>
+#include <ldap_util.h>
+
+extern int _rpcpmstart; /* Started by a port monitor ? */
+extern int _rpcfdtype; /* Whether Stream or Datagram ? */
+extern int _rpcsvcdirty; /* Still serving ? */
+
+/*
+ * Private object name checking and printing functions for NIS+
+ */
+#define NIS_SVCARG_NOCHECK 0x00000000
+#define NIS_SVCARG_NULLPTR 0x00000001
+#define NIS_SVCARG_EMPTYSTRING 0x00000002
+#define NIS_SVCARG_TRAILINGDOT 0x00000004
+#define NIS_SVCARG_MAXLEN 0x00000008
+#define NIS_SVCARG_ROOTOBJECT 0x00000010
+
+/*
+ * NIS_SVCARG_ROOTOBJECT is not included in the CHECKALL set (below) as
+ * in effect it is in reverse sense to the TRAILINGDOT check.
+ * In addition, we only want to check for the ROOTOBJECT in a few
+ * particular cases.
+ */
+
+#define NIS_SVCARG_CHECKALL (NIS_SVCARG_NULLPTR | \
+ NIS_SVCARG_EMPTYSTRING | \
+ NIS_SVCARG_TRAILINGDOT | \
+ NIS_SVCARG_MAXLEN)
+
+static bool_t legal_object_name(nis_name, uint_t);
+static nis_name safe_object_name(nis_name);
+
+
+/*
+ * NIS Version 2 (YP) Dispatch table
+ */
+extern int *ypproc_domain_svc();
+extern int *ypproc_domain_nonack_svc();
+extern struct ypresp_master *ypproc_master_svc();
+extern struct ypresp_val *ypproc_match_svc();
+extern struct ypresp_key_val *ypproc_first_svc();
+extern struct ypresp_key_val *ypproc_next_svc();
+extern struct ypresp_all *ypproc_all_svc();
+extern struct ypresp_maplist *ypproc_maplist_svc();
+extern bool xdr_ypresp_all();
+
+void
+ypprog_svc(rqstp, transp)
+ struct svc_req *rqstp;
+ register SVCXPRT *transp;
+{
+ union {
+ string_t ypproc_domain_svc_arg;
+ string_t ypproc_domain_nonack_svc_arg;
+ struct ypreq_key ypproc_match_svc_arg;
+ struct ypreq_nokey ypproc_first_svc_arg;
+ struct ypreq_key ypproc_next_svc_arg;
+ struct ypreq_nokey ypproc_all_svc_arg;
+ struct ypreq_nokey ypproc_master_svc_arg;
+ string_t ypproc_maplist_svc_arg;
+ } argument;
+ char *result;
+ bool (*xdr_argument)(), (*xdr_result)();
+ char *(*local)();
+
+ mark_activity();
+
+ _rpcsvcdirty = 1;
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ (void) svc_sendreply(transp, xdr_void, (char *)NULL);
+ _rpcsvcdirty = 0;
+ return;
+
+ case YPPROC_DOMAIN:
+ xdr_argument = xdr_ypdomain_wrap_string;
+ xdr_result = (bool (*)()) xdr_bool;
+ local = (char *(*)()) ypproc_domain_svc;
+ break;
+
+ case YPPROC_DOMAIN_NONACK:
+ xdr_argument = xdr_ypdomain_wrap_string;
+ xdr_result = (bool (*)()) xdr_bool;
+ local = (char *(*)()) ypproc_domain_nonack_svc;
+ break;
+
+ case YPPROC_MATCH:
+ xdr_argument = xdr_ypreq_key;
+ xdr_result = xdr_ypresp_val;
+ local = (char *(*)()) ypproc_match_svc;
+ break;
+
+ case YPPROC_FIRST:
+ xdr_argument = xdr_ypreq_nokey;
+ xdr_result = xdr_ypresp_key_val;
+ local = (char *(*)()) ypproc_first_svc;
+ break;
+
+ case YPPROC_NEXT:
+ xdr_argument = xdr_ypreq_key;
+ xdr_result = xdr_ypresp_key_val;
+ local = (char *(*)()) ypproc_next_svc;
+ break;
+
+ case YPPROC_ALL:
+ xdr_argument = xdr_ypreq_nokey;
+ xdr_result = xdr_ypresp_all;
+ local = (char *(*)()) ypproc_all_svc;
+ break;
+
+ case YPPROC_MASTER:
+ xdr_argument = xdr_ypreq_nokey;
+ xdr_result = xdr_ypresp_master;
+ local = (char *(*)()) ypproc_master_svc;
+ break;
+
+ case YPPROC_MAPLIST:
+ xdr_argument = xdr_ypdomain_wrap_string;
+ xdr_result = xdr_ypresp_maplist;
+ local = (char *(*)()) ypproc_maplist_svc;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ _rpcsvcdirty = 0;
+ return;
+ }
+ (void) memset((char *)&argument, 0, sizeof (argument));
+ if (!svc_getargs(transp, (xdrproc_t)xdr_argument, (char *)&argument)) {
+ svcerr_decode(transp);
+ _rpcsvcdirty = 0;
+ return;
+ }
+ result = (*local)(&argument, rqstp);
+ if (result != NULL && !svc_sendreply(transp, (xdrproc_t)xdr_result,
+ result)) {
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, (xdrproc_t)xdr_argument, (char *)&argument)) {
+ syslog(LOG_ERR, "yp_svc: unable to free arguments");
+ exit(1);
+ }
+
+ __nis_thread_cleanup(__nis_get_tsd());
+
+ _rpcsvcdirty = 0;
+}
+
+struct ops_stats nisopstats[24];
+
+static void
+start_stat(p)
+ ulong_t p;
+{
+ if ((p > 0) && (p < 24)) {
+ WLOCK(nisopstats);
+ nisopstats[p].calls++;
+ WULOCK(nisopstats);
+ __start_clock(7);
+ }
+}
+
+static void
+stop_stat(p, e)
+ ulong_t p;
+ int e;
+{
+ struct ops_stats *os;
+ ulong_t ndx;
+
+ if ((p > 0) && (p < 24)) {
+ WLOCK(nisopstats);
+ os = &(nisopstats[p]);
+ os->tsamps[os->cursamp] = __stop_clock(7);
+ os->cursamp = (++(os->cursamp)%16);
+ if (e)
+ os->errors++;
+ WULOCK(nisopstats);
+ }
+}
+
+/*
+ * NIS Version 3 (NIS+) dispatch function.
+ */
+
+void
+nis_prog_svc(rqstp, transp)
+ struct svc_req *rqstp;
+ register SVCXPRT *transp;
+{
+ union {
+ ns_request nis_lookup_svc_arg;
+ ns_request nis_add_svc_arg;
+ ns_request nis_modify_svc_arg;
+ ns_request nis_remove_svc_arg;
+ ib_request nis_iblist_svc_arg;
+ ib_request nis_ibadd_svc_arg;
+ ib_request nis_ibmodify_svc_arg;
+ ib_request nis_ibremove_svc_arg;
+ ib_request nis_ibfirst_svc_arg;
+ ib_request nis_ibnext_svc_arg;
+ fd_args nis_finddirectory_svc_arg;
+ nis_taglist nis_status_svc_arg;
+ dump_args nis_dumplog_svc_arg;
+ dump_args nis_dump_svc_arg;
+ netobj nis_callback_svc_arg;
+ nis_name nis_cptime_svc_arg;
+ nis_name nis_checkpoint_svc_arg;
+ ping_args nis_ping_svc_arg;
+ nis_taglist nis_servstate_svc_arg;
+ nis_name nis_mkdir_svc_arg;
+ nis_name nis_rmdir_svc_arg;
+ } argument;
+ char *result;
+ bool_t (*xdr_argument)(), (*xdr_result)();
+ char *(*local)();
+ ulong_t starttime;
+ nis_name *nameaddr = NULL;
+ uint_t docheck = NIS_SVCARG_NOCHECK;
+ struct netconfig *nconfp;
+ char *ipaddrp;
+
+ mark_activity();
+
+ _rpcsvcdirty = 1;
+
+ start_stat(rqstp->rq_proc);
+
+ if (verbose) {
+ if ((nconfp = __rpcfd_to_nconf(rqstp->rq_xprt->xp_fd,
+ rqstp->rq_xprt->xp_type)) != NULL) {
+ if ((ipaddrp = taddr2uaddr(nconfp,
+ svc_getrpccaller(rqstp->rq_xprt))) != NULL) {
+ syslog(LOG_DEBUG, "nis_prog_svc: "
+ "Request %d from %s",
+ rqstp->rq_proc, ipaddrp);
+ free(ipaddrp);
+ }
+ freenetconfigent(nconfp);
+ }
+ }
+
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ (void) svc_sendreply(transp, xdr_void, (char *)NULL);
+ _rpcsvcdirty = 0;
+ stop_stat(rqstp->rq_proc, 0);
+ return;
+
+ case NIS_LOOKUP:
+ xdr_argument = xdr_ns_request;
+ xdr_result = xdr_nis_result;
+ local = (char *(*)()) nis_lookup_svc;
+ nameaddr = &argument.nis_lookup_svc_arg.ns_name;
+ docheck = NIS_SVCARG_CHECKALL;
+ break;
+
+ case NIS_ADD:
+ xdr_argument = xdr_ns_request;
+ xdr_result = xdr_nis_result;
+ local = (char *(*)()) nis_add_svc;
+ nameaddr = &argument.nis_add_svc_arg.ns_name;
+ docheck = NIS_SVCARG_CHECKALL;
+ break;
+
+ case NIS_MODIFY:
+ xdr_argument = xdr_ns_request;
+ xdr_result = xdr_nis_result;
+ local = (char *(*)()) nis_modify_svc;
+ nameaddr = &argument.nis_modify_svc_arg.ns_name;
+ docheck = NIS_SVCARG_CHECKALL;
+ break;
+
+ case NIS_REMOVE:
+ xdr_argument = xdr_ns_request;
+ xdr_result = xdr_nis_result;
+ local = (char *(*)()) nis_remove_svc;
+ nameaddr = &argument.nis_remove_svc_arg.ns_name;
+ docheck = NIS_SVCARG_CHECKALL;
+ break;
+
+ case NIS_IBLIST:
+ xdr_argument = xdr_ib_request;
+ xdr_result = xdr_nis_result;
+ local = (char *(*)()) nis_iblist_svc;
+ nameaddr = &argument.nis_iblist_svc_arg.ibr_name;
+ docheck = NIS_SVCARG_CHECKALL;
+ break;
+
+ case NIS_IBADD:
+ xdr_argument = xdr_ib_request;
+ xdr_result = xdr_nis_result;
+ local = (char *(*)()) nis_ibadd_svc;
+ nameaddr = &argument.nis_ibadd_svc_arg.ibr_name;
+ docheck = NIS_SVCARG_CHECKALL;
+ break;
+
+ case NIS_IBMODIFY:
+ xdr_argument = xdr_ib_request;
+ xdr_result = xdr_nis_result;
+ local = (char *(*)()) nis_ibmodify_svc;
+ nameaddr = &argument.nis_ibmodify_svc_arg.ibr_name;
+ docheck = NIS_SVCARG_CHECKALL;
+ break;
+
+ case NIS_IBREMOVE:
+ xdr_argument = xdr_ib_request;
+ xdr_result = xdr_nis_result;
+ local = (char *(*)()) nis_ibremove_svc;
+ nameaddr = &argument.nis_ibremove_svc_arg.ibr_name;
+ docheck = NIS_SVCARG_CHECKALL;
+ break;
+
+ case NIS_IBFIRST:
+ xdr_argument = xdr_ib_request;
+ xdr_result = xdr_nis_result;
+ local = (char *(*)()) nis_ibfirst_svc;
+ nameaddr = &argument.nis_ibfirst_svc_arg.ibr_name;
+ docheck = NIS_SVCARG_CHECKALL;
+ break;
+
+ case NIS_IBNEXT:
+ xdr_argument = xdr_ib_request;
+ xdr_result = xdr_nis_result;
+ local = (char *(*)()) nis_ibnext_svc;
+ nameaddr = &argument.nis_ibnext_svc_arg.ibr_name;
+ docheck = NIS_SVCARG_CHECKALL;
+ break;
+
+ case NIS_FINDDIRECTORY:
+ xdr_argument = xdr_fd_args;
+ xdr_result = xdr_fd_result;
+ local = (char *(*)()) nis_finddirectory_svc;
+ nameaddr = &argument.nis_finddirectory_svc_arg.dir_name;
+ docheck = NIS_SVCARG_CHECKALL;
+ break;
+
+ case NIS_STATUS:
+ xdr_argument = xdr_nis_taglist;
+ xdr_result = xdr_nis_taglist;
+ local = (char *(*)()) nis_status_svc;
+ break;
+
+ case NIS_DUMPLOG:
+ xdr_argument = xdr_dump_args;
+ xdr_result = xdr_log_result;
+ local = (char *(*)()) nis_dumplog_svc;
+ nameaddr = &argument.nis_dumplog_svc_arg.da_dir;
+ docheck = NIS_SVCARG_CHECKALL;
+ break;
+
+ case NIS_DUMP:
+ xdr_argument = xdr_dump_args;
+ xdr_result = xdr_log_result;
+ local = (char *(*)()) nis_dump_svc;
+ nameaddr = &argument.nis_dump_svc_arg.da_dir;
+ docheck = NIS_SVCARG_CHECKALL;
+ break;
+
+ case NIS_CALLBACK:
+ xdr_argument = xdr_netobj;
+ xdr_result = xdr_bool;
+ local = (char *(*)()) nis_callback_svc;
+ break;
+
+ case NIS_CPTIME:
+ xdr_argument = xdr_nis_name;
+ xdr_result = xdr_u_long;
+ local = (char *(*)()) nis_cptime_svc;
+ nameaddr = &argument.nis_cptime_svc_arg;
+ /*
+ * Setup docheck with the NIS_SVCARG_ROOTOBJECT flag so that
+ * objects called 'root.object' can have their time checked.
+ */
+ docheck = NIS_SVCARG_CHECKALL | NIS_SVCARG_ROOTOBJECT;
+ break;
+
+ case NIS_CHECKPOINT:
+ xdr_argument = xdr_nis_name;
+ xdr_result = xdr_cp_result;
+ local = (char *(*)()) nis_checkpoint_svc;
+ nameaddr = &argument.nis_checkpoint_svc_arg;
+ docheck = NIS_SVCARG_NULLPTR | NIS_SVCARG_TRAILINGDOT |
+ NIS_SVCARG_MAXLEN | NIS_SVCARG_ROOTOBJECT;
+ break;
+
+ case NIS_PING:
+ xdr_argument = xdr_ping_args;
+ xdr_result = xdr_void;
+ local = (char *(*)()) nis_ping_svc;
+ nameaddr = &argument.nis_ping_svc_arg.dir;
+ /*
+ * Setup docheck with the NIS_SVCARG_ROOTOBJECT flag so that
+ * objects called 'root.object' can be 'pinged'.
+ */
+ docheck = NIS_SVCARG_CHECKALL | NIS_SVCARG_ROOTOBJECT;
+ break;
+
+ case NIS_SERVSTATE:
+ xdr_argument = xdr_nis_taglist;
+ xdr_result = xdr_nis_taglist;
+ local = (char *(*)()) nis_servstate_svc;
+ break;
+
+ case NIS_MKDIR:
+ xdr_argument = xdr_nis_name;
+ xdr_result = xdr_nis_error;
+ local = (char *(*)()) nis_mkdir_svc;
+ nameaddr = &argument.nis_mkdir_svc_arg;
+ docheck = NIS_SVCARG_CHECKALL;
+ break;
+
+ case NIS_RMDIR:
+ xdr_argument = xdr_nis_name;
+ xdr_result = xdr_nis_error;
+ local = (char *(*)()) nis_rmdir_svc;
+ nameaddr = &argument.nis_rmdir_svc_arg;
+ docheck = NIS_SVCARG_CHECKALL;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ _rpcsvcdirty = 0;
+ stop_stat(rqstp->rq_proc, 1);
+ return;
+ }
+ (void) memset((char *)&argument, 0, sizeof (argument));
+ if (!svc_getargs(transp, xdr_argument, (char *)&argument)) {
+ svcerr_decode(transp);
+ _rpcsvcdirty = 0;
+ stop_stat(rqstp->rq_proc, 1);
+ return;
+ }
+ if (nameaddr == NULL ||
+ legal_object_name(*nameaddr, docheck) == TRUE) {
+ result = (*local)(&argument, rqstp);
+ } else {
+ /*
+ * Service routine expects an object name argument, but
+ * the name supplied was bad.
+ */
+ result = (char *)nis_make_error(NIS_BADNAME, 0, 0, 0, 0);
+ add_xdr_cleanup(xdr_nis_result, result, "nis_svc result");
+ if (verbose)
+ syslog(LOG_INFO, "nis_svc: bad name '%s'",
+ safe_object_name(*nameaddr));
+ }
+ if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, xdr_argument, (char *)&argument)) {
+ syslog(LOG_ERR, "nis_svc: unable to free arguments");
+ exit(1);
+ }
+ _rpcsvcdirty = 0;
+
+ /* If the DB deferred an object deletion, perform it now */
+ {
+ nisdb_tsd_t *tsd = __nisdb_get_tsd();
+
+ if (tsd != 0) {
+ nisdb_obj_del_t *nod, *next;
+ ns_request nsr;
+ nis_result *res;
+ nis_error *err;
+
+ for (nod = tsd->objDelList; nod != 0; nod = next) {
+ next = nod->next;
+
+ nsr.ns_name = nod->objName;
+ nsr.ns_object.ns_object_len = 0;
+ nsr.ns_object.ns_object_val = 0;
+
+ res = nis_remove_svc(&nsr, 0);
+ if (res == 0) {
+ logmsg(MSG_NOTIMECHECK, LOG_ERR,
+ "Deferred nis_remove(\"%s\") => NULL",
+ nod->objName);
+ nod->objType = NIS_BOGUS_OBJ;
+ } else if (res->status != NIS_SUCCESS) {
+ logmsg(MSG_NOTIMECHECK, LOG_ERR,
+ "Deferred nis_remove(\"%s\") => %s",
+ nod->objName,
+ nis_sperrno(res->status));
+ nod->objType = NIS_BOGUS_OBJ;
+ }
+
+ if (res != 0)
+ nis_freeresult(res);
+
+ if (nod->objType == NIS_DIRECTORY_OBJ) {
+ err = nis_rmdir_svc(&nod->objName, 0);
+ if (err == 0 || *err != NIS_SUCCESS)
+ logmsg(MSG_NOTIMECHECK, LOG_ERR,
+ "Deferred nis_rmdir(\"%s\") => %s",
+ nod->objName,
+ err != 0 ? nis_sperrno(*err) :
+ "<NIL>");
+ }
+
+ sfree(nod->objName);
+ sfree(nod);
+ }
+
+ tsd->objDelList = 0;
+ }
+ }
+
+ __nis_thread_cleanup(__nis_get_tsd());
+
+ stop_stat(rqstp->rq_proc, 0);
+}
+
+/*
+ * check_object_name()
+ *
+ * Return TRUE if the following assertions hold for "name", FALSE otherwise:
+ *
+ * - Not a NULL pointer
+ *
+ * - Not the empty string
+ *
+ * - Does not exceed the maximum allowed length for a nis_name
+ *
+ * - Ends with trailing dot.
+ *
+ */
+static bool_t
+legal_object_name(nis_name name, uint_t docheck)
+{
+ size_t namelen;
+
+ if (docheck == NIS_SVCARG_NOCHECK)
+ return (TRUE);
+
+ if (name == NULL)
+ if ((docheck & NIS_SVCARG_NULLPTR) == NIS_SVCARG_NULLPTR)
+ return (FALSE);
+ else
+ /* NULL pointer allowed and present; no more checks */
+ return (TRUE);
+
+ namelen = strlen(name);
+
+ if (namelen == 0)
+ if ((docheck & NIS_SVCARG_EMPTYSTRING) ==
+ NIS_SVCARG_EMPTYSTRING)
+ return (FALSE);
+ else
+ /* Empty string allowed and present; no more checks */
+ return (TRUE);
+
+ if ((docheck & NIS_SVCARG_MAXLEN) == NIS_SVCARG_MAXLEN &&
+ namelen >= NIS_MAXNAMELEN)
+ return (FALSE);
+
+
+ /*
+ * Check for ROOT_OBJECT (root.object) as a valid name. Only
+ * usually set for special cases when dealing with pinging &
+ * checking times on root objects.
+ */
+
+ if ((docheck & NIS_SVCARG_ROOTOBJECT) == NIS_SVCARG_ROOTOBJECT &&
+ (strcmp(ROOT_OBJ, name) == 0))
+ return (TRUE);
+
+ if ((docheck & NIS_SVCARG_TRAILINGDOT) == NIS_SVCARG_TRAILINGDOT &&
+ name[namelen-1] != '.')
+ return (FALSE);
+
+ return (TRUE);
+}
+
+/*
+ * safe_object_name()
+ *
+ * Returns a version of the nis_name argument that is safe to pass to
+ * printf et al.
+ *
+ */
+static nis_name
+safe_object_name(nis_name name)
+{
+ static const nis_name nullname = "<NULL pointer>";
+ static const nis_name emptyname = "<Empty string>";
+ static const nis_name toolongname = "<Too long>";
+ size_t namelen;
+
+ if (name == NULL)
+ return (nullname);
+ else if ((namelen = strlen(name)) == 0)
+ return (emptyname);
+ else if (namelen >= NIS_MAXNAMELEN)
+ return (toolongname);
+ else
+ return (name);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_subr_proc.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_subr_proc.c
new file mode 100644
index 0000000000..fe37b2de17
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_subr_proc.c
@@ -0,0 +1,3347 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * nis_subr_proc.c
+ *
+ * This module contains subroutines that are used by the NIS+ service but
+ * _not_ the NIS+ clients. NIS+ client routines are in the library module
+ * nis_subr.c
+ */
+
+#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 <sys/time.h>
+#include <pwd.h>
+#include <thread.h>
+#include <gssapi/gssapi.h>
+#include <rpc/rpc.h>
+#include <rpc/clnt.h>
+#include <rpc/svc.h>
+#include <rpc/auth.h>
+#include <rpc/auth_sys.h>
+#include <rpc/auth_des.h>
+#include <rpcsvc/nis.h>
+#include <rpcsvc/nis_callback.h>
+#include <rpcsvc/nis_dhext.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <rpcsvc/nis_db.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"
+
+#define GETSERVER(o, n) (((o)->DI_data.do_servers.do_servers_val) + n)
+#define MAXSERVER(o) (o)->DI_data.do_servers.do_servers_len
+
+extern void __nis_destroy_callback();
+extern nis_result *nis_iblist_svc(), *nis_lookup_svc();
+extern CLIENT *__nis_get_server(directory_obj *, int);
+extern fd_result *__fd_res(nis_name, nis_error, directory_obj*);
+extern nis_result *__nis_remote_lookup(ib_request *req, u_long flags,
+ int list_op, void *cbdata, int (*cback)());
+extern fd_result *__nis_finddirectory_remote(nis_bound_directory **binding,
+ char *dname);
+
+/* routines for registering server's availability wrt given dirctory */
+extern db_status nis_put_offline(nis_name, bool_t);
+extern db_status nis_put_online(nis_name, __nis_defer_t, bool_t);
+extern nis_name __nis_local_root();
+
+extern FILE *cons;
+
+fd_result *dup_fdres(fd_result *, fd_result *);
+static nis_error apply_update(log_entry *);
+
+static struct directory_item {
+ NIS_HASH_ITEM dl_item; /* Generic ITEM tag */
+ nis_object *dl_obj; /* Directory object */
+ ulong_t dl_expires; /* Expiration time */
+ ulong_t dl_serve; /* nonzero if we serve */
+ ulong_t *dl_stamps; /* Replica timestamps */
+};
+typedef struct directory_item dirItem_t;
+
+static NIS_HASH_TABLE *dl = NULL;
+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) __nis_release_item(d, dl, 1);
+}
+
+void
+dirCacheReleaseWR(void *ptr) {
+ struct directory_item *d = ptr;
+
+ if (d != 0)
+ (void) __nis_release_item(d, dl, -1);
+}
+
+/*
+ * 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
+dirDeferRelease(struct directory_item *d, int wr) {
+ if (d != 0) {
+ if (wr) {
+ add_cleanup(dirCacheReleaseWR, d, "dir obj from cache");
+ } else {
+ add_cleanup(dirCacheReleaseRO, d, "dir obj from cache");
+ }
+ }
+}
+
+/*
+ * Destroy a directory cache item.
+ */
+void
+destroyDirCacheItem(void *item) {
+ struct directory_item *d = item;
+
+ if (d != 0) {
+ if (d->dl_item.name != 0)
+ free(d->dl_item.name);
+ if (d->dl_stamps != 0)
+ free(d->dl_stamps);
+ 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
+free_dlitem(dd)
+ struct directory_item *dd;
+{
+ if (dd != 0)
+ (void) nis_remove_item(dd->dl_item.name, dl);
+}
+
+
+/*
+ * 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
+flush_dircache(name, dobj)
+ nis_name name;
+ directory_obj *dobj;
+{
+ struct directory_item *dd;
+ int cachemgr_updated = 0;
+
+ if (verbose)
+ syslog(LOG_INFO, "flush_dircache: name '%s'", name);
+
+ /* Flush given directory object from cachemgr. */
+ if (dobj) {
+ __nis_CacheRemoveEntry(dobj);
+ cachemgr_updated = 1;
+ }
+
+ /* Update server's local directory cache. */
+ if (dl) {
+ dd = (struct directory_item *)nis_find_item(name, dl);
+ if (dd) {
+ if (cachemgr_updated == 0) {
+ __nis_CacheRemoveEntry(&(dd->dl_obj->DI_data));
+ cachemgr_updated = 1;
+ }
+ free_dlitem(dd);
+ __nis_release_item(dd, dl, -1);
+ }
+ }
+
+ /* Update cachemgr if have not done so yet. */
+ if (cachemgr_updated == 0) {
+ directory_obj dobj2;
+ if (__nis_CacheSearch(name, &dobj2) == NIS_SUCCESS &&
+ nis_dir_cmp(name, dobj2.do_name) == SAME_NAME) {
+ __nis_CacheRemoveEntry(&dobj2);
+ xdr_free((xdrproc_t)xdr_directory_obj, (char *)&dobj2);
+ }
+ }
+
+ /*
+ * Since we cache the table for this dir object,
+ * we will flush that away as well.
+ */
+ flush_tablecache(name);
+}
+
+void
+flush_dircache_refresh(name)
+ nis_name name;
+{
+ struct ticks t;
+ nis_object *obj;
+
+ if (verbose)
+ syslog(LOG_INFO, "flush_dircache_refresh: name '%s'", name);
+
+ flush_dircache(name, (directory_obj *) NULL); /* Flush it first */
+ /*
+ * 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.
+ */
+ (void) __directory_object(name, &t, NO_CACHE, &obj);
+}
+
+/*
+ * Flush all cached directory objects from dircache and cachemgr.
+ */
+void
+flush_dircache_all()
+{
+ struct directory_item *dd, *nxt;
+
+ if (verbose)
+ syslog(LOG_INFO, "flush_dircache_all");
+
+ while ((dd = __nis_pop_item_mt(dl)) != 0) {
+ /*
+ * 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
+flush_local_dircache(name)
+ nis_name name;
+{
+ struct directory_item *dd;
+
+ if (verbose)
+ syslog(LOG_INFO, "flush_local_dircache: name '%s'", name);
+ if (dl) {
+ dd = (struct directory_item *)nis_find_item(name, dl);
+ if (dd) {
+ free_dlitem(dd);
+ __nis_release_item(dd, dl, -1);
+ }
+ }
+}
+
+/*
+ * Flushes the local TABLE cache. Called when the database changes.
+ */
+void
+flush_tablecache(name)
+ nis_name name;
+{
+ struct table_item *ti;
+
+ if (verbose)
+ syslog(LOG_INFO, "flush_tablecache: name '%s'", name);
+ if (table_cache) {
+ ti = (struct table_item *)nis_remove_item(name, 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
+flush_tablecache_all()
+{
+ struct table_item *ti;
+
+ if (verbose)
+ syslog(LOG_INFO, "flush_tablecache_all");
+ if (table_cache) {
+ while (ti = (struct table_item *)nis_pop_item(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
+flush_groupcache(name)
+ nis_name name;
+{
+ if (verbose)
+ syslog(LOG_INFO, "flush_groupcache: name '%s'", name);
+
+ __nis_flush_group_exp_name(name);
+}
+
+/*
+ * Flushes all group caches.
+ */
+void
+flush_groupcache_all()
+{
+ if (verbose)
+ syslog(LOG_INFO, "flush_groupcache_all");
+
+ nis_flushgroups();
+}
+
+extern enum clnt_stat __nis_cast_proc();
+
+/*
+ * A convenient wrapper around __nis_cast_proc().
+ */
+bool_t
+nis_mcast_tags(dobj, tags)
+ directory_obj *dobj;
+ nis_taglist *tags;
+{
+ int i;
+ int nsrv;
+ nis_server *srv;
+
+ if (dobj == NULL)
+ return (FALSE);
+ srv = dobj->do_servers.do_servers_val;
+ nsrv = dobj->do_servers.do_servers_len;
+ for (i = 0; i < nsrv; i++) {
+ __nis_send_msg(&srv[i], NIS_SERVSTATE, xdr_nis_taglist, tags);
+ }
+ 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
+add_pingitem_with_name(buf, dir, ptime, tbl)
+ char *buf;
+ nis_object *dir;
+ u_long ptime;
+ NIS_HASH_TABLE *tbl;
+{
+ ping_item *pp;
+
+ pp = (ping_item *)nis_find_item(buf, tbl);
+ if (pp) {
+ if (verbose)
+ syslog(LOG_INFO, "add_pingitem: updated %s", buf);
+ switch (ldapConfig.updateBatching) {
+ case upd_none:
+ /*
+ * We're not accumulating updates, so we only allow
+ * the mtime to go backwards.
+ */
+ if (ptime < pp->mtime) {
+ pp->mtime = ptime;
+ setPingWakeup(pp->mtime +
+ updateBatchingTimeout());
+ }
+ break;
+ case bounded_accumulate:
+ /* Leave pp->mtime unchanged */
+ break;
+ case accumulate:
+ default:
+ if (ptime != pp->mtime) {
+ pp->mtime = ptime;
+ setPingWakeup(pp->mtime +
+ updateBatchingTimeout());
+ }
+ break;
+ }
+ /*
+ * Reset update time and backoff delta, since it appears
+ * that we can talk to the master.
+ */
+ pp->utime = 0;
+ pp->delta = INIT_UPD_LIST_TIME_INCR;
+ __nis_release_item(pp, tbl, -1);
+ return;
+ }
+
+ pp = (ping_item *)XCALLOC(1, sizeof (ping_item));
+ if (! pp) {
+ if (verbose)
+ syslog(LOG_ERR,
+ "add_pingitem: Couldn't add '%s' to pinglist (no memory)",
+ buf);
+ return;
+ }
+ pp->item.name = (nis_name) XSTRDUP(buf);
+ if (pp->item.name == NULL) {
+ if (verbose)
+ syslog(LOG_ERR,
+ "add_pingitem: Couldn't add '%s' to pinglist (no memory)",
+ buf);
+ XFREE(pp);
+ return;
+ }
+ pp->mtime = ptime;
+ if (dir)
+ pp->obj = nis_clone_object(dir, NULL);
+ else
+ pp->obj = NULL;
+ /* Update time (utime) == 0 means resync ASAP */
+ pp->utime = 0;
+ pp->delta = INIT_UPD_LIST_TIME_INCR;
+ (void) __nis_insert_item_mt(pp, tbl, 0);
+ setPingWakeup(ptime + updateBatchingTimeout());
+ if (verbose)
+ syslog(LOG_INFO, "add_pingitem: added %s", buf);
+}
+
+void
+add_pingitem(dir, ptime, tbl)
+ nis_object *dir;
+ u_long ptime;
+ NIS_HASH_TABLE *tbl;
+{
+ char buf[1024];
+
+ if (dir == NULL)
+ return;
+
+ sprintf(buf, "%s.%s", dir->zo_name, dir->zo_domain);
+ add_pingitem_with_name(buf, dir, ptime, tbl);
+}
+
+/*
+ * 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 {
+ nis_name master;
+ u_long expiry;
+};
+
+static bool_t
+reset_dl_expires(NIS_HASH_ITEM *nhi, void *funcarg)
+{
+ struct directory_item *dd = (struct directory_item *)nhi;
+ struct reset_dl_expires_args *args = funcarg;
+
+ if (strcmp(GETSERVER(dd->dl_obj, 0)->name, args->master) == 0) {
+ if (dd->dl_expires < args->expiry) {
+ dd->dl_expires = args->expiry;
+ if (verbose)
+ syslog(LOG_DEBUG, "reset_dl_expires: reset "
+ "expiry for directory %s to %d",
+ dd->dl_item.name, args->expiry);
+ } else {
+ if (verbose)
+ syslog(LOG_DEBUG, "reset_dl_expires: skipping "
+ "directory %s, expiry %d is later",
+ dd->dl_item.name, dd->dl_expires);
+ }
+ } else {
+ if (verbose)
+ syslog(LOG_DEBUG, "reset_dl_expires: skipping "
+ "directory %s as master (%s) doesn't match",
+ dd->dl_item.name, GETSERVER(dd->dl_obj, 0)->name);
+ }
+ /* 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
+update_dl_item(nis_name name,
+ struct directory_item *dd,
+ nis_object* d_obj,
+ struct timeval *tv,
+ enum name_pos p)
+{
+ nis_name rootname;
+ nis_server *master;
+ int retry_time = 1800; /* Backoff time for a dead master */
+ struct reset_dl_expires_args funcarg;
+
+ dd->dl_expires = tv->tv_sec + d_obj->zo_ttl;
+ dd->dl_obj = d_obj;
+
+ if ((dd->dl_serve = nis_isserving(d_obj)) == 0)
+ return;
+
+ /*
+ * Add it to the list of directories served by this
+ * server if not already listed.
+ */
+ nis_server_control(SERVING_LIST, DIR_ADD, name);
+
+ if (dd->dl_serve == 1) /* master */
+ dd->dl_stamps[0] = last_update(name);
+ else { /* replica */
+
+ ulong_t timestamp;
+
+ /*
+ * schedule update for directory contents but don't
+ * try too hard.
+ */
+ master = GETSERVER(d_obj, 0);
+ timestamp = nis_cptime_msg(master, name, FALSE, FALSE);
+ /*
+ * 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)
+ syslog(LOG_WARNING, "update_dl_item: "
+ "unable to get timestamp for %s "
+ "from master %s",
+ name, master->name);
+ funcarg.master = master->name;
+ funcarg.expiry = tv->tv_sec + retry_time;
+ if (verbose)
+ syslog(LOG_INFO, "update_dl_item: "
+ "reseting expiry to %d", funcarg.expiry);
+ /* Update the hash list */
+ nis_scan_table(dl, reset_dl_expires, &funcarg);
+ /*
+ * The current entry may not be on the hash
+ * list yet so we explicitly reset it here.
+ */
+ dd->dl_expires = funcarg.expiry;
+ return;
+ }
+ /*
+ * We have a valid timestamp from the master. Check and
+ * update upd_list accordingly.
+ */
+ dd->dl_stamps[0] = timestamp;
+ if (!readonly && last_update(name) < timestamp) {
+ add_pingitem(dd->dl_obj, timestamp, &upd_list);
+ }
+
+ /* If root object, schedule update for object itself too */
+ if (!readonly && p == SAME_NAME &&
+ ((rootname = __nis_local_root()) != 0) &&
+ nis_dir_cmp(name, rootname) == SAME_NAME &&
+ last_update(ROOT_OBJ) < d_obj->zo_oid.mtime) {
+ add_pingitem_with_name(ROOT_OBJ,
+ d_obj,
+ d_obj->zo_oid.mtime,
+ &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;
+
+nis_error
+__directory_object(name, ticks, is_master, obj)
+ nis_name name;
+ struct ticks *ticks;
+ int is_master; /* Need we be the master ? */
+ nis_object **obj;
+{
+ return (__directory_object_msg(name, ticks, is_master, obj, 1));
+}
+
+#define INCDIRSTAT(stat) WLOCK(dircachestats); \
+ stat ++; \
+ WULOCK(dircachestats);
+
+/*
+ * __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.
+ */
+nis_error
+__directory_object_msg(name, ticks, is_master, obj, msg)
+ nis_name name;
+ struct ticks *ticks;
+ int is_master; /* Need we be the master ? */
+ nis_object **obj;
+ int msg;
+{
+ register nis_object *d_obj = NULL;
+ register struct directory_item *dd;
+ nis_db_result *dbres = NULL;
+ nis_result *zres = NULL;
+ int lookup_flags = 0;
+ struct timeval tv;
+ 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.
+ */
+ *obj = NULL;
+
+ if (name == NULL)
+ return (NIS_BADNAME); /* illegal name */
+
+ p = nis_dir_cmp(name, __nis_rpc_domain());
+ if ((p == HIGHER_NAME) || (p == NOT_SEQUENTIAL) || (p == BAD_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.
+ */
+ if ((is_master & NO_CACHE) != 0) {
+ is_master = ((is_master & TRUE) != 0);
+ /* get latest copy of directory object */
+ lookup_flags = MASTER_ONLY+NO_CACHE;
+ }
+
+ WLOCK(dl);
+ if (! dl) {
+ dl = (NIS_HASH_TABLE *)calloc(1, sizeof (NIS_HASH_TABLE));
+ if (!dl) {
+ syslog(LOG_ERR, "__directory_object: out of memory");
+ WULOCK(dl);
+ return (NIS_NOMEMORY);
+ }
+ __nis_init_hash_table(dl, destroyDirCacheItem);
+ }
+ WULOCK(dl);
+
+ /* some statistics stuff ... */
+ memset((char *)ticks, 0, sizeof (struct ticks));
+ INCDIRSTAT(dircachecall);
+ if (verbose)
+ syslog(LOG_INFO, "Looking for directory object %s", name);
+
+ /*
+ * Now we check the cache to see if we have it cached locally.
+ */
+ (void) gettimeofday(&tv, NULL);
+ dd = (struct directory_item *)__nis_find_item_mt(name, dl, 1, 0);
+
+ /*
+ * 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 && (lookup_flags & NO_CACHE)) {
+ free_dlitem(dd);
+ (void) __nis_release_item(dd, dl, 1);
+ dd = NULL;
+ }
+
+ if (dd) {
+ if (verbose)
+ syslog(LOG_INFO, "__directory_object: Cache hit.");
+ INCDIRSTAT(dircachehit);
+ /*
+ * 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
+ */
+ if ((tv.tv_sec < dd->dl_expires) && !(is_master)) {
+ if (dd->dl_serve) {
+ *obj = dd->dl_obj;
+ dirDeferRelease(dd, 0);
+ return (NIS_SUCCESS); /* Here it is ... */
+ } else {
+ /* Should never happen */
+ *obj = NULL;
+ (void) __nis_release_item(dd, dl, 1);
+ return (NIS_NOT_ME);
+ }
+ } else if ((tv.tv_sec < dd->dl_expires) && is_master) {
+ if (dd->dl_serve == 1) {
+ *obj = dd->dl_obj;
+ dirDeferRelease(dd, 0);
+ return (NIS_SUCCESS); /* Here it is ... */
+ } else {
+ (void) __nis_release_item(dd, dl, 1);
+ return (NIS_NOTMASTER); /* Not master */
+ }
+ }
+ /* Else it is expired so we're refreshing it. */
+ }
+ if (! dd) {
+ if (verbose)
+ syslog(LOG_INFO, "__directory_object: Cache miss.");
+ INCDIRSTAT(dircachemiss);
+ }
+
+ /*
+ * 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.
+ */
+
+ dbres = db_lookup(name);
+ ticks->dticks += dbres->ticks;
+ if (dbres->status == NIS_SUCCESS) {
+ d_obj = nis_clone_object(dbres->obj, NULL);
+ } else if (dbres->status == NIS_SYSTEMERROR) {
+ syslog(LOG_ERR,
+ "__directory_object: internal database error.");
+ } else if (dbres->status != NIS_NOTFOUND) {
+ /*
+ * (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.
+ */
+ zres = nis_lookup(name, lookup_flags);
+ if (zres->status == NIS_SUCCESS) {
+ d_obj = nis_clone_object(
+ zres->objects.objects_val, 0);
+ ticks->zticks += zres->zticks;
+ ticks->dticks += zres->dticks;
+ ticks->cticks += zres->cticks;
+ ticks->aticks += zres->aticks;
+ } else if (zres->status == NIS_NOTFOUND) {
+ if (dd) {
+ /* It no longer exists */
+ free_dlitem(dd);
+ (void) __nis_release_item(dd, dl, 1);
+ dd = NULL;
+ }
+ } else {
+ if (msg)
+ syslog(LOG_WARNING,
+ "__directory_object: Failed to lookup %s, status %s",
+ name, nis_sperrno(zres->status));
+ }
+ nis_freeresult(zres);
+ } else if (dd) {
+ /* It no longer exists */
+ free_dlitem(dd);
+ (void) __nis_release_item(dd, dl, 1);
+ dd = NULL;
+ }
+
+ /*
+ * 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) {
+ syslog(LOG_WARNING,
+ "__directory_object: Using expired directory object for %s", name);
+ if ((!(is_master) && (dd->dl_serve)) ||
+ (is_master && (dd->dl_serve == 1))) {
+ *obj = dd->dl_obj;
+ dirDeferRelease(dd, 0);
+ return (NIS_S_SUCCESS);
+ } else {
+ *obj = NULL;
+ (void) __nis_release_item(dd, dl, 1);
+ return (NIS_NOT_ME);
+ }
+ } else
+ return (NIS_NOTFOUND);
+ }
+
+ /* If we found one, and it isn't a directory abort */
+ if (__type_of(d_obj) != NIS_DIRECTORY_OBJ) {
+ if (msg)
+ syslog(LOG_WARNING, "Object %s isn't a directory.",
+ name);
+ /* If the cache had one toss it out */
+ if (dd) {
+ free_dlitem(dd);
+ (void) __nis_release_item(dd, dl, 1);
+ }
+ nis_destroy_object(d_obj);
+ 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.
+ */
+ free_dlitem(dd);
+ (void) __nis_release_item(dd, dl, 1);
+ dd = 0;
+ }
+
+ dd = calloc(1, sizeof (*dd));
+ if (dd) {
+ dd->dl_item.name = (nis_name) strdup(name);
+ dd->dl_stamps = (ulong_t *)calloc(MAXSERVER(d_obj),
+ sizeof (ulong_t));
+ update_dl_item(name, dd, d_obj, &tv, p);
+ if (!__nis_insert_item_mt(dd, dl, 1)) {
+ /*
+ * Some other thread got there before us.
+ * Look in the cache for a new copy.
+ */
+ destroyDirCacheItem(dd);
+ dd = __nis_find_item_mt(name, dl, 1, 0);
+ if (dd != 0) {
+ d_obj = dd->dl_obj;
+ } 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) {
+ serve = dd->dl_serve;
+ } else {
+ serve = nis_isserving(d_obj);
+ }
+
+ /* Check the master requirement */
+ if (is_master) {
+ if (serve != 1) {
+ if (dd)
+ (void) __nis_release_item(dd, dl, 1);
+ else
+ nis_destroy_object(d_obj);
+ return (NIS_NOTMASTER);
+ } else {
+ *obj = d_obj;
+ dirDeferRelease(dd, 0);
+ return (NIS_SUCCESS);
+ }
+ }
+
+ if (serve) {
+ *obj = d_obj;
+ dirDeferRelease(dd, 0);
+ return (NIS_SUCCESS);
+ }
+
+ if (dd)
+ (void) __nis_release_item(dd, dl, 1);
+ else
+ nis_destroy_object(d_obj);
+
+ 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.
+ */
+static const nis_name nobody = "nobody";
+
+static NIS_HASH_TABLE credtbl = NIS_HASH_TABLE_MT_INIT;
+
+struct creditem {
+ NIS_HASH_ITEM item;
+ char pname[1024];
+ time_t cred_expires;
+};
+
+static void
+add_cred_item(char *netname, char *pname, time_t cred_expires)
+{
+ struct creditem *foo, *old;
+
+ old = (struct creditem *)nis_find_item(netname, &credtbl);
+ if (old) {
+ __nis_release_item((NIS_HASH_ITEM *)old, &credtbl, -1);
+ return;
+ }
+
+ foo = (struct creditem *)calloc(1, sizeof (struct creditem));
+ foo->cred_expires = cred_expires;
+ foo->item.name = strdup(netname);
+ strcpy(foo->pname, pname);
+ __nis_insert_item_mt(foo, &credtbl, 0);
+}
+
+static int
+find_cred_item(char *netname, char *pname)
+{
+ struct creditem *old;
+
+ old = (struct creditem *)nis_find_item(netname, &credtbl);
+ if (! old || (old->cred_expires < time(0))) {
+ struct creditem *toremove;
+
+ if (toremove =
+ (struct creditem *)nis_remove_item(netname,
+ &credtbl)) {
+ free(toremove->item.name);
+ free(toremove);
+ }
+ return (0);
+ }
+ strcpy(pname, old->pname);
+ __nis_release_item((NIS_HASH_ITEM *)old, &credtbl, -1);
+ 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;
+
+ if (mechs = __nis_get_mechanisms(FALSE)) {
+ mechanism_t **mpp;
+
+ for (mpp = mechs; *mpp; mpp++) {
+ mechanism_t *mp = *mpp;
+
+ if (AUTH_DES_COMPAT_CHK(mp)) {
+ __nis_release_mechanisms(mechs);
+ return (TRUE);
+ }
+ }
+ __nis_release_mechanisms(mechs);
+ 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
+mech_is_permitted(rpc_gss_rawcred_t *rcred)
+{
+ mechanism_t **mechs;
+
+ if (mechs = __nis_get_mechanisms(FALSE)) {
+ mechanism_t **mpp;
+
+ for (mpp = mechs; *mpp; mpp++) {
+ mechanism_t *mp = *mpp;
+
+ if (AUTH_DES_COMPAT_CHK(mp))
+ break;
+
+ if (mp->mechname && rcred->mechanism) {
+ if (strcasecmp(mp->mechname,
+ rcred->mechanism) == 0) {
+ __nis_release_mechanisms(mechs);
+ return (TRUE);
+ }
+ }
+ }
+ __nis_release_mechanisms(mechs);
+ }
+
+ 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
+nis_getprincipal(
+ char *name, /* NIS+ principal (out) */
+ struct svc_req *req) /* service request (in) */
+{
+ struct authsys_parms *au;
+ struct authdes_cred *ad;
+ char *rmtdomain;
+ char srch[2048]; /* search criteria */
+ nis_result *res;
+ int message;
+ caddr_t auth;
+ enum_t flavor;
+ char netname[MAXNETNAMELEN+1] = {0};
+ char auth_type[MECH_MAXATNAME+1] = {0};
+
+ message = verbose || auth_verbose;
+ strcpy(name, nobody); /* default is "nobody" */
+
+ if (req) {
+ flavor = req->rq_cred.oa_flavor;
+ auth = req->rq_clntcred;
+ } else {
+ syslog(LOG_ERR,
+ "RPC request is NULL: returning '%s'", nobody);
+ return;
+ }
+
+ if (flavor == AUTH_NONE) {
+ if (message) {
+ syslog(LOG_INFO,
+ "nis_getprincipal: flavor = NONE: returning '%s'", nobody);
+ }
+ return;
+ } else if (flavor == AUTH_SYS) { /* XXX ifdef this for 4.1 */
+ if (secure_level > 1) {
+ if (message) {
+ syslog(LOG_INFO,
+ "nis_getprincipal: flavor = SYS: returning '%s'",
+ nobody);
+ }
+ return;
+ }
+ au = (struct authsys_parms *)(auth);
+ rmtdomain = nis_domain_of(au->aup_machname);
+ if (au->aup_uid == 0) {
+ sprintf(name, "%s", au->aup_machname);
+ if (! rmtdomain)
+ strcat(name, __nis_rpc_domain());
+ if (name[strlen(name) - 1] != '.')
+ strcat(name, ".");
+ if (message) {
+ syslog(LOG_INFO,
+ "nis_getprincipal: flavor = SYS: returning '%s'", name);
+ }
+ return;
+ }
+ sprintf(srch,
+ "[auth_name=\"%d\", auth_type=LOCAL], cred.org_dir.%s",
+ au->aup_uid, (*rmtdomain == '.') ?
+ (char *)nis_local_directory() : rmtdomain);
+ if (srch[strlen(srch) - 1] != '.') {
+ strcat(srch, ".");
+ }
+ } else if (flavor == AUTH_DES) {
+
+ if (secure_level > 1 && ! auth_des_permitted()) {
+ if (message) {
+ syslog(LOG_INFO,
+ "nis_getprincipal: AUTH_DES not permitted: returning '%s'",
+ nobody);
+ }
+ return;
+ }
+
+ ad = (struct authdes_cred *)(auth);
+ if (find_cred_item(ad->adc_fullname.name, name)) {
+ if (message)
+ syslog(LOG_INFO,
+ "nis_getprincipal: flavor = DES: returning from cache '%s'",
+ name);
+ return;
+ }
+ rmtdomain = strchr(ad->adc_fullname.name, '@');
+ if (rmtdomain) {
+ rmtdomain++;
+ sprintf(srch,
+ "[auth_name=%s, auth_type=DES], cred.org_dir.%s",
+ ad->adc_fullname.name, rmtdomain);
+ if (srch[strlen(srch) - 1] != '.') {
+ strcat(srch, ".");
+ (void) strncpy(netname, ad->adc_fullname.name,
+ sizeof (netname));
+ netname[sizeof (netname) - 1] = '\0';
+ }
+ } else {
+ if (auth_verbose) {
+ syslog(LOG_INFO,
+ "nis_getprincipal: flavor = DES: returning '%s'",
+ nobody);
+ }
+ return;
+ }
+ } else if (flavor == RPCSEC_GSS) {
+ rpc_gss_rawcred_t *rcred;
+ void *cookie;
+
+ if (! rpc_gss_getcred(req, &rcred, NULL, &cookie)) {
+ if (message) {
+ syslog(LOG_WARNING,
+ "nis_getprincipal: GSS getcred failure: returning '%s'",
+ nobody);
+ }
+ return;
+ }
+
+ if (secure_level > 1 && ! mech_is_permitted(rcred)) {
+ if (message)
+ syslog(LOG_INFO,
+"nis_getprincipal: RPC GSS mechanism '%s' not permitted: returning nobody",
+ rcred->mechanism ? rcred->mechanism
+ : "NULL");
+ return;
+ }
+
+ if (__nis_gssprin2netname(rcred->client_principal,
+ netname) < 0) {
+ syslog(LOG_ERR,
+"nis_getprincipal: can't extract netname from RPC GSS cred: returning nobody");
+ return;
+ }
+
+ if (find_cred_item(netname, name)) {
+ if (message)
+ syslog(LOG_INFO,
+ "nis_getprincipal: flavor = GSS: returning '%s' from cache",
+ name);
+ return;
+ }
+ rmtdomain = strchr(netname, '@');
+ if (rmtdomain) {
+ char alias[MECH_MAXALIASNAME+1] = { 0 };
+
+ rmtdomain++;
+ if (! __nis_mechname2alias(rcred->mechanism, alias,
+ sizeof (alias))) {
+ syslog(LOG_ERR,
+ "nis_getprincipal: GSS mechname '%s' not found: returning nobody",
+ rcred->mechanism);
+ return;
+ }
+
+ if (alias[0] != '\0') {
+ (void) __nis_mechalias2authtype(alias,
+ auth_type,
+ sizeof (auth_type));
+
+ (void) snprintf(srch, NIS_MAXNAMELEN,
+ "[auth_name=%s, auth_type=%s], cred.org_dir.%s",
+ netname, auth_type, rmtdomain);
+
+ if (srch[strlen(srch) - 1] != '.') {
+ strcat(srch, ".");
+ }
+ } else {
+ syslog(LOG_ERR,
+"nis_getprincipal: no alias was found for mechname '%s': returning 'nobody'",
+ rcred->mechanism);
+ return;
+ }
+ } else {
+ if (auth_verbose) {
+ syslog(LOG_INFO,
+ "nis_getprincipal: flavor = GSS: returning '%s'",
+ nobody);
+ }
+ return;
+ }
+ } else {
+ syslog(LOG_WARNING,
+ "nis_getprincipal: flavor = %d(unknown): returning '%s'",
+ flavor, nobody);
+ return;
+ }
+
+ if (message)
+ if (flavor == AUTH_DES || flavor == RPCSEC_GSS)
+ syslog(LOG_INFO,
+ "nis_getprincipal: calling list with name '%s' and type '%s'",
+ netname,
+ flavor == AUTH_DES ? "DES" : auth_type);
+ else /* AUTH_SYS */
+ syslog(LOG_INFO,
+ "nis_getprincipal: calling list with uid (LOCAL) '%d'",
+ au->aup_uid);
+
+ res = nis_list(srch, NO_AUTHINFO+USE_DGRAM+FOLLOW_LINKS, NULL, NULL);
+ if (res->status != NIS_SUCCESS) {
+ if (message)
+ syslog(LOG_INFO,
+ "nis_getprincipal: error doing nis_list: %s",
+ nis_sperrno(res->status));
+ } else {
+ if (strlcpy(name, ENTRY_VAL(res->objects.objects_val, 0), 1024)
+ >= 1024) {
+ strcpy(name, nobody); /* default is "nobody" */
+ syslog(LOG_ERR,
+ "nis_getprincipal: buffer overflow, returning '%s'", nobody);
+ nis_freeresult(res);
+ return;
+ }
+ if (flavor == AUTH_DES || flavor == RPCSEC_GSS) {
+ if (message)
+ syslog(LOG_INFO,
+ "nis_getprincipal: caching '%s'/'%s'",
+ netname, name);
+ add_cred_item(netname, name,
+ res->objects.objects_val[0].zo_ttl+time(0));
+ }
+ }
+
+ nis_freeresult(res);
+ if (message)
+ syslog(LOG_INFO,
+ "nis_getprincipal: flavor = %s: returning : '%s'",
+ flavor2str(flavor), name);
+}
+
+/*
+ * __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
+__can_do(right, mask, obj, pr)
+ unsigned long right; /* The Access right we desire */
+ unsigned long mask; /* The mask for World/Group/Owner */
+ nis_object *obj; /* A pointer to the object */
+ nis_name pr; /* Principal making the request */
+{
+ if ((secure_level == 0) || (*pr == 0))
+ return (1);
+
+ return (NIS_NOBODY(mask, right) ||
+ (NIS_WORLD(mask, right) &&
+ (strcmp(pr, "nobody") != 0)) ||
+ (NIS_OWNER(mask, right) &&
+ (nis_dir_cmp(pr, obj->zo_owner) == SAME_NAME)) ||
+ (NIS_GROUP(mask, right) &&
+ (strlen(obj->zo_group) > (size_t)(1)) &&
+ __do_ismember(pr, obj, nis_lookup)));
+}
+
+/* BEGIN CSTYLED */
+/*
+ * Get an xdr buffer
+ */
+u_char *
+__get_xdr_buf(size)
+ int size; /* Size in bytes of the buffer */
+{
+#ifdef local_buf
+#undef local_buf
+#endif
+#define local_buf (__nis_get_tsd()->local_buf__get_xdr_buf)
+
+ return ((u_char *)nis_get_static_storage(&local_buf, 1, size));
+}
+/* END CSTYLED */
+
+/*
+ * Get a string buffer
+ */
+char *
+__get_string_buf(size)
+ int size; /* Size in bytes of the buffer */
+{
+#ifdef local_buf
+#undef local_buf
+#endif
+#define local_buf (__nis_get_tsd()->local_buf__get_string_buf)
+
+ return ((char *)nis_get_static_storage(&local_buf, 1, size));
+}
+
+/*
+ * get an array of entry columns
+ */
+entry_col *
+__get_entry_col(n)
+ int n; /* required array size */
+{
+#ifdef local_buf
+#undef local_buf
+#endif
+#define local_buf (__nis_get_tsd()->local_buf__get_entry_col)
+
+ return ((entry_col *)nis_get_static_storage(&local_buf,
+ sizeof (entry_col), n));
+}
+
+/*
+ * get an array of table columns
+ */
+table_col *
+__get_table_col(n)
+ int n; /* required array size */
+{
+#ifdef local_buf
+#undef local_buf
+#endif
+#define local_buf (__nis_get_tsd()->local_buf__get_table_col)
+
+ return ((table_col *)nis_get_static_storage(&local_buf,
+ sizeof (table_col), n));
+}
+
+/*
+ * get an array of nis attributes
+ */
+nis_attr *
+__get_attrs(n)
+ int n; /* required array size */
+{
+#ifdef local_buf
+#undef local_buf
+#endif
+#define local_buf (__nis_get_tsd()->local_buf__get_attrs)
+
+ return ((nis_attr *)nis_get_static_storage(&local_buf,
+ 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
+nis_isstable(le, first)
+ log_entry *le;
+ int first;
+{
+ int i;
+ struct directory_item *dd;
+ nis_object *dobj;
+ struct ticks t;
+ nis_name dirname;
+ nis_error err;
+ bool_t ret;
+
+
+ if (le->le_type == UPD_STAMP)
+ dirname = le->le_name;
+ else
+ dirname = nis_domain_of(le->le_name);
+
+ /*
+ * 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)
+ err = __directory_object(dirname, &t, NO_CACHE+1, &dobj);
+ else
+ err = __directory_object(dirname, &t, 1, &dobj);
+
+ /*
+ * If we aren't the master any more, or the directory doesn't exist
+ * anymore we can toss this update.
+ */
+ if ((err == NIS_NOTMASTER) || (err == NIS_NOTFOUND) ||
+ (err == NIS_BADNAME))
+ return (TRUE);
+
+ /*
+ * If we encountered a problem trying to get the directory object
+ * we hold on to the update.
+ */
+ if ((err != NIS_SUCCESS) && (err != NIS_S_SUCCESS))
+ return (FALSE);
+
+ /*
+ * If we look at the object and there is only one server then
+ * the update has by definition propogated.
+ */
+ if (MAXSERVER(dobj) == 1)
+ 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().
+ */
+ dd = __nis_find_item_mt(dirname, dl, 1, 0);
+ if (!dd)
+ return (TRUE);
+ ret = TRUE;
+ for (i = 1; i < MAXSERVER(dobj); i++) {
+ if (dd->dl_stamps[i] == 0) {
+ /* Fetch the time from the replica */
+ dd->dl_stamps[i] = nis_cptime_msg(GETSERVER(dobj, i),
+ dd->dl_item.name,
+ FALSE, TRUE);
+
+ if (! dd->dl_stamps[i]) {
+ dd->dl_stamps[i] = 1;
+ }
+ }
+
+ if (dd->dl_stamps[i] < le->le_time)
+ ret = FALSE;
+ }
+ __nis_release_item((NIS_HASH_ITEM *)dd, dl, 1);
+ 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
+add_fenceposts()
+{
+ int i;
+ struct directory_item *dd;
+
+ LOCK_LIST(dl, "add_fenceposts(dl)");
+ for (dd = (struct directory_item *)dl->first; dd;
+ dd = (struct directory_item *)(dd->dl_item.nxt_item)) {
+ for (i = 0; i < MAXSERVER(dd->dl_obj); i++) {
+ if (nis_dir_cmp(GETSERVER(dd->dl_obj, i)->name,
+ nis_local_host()) == SAME_NAME) {
+ if (dd->dl_stamps[i] >
+ last_update(dd->dl_item.name)) {
+ make_stamp(dd->dl_item.name,
+ dd->dl_stamps[i]);
+ break;
+ }
+ }
+ }
+ }
+ ULOCK_LIST(dl, "add_fenceposts(dl)");
+}
+
+/*
+ * 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
+apply_update(le)
+ log_entry *le;
+{
+ nis_error status;
+ char *msg;
+
+ if (cons) {
+ fprintf(cons, "update type %d, name %s.%s\n", le->le_type,
+ le->le_object.zo_name, le->le_object.zo_domain);
+ }
+
+ switch (le->le_type) {
+ case ADD_NAME :
+ status = __db_add(le->le_name, &(le->le_object), 0);
+ msg = "Failed to add object ";
+ break;
+ case MOD_NAME_NEW :
+ status = __db_add(le->le_name, &(le->le_object), 1);
+ msg = "Failed to modify object ";
+ break;
+
+ case REM_NAME :
+ status = __db_remove(le->le_name, &(le->le_object));
+ msg = "Failed to remove object ";
+ break;
+
+ /* For old modified objects, just add this to the log */
+ case MOD_NAME_OLD :
+ status = NIS_SUCCESS;
+ break;
+
+ case ADD_IBASE :
+ status = __db_addib_nosync(le->le_name,
+ le->le_attrs.le_attrs_len,
+ le->le_attrs.le_attrs_val,
+ &(le->le_object));
+ msg = "Failed to add entry ";
+ break;
+
+ case REM_IBASE :
+ status = __db_remib_nosync(le->le_name,
+ le->le_attrs.le_attrs_len,
+ le->le_attrs.le_attrs_val);
+ msg = "Failed to remove entry ";
+ break;
+ /* ignore these as they are both NOPs */
+ case UPD_STAMP :
+ case LOG_NOP :
+ status = NIS_SUCCESS;
+ break;
+
+ default :
+ status = NIS_SYSTEMERROR;
+ msg = "Illegal transaction type on ";
+ break;
+ }
+ if (status != NIS_SUCCESS) {
+ syslog(LOG_ERR, "apply_update : %s %s NIS+ STATUS : %s",
+ msg, __make_name(le), nis_sperrno(status));
+ if (cons)
+ fprintf(cons, "apply_update : %s %s\n",
+ msg, __make_name(le));
+ }
+
+ 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
+clear_directory(name)
+ nis_name name;
+{
+ nis_db_list_result *tables;
+ int i, n;
+ char namebuf[NIS_MAXNAMELEN+1];
+ nis_error status;
+
+ tables = db_list_flags(name, 0, NULL, FN_NOMANGLE+FN_NORAGS);
+ if (tables != NULL && tables->status == NIS_SUCCESS) {
+ for (i = 0; i < tables->numo; i++) {
+ n = snprintf(namebuf, sizeof (namebuf), "%s.%s",
+ tables->objs[i].o->zo_name,
+ tables->objs[i].o->zo_domain);
+ if (n < 1 || n > sizeof (namebuf)) {
+ syslog(LOG_WARNING,
+ "clear_directory: buffer (%d) insufficient for \"%s.%s\" (%d)",
+ sizeof (namebuf),
+ tables->objs[i].o->zo_name,
+ tables->objs[i].o->zo_domain,
+ strlen(tables->objs[i].o->zo_name)+
+ strlen(tables->objs[i].o->zo_domain)+
+ 2 /* dot and NUL */);
+ continue;
+ }
+ if (__type_of(tables->objs[i].o) == NIS_TABLE_OBJ) {
+ if ((status = db_destroy(namebuf)) !=
+ NIS_SUCCESS)
+ syslog(LOG_WARNING,
+ "clear_directory: Could not destroy table \"%s\": %s.",
+ namebuf, nis_sperrno(status));
+ } else {
+ /* Not a table, so just remove the object */
+ status = __db_remove(namebuf,
+ tables->objs[i].o);
+ if (status != NIS_SUCCESS)
+ syslog(LOG_WARNING,
+ "clear_directory: Could not remove object \"%s\": %s",
+ namebuf, nis_sperrno(status));
+ }
+ }
+ } else if (tables == NULL || tables->status != NIS_NOTFOUND) {
+ syslog(LOG_WARNING,
+ "clear_directory: could not get object list for \"%s\": %s",
+ name, (tables != NULL) ? nis_sperrno(tables->status) :
+ "<nil>");
+ free_db_list(tables); /* (tables == NULL) OK */
+ return (0);
+ }
+ /*
+ * else (tables->status == NIS_NOTFOUND), which is fine by us;
+ * someone already did our work.
+ */
+
+ free_db_list(tables); /* (tables == NULL) OK */
+ if ((status = db_destroy(name)) != NIS_SUCCESS) {
+ syslog(LOG_WARNING,
+ "Could not destroy table %s: %s.",
+ name, nis_sperrno(status));
+ /* XXX recover? */
+ }
+
+ /* Make a clean version. */
+ if ((status = db_create(name, &tbl_prototype)) != NIS_SUCCESS) {
+ syslog(LOG_WARNING,
+ "Could not create table %s: %s.",
+ name, nis_sperrno(status));
+ /* XXX recover? */
+ }
+
+ if (verbose)
+ syslog(LOG_INFO, "directory '%s' cleared.", name);
+ return (1);
+}
+
+#define repl_stats (__nis_get_tsd()->repl_stats)
+
+/*
+ * nis_makename()
+ *
+ * This function prints out a nice name for a objects.
+ */
+void
+nis_makename(obj, tobj, str)
+ nis_object *obj;
+ nis_object *tobj;
+ char *str;
+{
+ int i;
+ table_col *tcols;
+ entry_col *ecols;
+ int ncols;
+
+ if (tobj == NULL) {
+ sprintf(str, "%s.%s", obj->zo_name, obj->zo_domain);
+ return;
+ }
+
+ strcpy(str, "[");
+ tcols = tobj->TA_data.ta_cols.ta_cols_val;
+ ncols = tobj->TA_data.ta_cols.ta_cols_len;
+ ecols = obj->EN_data.en_cols.en_cols_val;
+
+ for (i = 0; i < ncols; i++) {
+ if (tcols[i].tc_flags & TA_SEARCHABLE) {
+ strcat(str, tcols[i].tc_name);
+ strcat(str, " = \"");
+ if (ecols[i].ENLEN)
+ strncat(str, ecols[i].ENVAL, ecols[i].ENLEN);
+ strcat(str, "\", ");
+ }
+ }
+
+ str[strlen(str) - 2] = '\0';
+ strcat(str, "],");
+ strcat(str, obj->zo_name);
+ strcat(str, ".");
+ if (strlen(obj->zo_domain)) {
+ strcat(str, obj->zo_domain);
+ }
+}
+
+/*
+ * 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.
+ */
+static NIS_HASH_TABLE repl_objs = NIS_HASH_TABLE_MT_INIT;
+
+/* This is the callback for nis_dump (Full Resyncs) */
+int
+update_directory(name, obj, x)
+ nis_name name;
+ nis_object *obj;
+ void *x;
+{
+ nis_db_result *dbres;
+ nis_error status;
+ nis_object *tobj;
+ char namebuf[NIS_MAXNAMELEN+1];
+ int i, na, mc;
+ entry_col *ec;
+ table_col *tc;
+ nis_attr *attr_list;
+ ping_item *pp;
+ int release_pp = 0;
+
+ if (obj->zo_oid.mtime > repl_stats.utime) {
+ repl_stats.utime = obj->zo_oid.mtime;
+ }
+
+ /*
+ * Note :
+ * Once entered this if block returns to the caller.
+ */
+ if (__type_of(obj) == NIS_ENTRY_OBJ) {
+ pp = (ping_item *)nis_find_item(name, &repl_objs);
+ if (! pp) {
+ dbres = db_lookup_deferred(name, FALSE);
+ repl_stats.ticks += dbres->ticks;
+ if (dbres->status == NIS_SUCCESS)
+ add_pingitem(dbres->obj, 1, &repl_objs);
+ tobj = dbres->obj;
+ } else {
+ release_pp = 1;
+ tobj = pp->obj;
+ }
+
+ if (tobj) {
+ if (verbose) {
+ nis_makename(obj, tobj, namebuf);
+ syslog(LOG_INFO, "update_dir: %s", namebuf);
+ }
+ /* Build a fully specified name from the entry */
+ mc = tobj->TA_data.ta_cols.ta_cols_len;
+ tc = tobj->TA_data.ta_cols.ta_cols_val;
+ ec = obj->EN_data.en_cols.en_cols_val;
+ attr_list = __get_attrs(mc);
+ if (attr_list == NULL) {
+ syslog(LOG_WARNING,
+ "update_directory: out of memory resync aborted.");
+ repl_stats.errors++;
+ if (release_pp)
+ __nis_release_item(
+ (NIS_HASH_ITEM *)pp, &repl_objs, -1);
+ return (1);
+ }
+ for (i = 0, na = 0; i < mc; i++) {
+ if ((tc[i].tc_flags & TA_SEARCHABLE) != 0) {
+ attr_list[na].zattr_ndx = tc[i].tc_name;
+ attr_list[na].ZAVAL = ec[i].ENVAL;
+ attr_list[na].ZALEN = ec[i].ENLEN;
+ na++;
+ }
+ }
+ status = __db_addib_nolog(name, na, attr_list, obj);
+ if (status == NIS_SUCCESS) {
+ repl_stats.successes++;
+ if ((repl_stats.successes % 1000) == 0)
+ syslog(LOG_INFO,
+ "update_directory : %d objects, still running.",
+ repl_stats.successes);
+ if (release_pp)
+ __nis_release_item(
+ (NIS_HASH_ITEM *)pp, &repl_objs, -1);
+ return (0);
+ } else {
+ nis_makename(obj, tobj, namebuf);
+ syslog(LOG_ERR,
+ "replica_update (update_directory): adding %s returned %s",
+ namebuf, nis_sperrno(status));
+ repl_stats.errors++;
+ if (release_pp)
+ __nis_release_item(
+ (NIS_HASH_ITEM *)pp, &repl_objs, -1);
+ return (1);
+ }
+ } else {
+ syslog(LOG_ERR,
+ "replica_update (update_directory): adding entry to %s returned %s",
+ name, dbres->status);
+ repl_stats.errors++;
+ if (release_pp)
+ __nis_release_item(
+ (NIS_HASH_ITEM *)pp, &repl_objs, -1);
+ return (1);
+ }
+ }
+
+ /*
+ * At this point it isn't an ENTRY object so we're not adding
+ * it to a table.
+ */
+ if (verbose) {
+ nis_makename(obj, NULL, namebuf);
+ syslog(LOG_INFO, "update_dir: %s", namebuf);
+ }
+ status = __db_add(name, obj, 0);
+ if (status == NIS_SUCCESS) {
+ repl_stats.successes++;
+ if (release_pp)
+ __nis_release_item((NIS_HASH_ITEM *)pp, &repl_objs,
+ -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 (__type_of(obj) == NIS_TABLE_OBJ) {
+ if ((status = db_destroy(name)) == NIS_SUCCESS) {
+ status = __db_add(name, obj, 0);
+ if (status == NIS_SUCCESS) {
+ repl_stats.successes++;
+ return (0);
+ }
+ }
+ }
+ repl_stats.errors++;
+ if (release_pp)
+ __nis_release_item((NIS_HASH_ITEM *)pp, &repl_objs,
+ -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
+verify_table_exists(nis_name name)
+{
+ nis_error status;
+
+ status = db_find_table(name);
+ switch (status) {
+ case NIS_NOSUCHTABLE:
+ if ((status = db_create(name, &tbl_prototype)) !=
+ NIS_SUCCESS) {
+ syslog(LOG_ERR,
+ "verify_table_exists: cannot create table for %s:%s.",
+ name, nis_sperrno(status));
+ return (0);
+ }
+ break;
+ case NIS_SUCCESS:
+ break;
+ default:
+ syslog(LOG_ERR,
+ "verify_table_exists: unexpected database error for %s: %s.",
+ name, nis_sperrno(status));
+ 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
+root_replica_update()
+{
+ nis_object *obj;
+ nis_result *res = 0;
+ nis_name root_dir = __nis_rpc_domain();
+ int readok;
+ char *myself = "root_replica_update";
+
+ /* If we're reading the root dir from LDAP, do nothing */
+ if (getObjMapping(root_dir, 0, 1, &readok, 0) != 0 && readok) {
+ logmsg(MSG_NOTIMECHECK,
+#ifdef NIS_LDAP_DEBUG
+ LOG_WARNING,
+#else
+ LOG_INFO,
+#endif /* NIS_LDAP_DEBUG */
+ "%s: Mapping root dir object from LDAP", myself);
+ return (1);
+ }
+
+ res = nis_lookup(root_dir, MASTER_ONLY);
+ if (res->status != NIS_SUCCESS) {
+ syslog(LOG_WARNING,
+"root_replica_update: update failed '%s': could not fetch object from master.",
+ root_dir);
+ nis_freeresult(res);
+ return (0); /* failed, try again later. */
+ }
+
+ obj = res->objects.objects_val;
+ if (we_serve(&(obj->DI_data), 0)) {
+ if (verbose)
+ syslog(LOG_INFO,
+ "root_replica_update: updating '%s'",
+ root_dir);
+ if (!update_root_object(root_dir, obj)) {
+ nis_freeresult(res);
+ return (0); /* failed, try again later. */
+ }
+ } else {
+ if (verbose)
+ syslog(LOG_INFO,
+ "root_replica_update: removing '%s'",
+ root_dir);
+ remove_root_object(root_dir, obj);
+ }
+ nis_freeresult(res);
+ 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
+isAllMapped(nis_name dirname) {
+ nis_db_list_result *objs;
+ int i, n, ok;
+ char namebuf[NIS_MAXNAMELEN+1];
+ nis_error status;
+ char *myself = "isAllMapped";
+
+ objs = db_list_flags(dirname, 0, 0, FN_NOMANGLE+FN_NORAGS);
+ if (objs == 0 || objs->status != NIS_SUCCESS) {
+ logmsg(MSG_NOTIMECHECK,
+#ifdef NIS_LDAP_DEBUG
+ LOG_WARNING,
+#else
+ LOG_INFO,
+#endif /* NIS_LDAP_DEBUG */
+ "%s: Unable to get directory list for \"%s\": %s",
+ myself, NIL(dirname),
+ objs == 0 ? "<NULL>" : nis_sperrno(objs->status));
+ return (0);
+ }
+
+ for (ok = 1, i = 0; ok && i < objs->numo; i++) {
+ int readok;
+
+ n = snprintf(namebuf, sizeof (namebuf), "%s.%s",
+ objs->objs[i].o->zo_name, objs->objs[i].o->zo_domain);
+ if (n < 1 || n > sizeof (namebuf)) {
+ logmsg(MSG_NOTIMECHECK,
+#ifdef NIS_LDAP_DEBUG
+ LOG_WARNING,
+#else
+ LOG_INFO,
+#endif /* NIS_LDAP_DEBUG */
+ "%s: Buffer size (%d) insufficient for \"%s.%s\" (need %d)",
+ myself, sizeof (namebuf),
+ objs->objs[i].o->zo_name,
+ objs->objs[i].o->zo_domain,
+ strlen(objs->objs[i].o->zo_name) +
+ strlen(objs->objs[i].o->zo_domain) + 2);
+ ok = 0;
+ continue;
+ }
+
+ if (getObjMapping(namebuf, 0, 1, &readok, 0) == 0 ||
+ readok == 0) {
+ logmsg(MSG_NOTIMECHECK,
+#ifdef NIS_LDAP_DEBUG
+ LOG_WARNING,
+#else
+ LOG_INFO,
+#endif /* NIS_LDAP_DEBUG */
+ "%s: No read mapping for obj \"%s\"",
+ myself, namebuf);
+ ok = 0;
+ continue;
+ }
+
+ if (__type_of(objs->objs[i].o) == NIS_TABLE_OBJ &&
+ (getObjMapping(namebuf, 0, 0, &readok, 0) == 0 ||
+ readok == 0)) {
+ logmsg(MSG_NOTIMECHECK,
+#ifdef NIS_LDAP_DEBUG
+ LOG_WARNING,
+#else
+ LOG_INFO,
+#endif /* NIS_LDAP_DEBUG */
+ "%s: No read mapping for entries in \"%s\"",
+ myself, namebuf);
+ ok = 0;
+ continue;
+ }
+ }
+
+ free_db_list(objs);
+
+ 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
+sameEntryObj(log_entry *l1, log_entry *l2) {
+ int i;
+
+ if (l1 == 0 || l2 == 0)
+ return (0);
+
+ /* Must both be entry objects */
+ if (l1->le_object.zo_data.zo_type != l2->le_object.zo_data.zo_type)
+ return (0);
+
+ if (l1->le_object.zo_data.zo_type != NIS_ENTRY_OBJ)
+ return (0);
+
+ /* Same table name */
+ if (l1->le_name == 0 || l2->le_name == 0 ||
+ strcmp(l1->le_name, l2->le_name) != 0)
+ return (0);
+
+ /* Same number of entry selector attributes */
+ if (l1->le_attrs.le_attrs_len != l2->le_attrs.le_attrs_len)
+ return (0);
+
+ /*
+ * Check the selector attribute names/values. For simplicity,
+ * we require that they're in the same order.
+ */
+ for (i = 0; i < l1->le_attrs.le_attrs_len; i++) {
+ if (l1->le_attrs.le_attrs_val[i].zattr_ndx == 0 ||
+ l2->le_attrs.le_attrs_val[i].zattr_ndx == 0 ||
+ strcmp(l1->le_attrs.le_attrs_val[i].zattr_ndx,
+ l2->le_attrs.le_attrs_val[i].zattr_ndx) != 0)
+ return (0);
+ if (l1->le_attrs.le_attrs_val[i].zattr_val.zattr_val_len !=
+ l2->le_attrs.le_attrs_val[i].zattr_val.zattr_val_len)
+ return (0);
+ if (memcmp(l1->le_attrs.le_attrs_val[i].zattr_val.zattr_val_val,
+ l2->le_attrs.le_attrs_val[i].zattr_val.zattr_val_val,
+ l1->le_attrs.le_attrs_val[i].zattr_val.zattr_val_len) !=
+ 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
+replica_update(upd)
+ ping_item *upd;
+{
+ nis_name name; /* Directory name */
+ nis_server *srv; /* Endpoint for the server */
+ log_result *lres;
+ log_entry *le;
+ log_upd *curr_upd = 0;
+ char ctbuf[CTBUFSIZ];
+ ulong_t ttime; /* timestamp retrieved from last_update() */
+ extern void __nis_freelogresult(log_result *);
+
+ /* timestamp used to write back into log when full resync is done */
+ ulong_t xttime;
+ int i, xid, nument, lstat;
+ ping_item *pp, *nxt;
+ db_status dbStatus = DB_SUCCESS;
+ __nisdb_retry_t dumpRetry = ldapConfig.dumpErrorTimeout;
+ /* If a full dump fails, is it unsafe to return (i.e., try again) ? */
+ bool_t dumpReturnUnsafe = FALSE;
+ char *myself = "replica_update";
+
+ if (verbose)
+ syslog(LOG_INFO, "replica_update:");
+
+ memset((char *)(&repl_stats), 0, sizeof (repl_stats));
+
+ if (cons)
+ fprintf(cons, "replica_update: %s\n", upd->item.name);
+ name = upd->item.name;
+
+ /* check to see if we're replicating the root object */
+ if (root_object_p(name)) {
+ return (root_replica_update());
+ }
+
+ srv = GETSERVER(upd->obj, 0); /* master server for directory */
+
+ if (nis_dir_cmp(srv->name, nis_local_host()) == SAME_NAME) {
+ syslog(LOG_WARNING,
+"host %s thinks that it is the replica for %s, but it is the master!",
+ nis_local_host(), name);
+ return (1);
+ }
+
+ if (verbose)
+ syslog(LOG_INFO, "replica_update: updating '%s'", name);
+
+ 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)) {
+ ttime = nis_cptime_msg(srv, name, FALSE, TRUE);
+ if (ttime != 0L && ttime != ULONG_MAX) {
+ make_stamp(name, ttime);
+ logmsg(MSG_NOTIMECHECK, LOG_INFO,
+ "%s: All objects in \"%s\" mapped from LDAP; "
+ "setting update time to %s",
+ myself, name, ctime_r((time_t *)&ttime, ctbuf,
+ CTBUFSIZ));
+ return (1);
+ } else {
+ logmsg(MSG_NOTIMECHECK, LOG_WARNING,
+ "%s: Unable to get update time for \"%s\" from \"%s\"",
+ myself, name, srv->name);
+ /* 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.
+ */
+ lstat = lockTransLog(myself, 1, 0);
+ if (lstat != 0)
+ return (0);
+
+ ttime = last_update(name);
+ /*
+ * 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.
+ */
+ if (ttime != 0L && ttime != ULONG_MAX) {
+ /* get the delta from transaction log */
+ if (cons)
+ fprintf(cons,
+ "replica_update: dumping master's log.\n");
+ syslog(LOG_INFO, "replica_update: delta update of %s", name);
+ lres = nis_dumplog(srv, name, ttime);
+ if (lres == NULL) {
+ unlockTransLog(myself, 1);
+ return (0);
+ }
+ if ((lres->lr_status != NIS_SUCCESS) && cons)
+ fprintf(cons, "replica_update: error result was %s\n",
+ nis_sperrno(lres->lr_status));
+ if (lres->lr_status == NIS_SYSTEMERROR) {
+ syslog(LOG_WARNING,
+ "replica_update: Couldn't contact '%s' serving '%s' for an update.",
+ srv->name, name);
+ __nis_freelogresult(lres);
+ unlockTransLog(myself, 1);
+ return (0);
+ }
+
+ if (lres->lr_status == NIS_DUMPLATER) {
+ syslog(LOG_INFO,
+ "replica_update: master server is busy, will try later.");
+ __nis_freelogresult(lres);
+ unlockTransLog(myself, 1);
+ return (0);
+ }
+
+ if (lres->lr_status == NIS_SUCCESS) {
+ int skipped = 0;
+
+ if (verbose)
+ syslog(LOG_INFO,
+ "replica_update: %d updates from '%s'",
+ lres->lr_entries.lr_entries_len, srv->name);
+ if (cons)
+ fprintf(cons,
+ "replica_update: %d updates from '%s'\n",
+ lres->lr_entries.lr_entries_len, srv->name);
+ nument = lres->lr_entries.lr_entries_len;
+ if (nument == 0) {
+ syslog(LOG_WARNING,
+ "replica_update: master server returned NIS_SUCCESS, but 0 updates!");
+ __nis_freelogresult(lres);
+ unlockTransLog(myself, 1);
+ return (1);
+ }
+
+ le = lres->lr_entries.lr_entries_val;
+
+ /*
+ * 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'.
+ */
+ xttime = le[nument-1].le_time;
+
+ /* invalidate this directory in case of crash */
+ make_stamp(name, ULONG_MAX);
+ logmsg(MSG_NOTIMECHECK, LOG_INFO,
+ "%s: Temporarily setting update time for "
+ "\"%s\" to 0x%lx",
+ myself, name, ULONG_MAX);
+ xid = begin_transaction(nis_local_principal());
+ begin_update();
+ dbStatus = nis_put_offline(name, FALSE);
+ if (dbStatus != DB_SUCCESS) {
+ syslog(LOG_ERR,
+ "replica_update: offline DB error %d for \"%s\"",
+ dbStatus, name);
+ }
+ for (i = 0; i < nument; i++) {
+ int shouldskip, readok, io,
+ doingModify = 0;
+ nis_error nerr;
+ /*
+ * Is the object mapped from LDAP ? If so,
+ * skip the update, but consider the
+ * application of the change a success.
+ */
+ io = 0;
+ switch (le[i].le_object.zo_data.zo_type) {
+ 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:
+ shouldskip =
+ (getObjMapping(le[i].le_name, 0,
+ io, &readok, 0) != 0 &&
+ readok != 0);
+ break;
+ default:
+ shouldskip = 0;
+ break;
+ }
+ if (shouldskip) {
+ logmsg(MSG_NOTIMECHECK,
+#ifdef NIS_LDAP_DEBUG
+ LOG_WARNING,
+#else
+ LOG_INFO,
+#endif /* NIS_LDAP_DEBUG */
+ "%s: skipping LDAP-mapped entry for \"%s\"",
+ myself, NIL(le[i].le_name));
+ skipped++;
+ repl_stats.successes++;
+ 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
+ * merge the remove/add into a single
+ * LDAP update.
+ */
+ if (le[i].le_type == REM_IBASE &&
+ i < nument-1 &&
+ le[i+1].le_type == ADD_IBASE &&
+ sameEntryObj(&le[i], &le[i+1])) {
+ __nisdb_get_tsd()->doingModify = 1;
+ doingModify = 1;
+ }
+
+ nerr = apply_update(&(le[i]));
+
+ /*
+ * 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 &&
+ (le[i].le_type == ADD_IBASE ||
+ nerr != NIS_SUCCESS)) {
+ __nisdb_get_tsd()->doingModify = 0;
+ doingModify = 0;
+ }
+
+ if (nerr != NIS_SUCCESS) {
+ if (cons)
+ fprintf(cons,
+ "Failed to apply update.\n");
+ syslog(LOG_ERR,
+ "replica_update: Unable to apply update.");
+ repl_stats.errors++;
+ break;
+ }
+ if (cons)
+ fprintf(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)
+ fprintf(cons,
+ "Added update to trans.log.\n");
+ repl_stats.successes++;
+ }
+ }
+ if (repl_stats.errors == 0) {
+ if (cons)
+ fprintf(cons,
+ "Successfully updated.\n");
+ end_update();
+ dbStatus = nis_put_online(name, d_commit,
+ FALSE);
+ if (dbStatus != DB_SUCCESS) {
+ syslog(LOG_ERR,
+ "replica_update: online DB error %d for \"%s\"",
+ dbStatus, name);
+ }
+ nis_db_sync_log(); /* *.log */
+ sync_log(); /* trans.log */
+ end_transaction(xid);
+ if (curr_upd != 0)
+ ttime = curr_upd->lu_time;
+ else
+ ttime = 0;
+ if (!ttime && !skipped)
+ /*
+ * This should never happen, otherwise
+ * this can force the replica to go
+ * into full resync.
+ */
+ syslog(LOG_INFO,
+ "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'.
+ */
+ make_stamp(name, skipped ? xttime : ttime);
+ __nis_freelogresult(lres);
+ logmsg(MSG_NOTIMECHECK, LOG_INFO,
+ "%s: Setting update time for \"%s\" to %s",
+ myself, name,
+ ctime_r(skipped ? (time_t *)&xttime :
+ (time_t *)&ttime,
+ ctbuf, CTBUFSIZ));
+ unlockTransLog(myself, 1);
+ return (1);
+ } else {
+ if (cons)
+ fprintf(cons, "Aborted the update.\n");
+ abort_transaction(xid);
+ dbStatus = nis_put_online(name, d_rollback,
+ FALSE);
+ if (dbStatus != DB_SUCCESS) {
+ syslog(LOG_ERR,
+ "replica_update: online DB error %d for \"%s\"",
+ dbStatus, name);
+ }
+ nis_db_sync_log(); /* *.log */
+ sync_log(); /* trans.log */
+ __nis_freelogresult(lres);
+ unlockTransLog(myself, 1);
+ return (0);
+ }
+ }
+
+ if (lres->lr_status != NIS_RESYNC) {
+ syslog(LOG_WARNING,
+ "replica_update: nis_dumplog failed: srv='%s', dir='%s', err='%s'",
+ srv->name, name, nis_sperrno(lres->lr_status));
+ if (cons)
+ fprintf(cons,
+ "replica_update: nis_dumplog failed: srv='%s', dir='%s', err='%s'\n",
+ srv->name, name, nis_sperrno(lres->lr_status));
+ __nis_freelogresult(lres);
+ unlockTransLog(myself, 1);
+ return (0);
+ }
+
+ __nis_freelogresult(lres);
+ } /* end of delta update */
+
+ /*
+ * Our log and the masters are sufficently out of date that we
+ * need to completely resync.
+ */
+ if (verbose) {
+ if (cons)
+ fprintf(cons, "replica_update: Full dump required.\n");
+ syslog(LOG_INFO, "replica_update: Full dump required.");
+ }
+
+ /*
+ * 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.
+ */
+ syslog(LOG_INFO, "replica_update: Full dump of %s", name);
+
+ dbStatus = nis_put_offline(name, TRUE);
+ if (dbStatus != DB_SUCCESS) {
+ syslog(LOG_ERR,
+ "replica_update: offline DB error %d for \"%s\"",
+ dbStatus, name);
+ /* Haven't actually done anything, so return failure */
+ unlockTransLog(myself, 1);
+ 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.
+ */
+try_again:
+ __nis_init_hash_table(&repl_objs, 0);
+ __nis_destroy_callback();
+ /*
+ * 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.
+ */
+ lres = nis_dump(srv, name, update_directory);
+ if (lres) {
+ syslog(LOG_INFO, "replica_update: nis_dump result %s",
+ nis_sperrno(lres->lr_status));
+ syslog(LOG_INFO, "replica_update: %d updates, %d errors.",
+ repl_stats.successes, repl_stats.errors);
+ }
+ /*
+ * Free up the cache of table objects
+ */
+ LOCK_LIST(&repl_objs, "replica_update(repl_objs)");
+ for (pp = (ping_item *)(repl_objs.first); pp; pp = nxt) {
+ nxt = (ping_item *)(pp->item.nxt_item);
+ if (!checkpoint_table(pp->item.name)) {
+ /*
+ * 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->lr_status == NIS_CBRESULTS)
+ lres->lr_status = NIS_FAIL;
+ }
+ (void) nis_remove_item(pp->item.name, &repl_objs);
+ XFREE(pp->item.name);
+ nis_destroy_object(pp->obj);
+ XFREE(pp);
+ }
+ ULOCK_LIST(&repl_objs, "replica_update(repl_objs)");
+
+ if (lres == NULL || lres->lr_status == NIS_DUMPLATER) {
+ if (lres)
+ syslog(LOG_INFO,
+ "replica_update: master server busy, rescheduling the resync.");
+ if (ttime != 0L) {
+ ttime = 0;
+ make_stamp(name, ttime);
+ logmsg(MSG_NOTIMECHECK, LOG_INFO,
+ "%s: Setting update time for \"%s\" to 0x%lx",
+ myself, name, ttime);
+ }
+ /*
+ * Note, we are not addressing the case when several replicas
+ * are initialized simultaneously i.e several nismkdir -s
+ * are executed before a nisping.
+ */
+ dbStatus = nis_put_online(name, d_rollback, TRUE);
+ if (dbStatus != DB_SUCCESS) {
+ syslog(LOG_ERR,
+ "replica_update: online DB error %d for \"%s\"",
+ dbStatus, name);
+ }
+ __nis_freelogresult(lres);
+
+ if (__nis_retry_sleep(&dumpRetry,
+ dumpReturnUnsafe || (dbStatus != DB_SUCCESS))) {
+ syslog(LOG_INFO,
+ "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.
+ */
+ unlockTransLog(myself, 1);
+ return (0);
+ }
+ }
+
+ if (lres->lr_status == NIS_RPCERROR) { /* callback _setup_ failed */
+ if (ttime != 0L) {
+ ttime = 0;
+ make_stamp(name, ttime);
+ logmsg(MSG_NOTIMECHECK, LOG_INFO,
+ "%s: Setting update time for \"%s\" to 0x%lx",
+ myself, name, ttime);
+ }
+ /*
+ * Put back online, since the directory would not have been
+ * cleared.
+ */
+ dbStatus = nis_put_online(name, d_rollback, TRUE);
+ if (dbStatus != DB_SUCCESS) {
+ syslog(LOG_ERR,
+ "replica_update: online DB error %d for \"%s\"",
+ dbStatus, name);
+ }
+ __nis_freelogresult(lres);
+
+ if (__nis_retry_sleep(&dumpRetry,
+ dumpReturnUnsafe || (dbStatus != DB_SUCCESS))) {
+ syslog(LOG_INFO,
+ "replica_update: trying a full resync again");
+ goto try_again;
+ } else {
+ unlockTransLog(myself, 1);
+ return (0); /* don't delete from upd_list */
+ }
+
+ } else if (lres->lr_status == NIS_NOTFOUND) { /* No longer on master */
+ logmsg(MSG_NOTIMECHECK, LOG_INFO,
+ "%s: nis_dump failed: srv='%s', dir='%s', "
+ "No longer served by master", myself, srv->name, name);
+ if (ttime != 0L) {
+ ttime = 0;
+ make_stamp(name, ttime);
+ logmsg(MSG_NOTIMECHECK, LOG_INFO,
+ "%s: Setting update time for \"%s\" to 0x%lx",
+ myself, name, ttime);
+ }
+ /*
+ * Put back online, nothing more we can do
+ */
+ dbStatus = nis_put_online(name, d_rollback, TRUE);
+ if (dbStatus != DB_SUCCESS) {
+ logmsg(MSG_NOTIMECHECK, LOG_ERR,
+ "replica_update: online DB error %d for \"%s\"",
+ myself, dbStatus, name);
+ }
+ __nis_freelogresult(lres);
+ unlockTransLog(myself, 1);
+ return (1); /* do delete from upd_list */
+
+ } else if (lres->lr_status != NIS_CBRESULTS) { /* CB itself failed */
+ syslog(LOG_WARNING,
+ "replica_update: nis_dump failed: srv='%s', dir='%s', err='%s'",
+ srv->name, name, nis_sperrno(lres->lr_status));
+ if (ttime != 0L) {
+ ttime = 0;
+ make_stamp(name, ttime);
+ logmsg(MSG_NOTIMECHECK, LOG_INFO,
+ "%s: Setting update time for \"%s\" to 0x%lx",
+ myself, name, ttime);
+ }
+ __nis_freelogresult(lres);
+
+ if (ldapConfig.dumpError == rollback) {
+ dbStatus = nis_put_online(name, d_rollback, TRUE);
+ if (dbStatus != DB_SUCCESS) {
+ syslog(LOG_ERR,
+ "replica_update: online DB error %d for \"%s\"",
+ dbStatus, name);
+ }
+ }
+
+ dumpReturnUnsafe = TRUE;
+
+ /*
+ * 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))) {
+ syslog(LOG_WARNING,
+ "replica_update: Used %d dump attempts for \"%s\", serving stale data",
+ ldapConfig.dumpErrorTimeout.attempts, name);
+ unlockTransLog(myself, 1);
+ return (0);
+ }
+
+ syslog(LOG_INFO, "replica_update: trying a full resync again.");
+
+ if (ldapConfig.dumpError == rollback &&
+ dbStatus == DB_SUCCESS) {
+ dbStatus = nis_put_offline(name, TRUE);
+ if (dbStatus != DB_SUCCESS) {
+ syslog(LOG_ERR,
+ "replica_update: offline DB error %d for \"%s\"",
+ dbStatus, name);
+ }
+ }
+
+ goto try_again;
+ } else if (repl_stats.errors) {
+ syslog(LOG_WARNING,
+ "replica_update: errors during resync : srv='%s', dir='%s'",
+ srv->name, name);
+ xttime = 0;
+ __nis_freelogresult(lres);
+ if (ttime != 0L) {
+ ttime = 0;
+ make_stamp(name, ttime);
+ logmsg(MSG_NOTIMECHECK, LOG_INFO,
+ "%s: Setting update time for \"%s\" to 0x%lx",
+ myself, name, ttime);
+ }
+
+ if (ldapConfig.dumpError == rollback) {
+ dbStatus = nis_put_online(name, d_rollback, TRUE);
+ if (dbStatus != DB_SUCCESS) {
+ syslog(LOG_ERR,
+ "replica_update: online DB error %d for \"%s\"",
+ dbStatus, name);
+ }
+ }
+
+ dumpReturnUnsafe = TRUE;
+
+ /*
+ * 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))) {
+ syslog(LOG_WARNING,
+ "replica_update: Used %d dump attempts for \"%s\", serving stale data",
+ ldapConfig.dumpErrorTimeout.attempts, name);
+ unlockTransLog(myself, 1);
+ return (0);
+ }
+
+ syslog(LOG_INFO, "replica_update: trying a full resync again.");
+
+ if (ldapConfig.dumpError == rollback &&
+ dbStatus == DB_SUCCESS) {
+ dbStatus = nis_put_offline(name, TRUE);
+ if (dbStatus != DB_SUCCESS) {
+ syslog(LOG_ERR,
+ "replica_update: offline DB error %d for \"%s\"",
+ dbStatus, name);
+ }
+ }
+
+ 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.
+ */
+ if (lres->lr_entries.lr_entries_len) {
+ xttime = lres->lr_entries.lr_entries_val[0].le_time;
+ } else {
+ syslog(LOG_INFO,
+"replica_update: downrev version of NIS+ service serving dir %s as master.",
+ name);
+ xttime = nis_cptime_msg(srv, name, FALSE, TRUE);
+ xttime = (xttime < repl_stats.utime) ?
+ repl_stats.utime : xttime;
+ }
+ if (cons)
+ fprintf(cons, "Latest update was %s",
+ ctime_r((time_t *)&repl_stats.utime, ctbuf,
+ CTBUFSIZ));
+ if (verbose)
+ syslog(LOG_INFO,
+ "replica_update: directory %s updated", name);
+
+ /* Add it to the list of directories that it serves */
+ nis_server_control(SERVING_LIST, DIR_ADD, name);
+ __nis_freelogresult(lres);
+ dbStatus = nis_put_online(name, d_commit, TRUE);
+ if (dbStatus != NIS_SUCCESS) {
+ syslog(LOG_ERR,
+ "replica_update: online DB error %d for \"%s\"",
+ dbStatus, name);
+ }
+
+ /*
+ * 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.
+ */
+ if ((xttime != 0L) || ((xttime == 0L) && (ttime != 0L))) {
+ /*
+ * 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.
+ */
+ make_stamp(name, xttime);
+ logmsg(MSG_NOTIMECHECK, LOG_INFO,
+ "%s: Setting update time for \"%s\" after "
+ "full resync to %s",
+ myself, name,
+ ctime_r((time_t *)&xttime, ctbuf, CTBUFSIZ));
+ }
+ if (xttime == 0L)
+ syslog(LOG_DEBUG,
+ "replica_update: timestamp=0 after full resync completed!");
+ unlockTransLog(myself, 1);
+ return (1);
+}
+
+static void
+ping_replicas_thread(t_pung)
+ ping_item *t_pung;
+{
+
+ if (verbose)
+ syslog(LOG_INFO, "ping_replicas: Pinging %s",
+ t_pung->item.name);
+
+ nis_ping(t_pung->item.name, t_pung->mtime, t_pung->obj);
+
+ if (t_pung->obj)
+ nis_destroy_object(t_pung->obj);
+
+ if (verbose)
+ syslog(LOG_INFO, "ping_replicas_thread: directory %s time %ld",
+ t_pung->item.name, t_pung->mtime);
+
+ XFREE(t_pung->item.name);
+ XFREE(t_pung);
+
+ 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
+ping_replicas(pung)
+ ping_item *pung;
+{
+ thread_t tid;
+ sigset_t new_mask, orig_mask;
+ int error;
+
+ if (sigemptyset(&new_mask) != 0) {
+ if (verbose)
+ syslog(LOG_ERR,
+ "ping_replicas: Error (%d) zeroing mask for %s",
+ errno, pung->item.name);
+ return (FALSE);
+ }
+ if (thr_sigsetmask(SIG_SETMASK, &new_mask, &orig_mask) != 0) {
+ if (verbose)
+ syslog(LOG_ERR,
+ "ping_replicas: Error (%d) setting mask for %s",
+ errno, pung->item.name);
+ return (FALSE);
+ }
+ error = thr_create(NULL, 0,
+ (void *(*)(void *))ping_replicas_thread, (void *)pung,
+ THR_DETACHED, &tid);
+ if (thr_sigsetmask(SIG_SETMASK, &orig_mask, NULL) != 0) {
+ if (verbose)
+ syslog(LOG_ERR,
+ "ping_replicas: Error (%d) restoring mask for %s",
+ errno, pung->item.name);
+ }
+ if (error) {
+ if (verbose)
+ syslog(LOG_ERR,
+ "ping_replicas: Error (%d) creating ping thread for %s",
+ error, pung->item.name);
+ return (FALSE);
+ }
+ if (tid) {
+ if (verbose)
+ syslog(LOG_INFO,
+ "ping_replicas: Created ping thread %d for %s",
+ tid, pung->item.name);
+ 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.
+ */
+nis_result *
+__nis_local_lookup(req, flags, list_op, cbdata, cback)
+ ib_request *req; /* name parameters */
+ u_long flags; /* user flags */
+ int list_op; /* list semantics */
+ void *cbdata; /* Callback data */
+ int (*cback)(); /* Callback (for list calls) */
+{
+ ns_request nsr;
+ nis_result *res;
+ nis_result *local_res;
+ int i;
+
+ if (verbose)
+ syslog(LOG_INFO, "nis_local_lookup: making local call.");
+
+ res = XCALLOC(1, sizeof (nis_result));
+ if (res == NULL)
+ return (NULL);
+
+ /*
+ * Depending on name or list_op either
+ * list it or look it up in the namespace.
+ */
+ if ((req->ibr_srch.ibr_srch_len > 0) || (list_op)) {
+ if (verbose)
+ syslog(LOG_INFO, "local LIST");
+ local_res = nis_iblist_svc(req, NULL);
+ } else {
+ char *name = req->ibr_name;
+ size_t namelen = 0;
+
+ if (name)
+ namelen = strlen(name);
+
+ /* sanity check on object name */
+ if (namelen == 0 || name[namelen-1] != '.') {
+ if (verbose)
+ syslog(LOG_INFO,
+ "nis_local_lookup: bad name '%s'",
+ namelen ? name : "<NULL or empty>");
+ res->status = NIS_BADNAME;
+ return (res);
+ }
+
+ if (verbose)
+ syslog(LOG_INFO, "local LOOKUP");
+ nsr.ns_name = req->ibr_name;
+ nsr.ns_object.ns_object_len = 0;
+ nsr.ns_object.ns_object_val = NULL;
+ local_res = nis_lookup_svc(&nsr, NULL);
+ }
+
+ /*
+ * Now duplicate the result for the client so that the
+ * client can call nis_freeresult() with impunity.
+ */
+ *res = *local_res;
+ if (res->objects.objects_len) {
+ res->objects.objects_val = (nis_object *)
+ calloc(res->objects.objects_len,
+ sizeof (nis_object));
+ for (i = 0; i < res->objects.objects_len; i++) {
+ (void) nis_clone_object(
+ local_res->objects.objects_val+i,
+ res->objects.objects_val+i);
+ }
+ }
+ if (res->cookie.n_len) {
+ res->cookie.n_bytes = (char *)
+ malloc(res->cookie.n_len);
+ memcpy(res->cookie.n_bytes, local_res->cookie.n_bytes,
+ local_res->cookie.n_len);
+ }
+ return (res);
+}
+
+/*
+ * we_serve()
+ *
+ * return TRUE if an RPC call to this directory might call us
+ * back.
+ */
+int
+we_serve(srv, flags)
+ directory_obj *srv;
+ u_long flags;
+{
+ nis_server *servers;
+ int ns, i;
+
+ if (flags & MASTER_ONLY)
+ ns = 1;
+ else
+ ns = srv->do_servers.do_servers_len;
+ servers = srv->do_servers.do_servers_val;
+ /*
+ * 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++)
+ if (nis_dir_cmp(servers[i].name, nis_local_host())
+ == 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.
+ */
+nis_result *
+__nis_core_lookup(req, flags, list_op, cbdata, cback)
+ ib_request *req; /* name parameters */
+ u_long flags; /* user flags */
+ int list_op; /* list semantics */
+ void *cbdata; /* Callback data */
+ int (*cback)(); /* Callback (for list calls) */
+{
+ int parent_first;
+ nis_error err;
+ nis_result *res = NULL;
+ directory_obj *dir;
+ nis_bound_directory *binding;
+
+ if (verbose)
+ syslog(LOG_INFO, "__nis_core_lookup: (safe) called on %s",
+ req->ibr_name);
+ /*
+ * AS THE SERVICE, we don't support callbacks
+ */
+ if (cbdata || cback) {
+ return (nis_make_error(NIS_NOCALLBACK, 0, 0, 0, 0));
+ }
+
+ if (list_op && req->ibr_srch.ibr_srch_len == 0)
+ parent_first = 0;
+ else
+ parent_first = 1;
+
+ flags &= ~HARD_LOOKUP; /* server doesn't do HARD_LOOKUP */
+ err = nis_bind_dir(req->ibr_name, parent_first, &binding, flags);
+ if (err != NIS_SUCCESS) {
+ return (nis_make_error(err, 0, 0, 0, 0));
+ }
+
+ dir = &binding->dobj;
+
+ /*
+ * 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.
+ */
+ if ((nis_dir_cmp(dir->do_name, __nis_rpc_domain()) == SAME_NAME) &&
+ ! root_server && we_serve(dir, flags)) {
+ flags |= MASTER_ONLY;
+ if (we_serve(dir, flags)) {
+ nis_free_binding(binding);
+ 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.
+ */
+ if (we_serve(dir, flags)) {
+ res = __nis_local_lookup(req, flags, list_op, cbdata, cback);
+ if (res && res->status == NIS_NOSUCHTABLE &&
+ !we_serve(dir, MASTER_ONLY)) {
+ /*
+ * 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.
+ */
+ nis_freeresult(res);
+ res = __nis_remote_lookup(req, flags | MASTER_ONLY,
+ list_op, cbdata, cback);
+ }
+ } else {
+ res = __nis_remote_lookup(req, flags, list_op, cbdata, cback);
+ }
+
+ nis_free_binding(binding);
+
+ 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.
+ */
+fd_result *
+__nis_finddirectory(binding, name)
+ nis_bound_directory **binding;
+ nis_name name;
+{
+ directory_obj *slist;
+ fd_result *res;
+ fd_result *svc_res;
+ fd_args argp;
+ int i, ns;
+ nis_server *srvlist;
+ enum name_pos p;
+
+ slist = &(*binding)->dobj;
+ if (verbose)
+ syslog(LOG_INFO, "nis_finddirectory (safe) : called.");
+ memset((char *)&argp, 0, sizeof (argp));
+ res = (fd_result *) XCALLOC(1, sizeof (fd_result));
+ argp.dir_name = name;
+ argp.requester = nis_local_host();
+ p = nis_dir_cmp(name, __nis_rpc_domain());
+
+ if (root_server && p == HIGHER_NAME) {
+ res->status = NIS_NOTFOUND;
+ return (res);
+ }
+
+ ns = slist->do_servers.do_servers_len;
+ /*
+ * special case the root replica servers.
+ */
+ if (we_serve(slist, 0) && (p != LOWER_NAME) && ! root_server)
+ ns = 1; /* effectively "master only" */
+
+ srvlist = slist->do_servers.do_servers_val;
+
+ /*
+ * 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++) {
+ if (nis_dir_cmp(srvlist[i].name, argp.requester) == SAME_NAME) {
+ /* Make sure we've got a database for it. */
+ nis_error tab_status = db_find_table(slist->do_name);
+ if (tab_status == NIS_NOSUCHTABLE) {
+ if (we_serve(slist, MASTER_ONLY))
+ tab_status = NIS_SYSTEMERROR;
+ else
+ tab_status = NIS_NOT_ME;
+ }
+
+ switch (tab_status) {
+ case NIS_SUCCESS:
+ svc_res = nis_finddirectory_svc(&argp, NULL);
+ /*
+ * Note, we "clone" the find directory result
+ * because the cache client code will free it.
+ */
+ if (svc_res && dup_fdres(svc_res, res)) {
+ return (res);
+ }
+ return (NULL);
+
+ case NIS_NOT_ME:
+ /* not master */
+ free((void *)res);
+ goto try_srvlist;
+
+ default:
+ /* _fd_res puts returned obj on rags list */
+ svc_res = __fd_res(argp.requester, tab_status,
+ NULL);
+ /* clone fd_result for cache to free */
+ if (svc_res && dup_fdres(svc_res, res))
+ return (res);
+ return (NULL);
+ }
+ }
+ }
+
+ /* Make an RPC call to locate the directory. */
+
+try_srvlist:
+ res = __nis_finddirectory_remote(binding, name);
+ return (res);
+}
+
+int
+__nis_host_is_server(nis_server *srv, int nsrv)
+{
+ int i;
+ char *name = nis_local_host();
+
+ for (i = 0; i < nsrv; i++) {
+ if (nis_dir_cmp(srv[i].name, name) == SAME_NAME)
+ return (1);
+ }
+ return (0);
+}
+
+fd_result *
+dup_fdres(o, n)
+ fd_result *o; /* The result to duplicate */
+ fd_result *n; /* Where to put the data */
+{
+
+#ifdef DEBUG
+ if (verbose)
+ syslog(LOG_INFO, "dup_fdres: cloning the FD result.");
+#endif
+
+ *n = *o;
+
+ if (o->source)
+ n->source = strdup(o->source);
+
+ if (o->dir_data.dir_data_val) {
+ n->dir_data.dir_data_val = (char *)
+ malloc(o->dir_data.dir_data_len);
+ if (! n->dir_data.dir_data_val) {
+ syslog(LOG_ERR, "dup_fdres: Out of memory!");
+ return (NULL);
+ }
+ memcpy(n->dir_data.dir_data_val, o->dir_data.dir_data_val,
+ o->dir_data.dir_data_len);
+ }
+ if (o->signature.signature_val) {
+ n->signature.signature_val = (char *)
+ malloc(o->signature.signature_len);
+ if (! n->signature.signature_val) {
+ syslog(LOG_ERR, "dup_fdres: Out of memory!");
+ if (n->dir_data.dir_data_val)
+ free(n->dir_data.dir_data_val);
+ return (NULL);
+ }
+ memcpy(n->signature.signature_val, o->signature.signature_val,
+ o->signature.signature_len);
+ }
+ return (n);
+}
+
+DECLRWLOCK(root_object);
+
+int
+update_root_object(nis_name root_dir, nis_object *d_obj)
+{
+ WLOCK(root_object);
+ if (nis_write_obj(nis_data(ROOT_OBJ), d_obj)) {
+ writeColdStartFile(&(d_obj->DI_data));
+ root_server = 1;
+ WULOCK(root_object);
+ __nis_CacheRestart();
+ flush_local_dircache(root_dir);
+ return (1);
+ } else {
+ WULOCK(root_object);
+ return (0);
+ }
+}
+
+nis_object*
+get_root_object()
+{
+ /*
+ * XXX Maybe this can be cached and updated whenever
+ * update_root_object is called instead of being reread ???
+ */
+ nis_object* d_obj;
+
+ RLOCK(root_object);
+ d_obj = nis_read_obj(nis_data(ROOT_OBJ));
+ RULOCK(root_object);
+ return (d_obj);
+}
+
+int
+root_object_p(nis_name name)
+{
+ return (strcmp(name, ROOT_OBJ) == 0);
+}
+
+int
+remove_root_object(nis_name root_dir, nis_object* d_obj)
+{
+ WLOCK(root_object);
+ unlink(nis_data(ROOT_OBJ));
+ writeColdStartFile(&(d_obj->DI_data));
+ root_server = 0;
+ WULOCK(root_object);
+ __nis_CacheRestart();
+ flush_local_dircache(root_dir);
+ return (1);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_thread_funcs.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_thread_funcs.c
new file mode 100644
index 0000000000..1b42c20009
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_thread_funcs.c
@@ -0,0 +1,619 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <syslog.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <dirent.h>
+#include <string.h>
+#include <rpcsvc/nis.h>
+#include "nis_proc.h"
+#include "nis_svc.h"
+
+uint_t next_refresh; /* next time to re-load dot file */
+
+/* Track last time we started processing a request */
+static struct timeval last_activity = {0, 0};
+
+/* Max interval between checking number of open fd:s */
+#define FD_CHECK_DEF (5 * 60) /* Five minutes */
+
+/*
+ * Purge since now minus delta depending on number of open fd:s, in seconds.
+ *
+ * The array values were chosen to do the following:
+ *
+ * Behave like the old code (7200 seconds purge delta) for a very small
+ * number of open fd:s
+ *
+ * Give reasonably smooth purge behavior for realistic fd growth rates,
+ * as observed at customer site.
+ *
+ * Become aggressive about purging at high numbers of open fd:s, to
+ * reduce the risk of running out of fd:s.
+ */
+static int purge_deltas[] = {
+ 7200, 6900, 6600, 6300, 6000, 5400, 4800, 4200,
+ 3600, 3000, 1800, 1200, 600, 300, 75, 60
+};
+
+
+/* ARGSUSED1 */
+static int
+do_count(void *countp, int fd) {
+ (*(int *)countp)++;
+ return (0);
+}
+
+static long
+fd_open_count(void) {
+ int count = 0;
+
+ (void) fdwalk(do_count, &count);
+ return (count);
+}
+
+/* servloop_sleep and servloop_wake are protected by the same mutex */
+static pthread_cond_t servloop_sleep = PTHREAD_COND_INITIALIZER;
+static time_t servloop_wake = 0;
+static DECLMUTEXLOCK(servloop_sleep);
+
+int
+msleep(ulong_t sleeptime) {
+ struct timespec ts;
+ struct timeval tv;
+ int ret;
+
+ (void) gettimeofday(&tv, 0);
+ ts.tv_sec = tv.tv_sec + sleeptime/1000;
+ ts.tv_nsec = 1000*(tv.tv_usec+1000*(sleeptime%1000));
+ if (ts.tv_nsec >= 1000000000) {
+ ts.tv_nsec -= 1000000000;
+ ts.tv_sec += 1;
+ }
+
+ MUTEXLOCK(servloop_sleep, "servloop:msleep lock");
+ if (servloop_wake != 0) {
+ /* If we're past the servloop_wake time, reset and return */
+ if (servloop_wake <= tv.tv_sec) {
+ servloop_wake = 0;
+ MUTEXUNLOCK(servloop_sleep, "servloop:msleep unlock");
+ return (0);
+ }
+ /* servloop_wake may limit our sleep */
+ if (servloop_wake <= ts.tv_sec) {
+ ts.tv_sec = servloop_wake;
+ ts.tv_nsec = 0;
+ }
+ }
+ ret = pthread_cond_timedwait(&servloop_sleep,
+ &servloop_sleep_pmutex, &ts);
+ /* Reset servloop_wake if necessary */
+ if (servloop_wake != 0) {
+ (void) gettimeofday(&tv, 0);
+ if (servloop_wake <= tv.tv_sec)
+ servloop_wake = 0;
+ }
+ MUTEXUNLOCK(servloop_sleep, "servloop:msleep unlock");
+
+ return (ret);
+}
+
+void
+wakeup_servloop(void) {
+ (void) pthread_cond_signal(&servloop_sleep);
+}
+
+/*
+ * Return the update batching timeout.
+ */
+time_t
+updateBatchingTimeout(void) {
+
+ switch (ldapConfig.updateBatching) {
+ case upd_none:
+ return (0);
+ case accumulate:
+ case bounded_accumulate:
+ return (ldapConfig.updateBatchingTimeout.timeout);
+ default:
+ return (DIR_IDLE_TIME);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Establish the time when the servloop next should wake up to check
+ * if replicas should be pinged. If we change the wake up time, we
+ * always wake up the servloop so that it will take the new wakeup time
+ * into account when it goes back to sleep.
+ */
+void
+setPingWakeup(time_t when) {
+ int doWakeup = 0;
+
+ MUTEXLOCK(servloop_sleep, "servloop:set_ping_wakeup lock");
+ if (when < servloop_wake || servloop_wake == 0) {
+ servloop_wake = when;
+ doWakeup = 1;
+ }
+ MUTEXUNLOCK(servloop_sleep, "servloop:set_ping_wakeup unlock");
+
+ if (doWakeup)
+ wakeup_servloop();
+}
+
+void
+mark_activity(void) {
+
+ static DECLMUTEXLOCK(last_activity);
+
+ MUTEXLOCK(last_activity, "mark_activity(last_activity)");
+ (void) gettimeofday(&last_activity, 0);
+ MUTEXUNLOCK(last_activity, "mark_activity(last_activity)");
+}
+
+void *
+servloop(void *varg) {
+
+ long dtbsize;
+ long last_purge = 0;
+ long fd_check = 0;
+ long fd_check_interval = FD_CHECK_DEF;
+ long purge_delta = purge_deltas[
+ (sizeof (purge_deltas)/sizeof (purge_deltas[0])) - 1];
+ long prev_since;
+ long cur_open_fd;
+ struct rlimit rl;
+ struct timeval now;
+ ulong_t sleeptime = 120*1000;
+
+ /*
+ * get the maximum number of file descriptors for poll
+ */
+ getrlimit(RLIMIT_NOFILE, &rl);
+ dtbsize = rl.rlim_cur;
+
+ gettimeofday(&now, NULL);
+ last_purge = prev_since = now.tv_sec;
+
+ for (;;) {
+
+ /*
+ * Check open fd:s if more than 'fd_check_interval' seconds
+ * since last time.
+ *
+ * XXX: The somewhat complicated code below sets the
+ * purge_delta depending on the number of open fd:s,
+ * using linear interpolation in the purge_deltas[]
+ * array.
+ */
+ gettimeofday(&now, NULL);
+ if (now.tv_sec - fd_check >= fd_check_interval) {
+ long index;
+ int deltas = (sizeof (purge_deltas)/
+ sizeof (purge_deltas[0]));
+ cur_open_fd = fd_open_count();
+ fd_check = now.tv_sec;
+ index = (cur_open_fd-1) * deltas / dtbsize;
+ if (index < 0) {
+ purge_delta = purge_deltas[0];
+ } else if (index < deltas-1) {
+ /* Interpolate */
+ purge_delta = purge_deltas[index] +
+ ((cur_open_fd - index*(dtbsize/deltas)) *
+ (purge_deltas[index+1]-purge_deltas[index])) /
+ (dtbsize/deltas);
+ } else {
+ purge_delta = purge_deltas[deltas-1];
+ }
+ fd_check_interval = purge_delta;
+ if (fd_check_interval > FD_CHECK_DEF)
+ fd_check_interval = FD_CHECK_DEF;
+ /*
+ * Run a purge on virtual circuits unused since now
+ * minus purge_delta seconds, if
+ *
+ * The new purge cutoff would be more recent than
+ * the one used in the last purge, and
+ *
+ * We have a fairly large number of fd:s open
+ * (more than 128, if deltas=16 and dtbsize=1024),
+ *
+ * or
+ *
+ * More than 'purge_delta' seconds have passed
+ * since the last purge.
+ */
+ if (now.tv_sec - purge_delta > prev_since &&
+ (index > 1 || now.tv_sec - last_purge >= purge_delta)) {
+ __svc_nisplus_purge_since(
+ now.tv_sec-purge_delta);
+ prev_since = now.tv_sec - purge_delta;
+ last_purge = now.tv_sec;
+ }
+ }
+
+ gettimeofday(&now, NULL);
+ if (now.tv_sec > next_refresh) {
+ next_refresh = __nis_serverRefreshCache();
+ syslog(LOG_DEBUG, "nis_main: next_refresh %u",
+ next_refresh);
+ }
+
+ /* Perform deferred frees */
+ __nis_thread_cleanup(__nis_get_tsd());
+
+ (void) msleep(sleeptime);
+ if ((last_activity.tv_sec - now.tv_sec) >= sleeptime/1000) {
+ /*
+ * No activity while we slept. Remove tables from
+ * standby mode and go back to sleep.
+ */
+ (void) db_standby(0);
+ }
+ check_updaters();
+ check_pingers();
+ /* the force_checkpoint flag is set by -F or ping -C */
+ if (force_checkpoint) {
+ force_checkpoint = FALSE;
+ /*
+ * Give the local databases a chance to
+ * checkpoint their data.
+ */
+ if (verbose)
+ syslog(LOG_INFO,
+ "Service Checkpoint...");
+ checkpoint_db();
+ if (checkpoint_log()) {
+ if (verbose)
+ syslog(LOG_INFO,
+ "checkpoint succeeded.");
+ need_checkpoint = FALSE;
+ } else if (verbose)
+ syslog(LOG_INFO, "checkpoint failed.");
+ }
+ }
+ /* Not reached */
+}
+
+#define NO_ENTRY_OBJS 96
+
+void *
+callback_thread(void *varg) {
+
+ callback_thread_arg_t *arg = (callback_thread_arg_t *)varg;
+ netobj cookie;
+ int cbres, queued;
+ table_col *tc;
+ nis_object *cbarray[NO_ENTRY_OBJS];
+ nis_object *d_obj;
+ enum clnt_stat status = RPC_SUCCESS;
+ struct timeval tv = {3600, 0};
+ char tblbuf[NIS_MAXNAMELEN * 2];
+ char *table;
+ nis_error result;
+ int i;
+ pthread_t myself = pthread_self();
+
+ (void) nis_add_callback_id(myself, arg->pname);
+
+ arg->cbarg.entries.entries_val = &(cbarray[0]);
+ cookie = arg->fnr->cookie;
+ cbres = 0;
+ queued = 0;
+ if (__type_of(arg->ib_obj) == NIS_TABLE_OBJ)
+ tc = arg->ib_obj->TA_data.ta_cols.ta_cols_val;
+ else
+ tc = tbl_prototype.ta_cols.ta_cols_val;
+
+ while ((arg->fnr->status == NIS_SUCCESS) && (!cbres)) {
+ if (arg->nm && multival_filter(arg->ib_obj,
+ arg->nm, arg->a + arg->na, arg->fnr->obj)) {
+ nis_destroy_object(arg->fnr->obj);
+ } else if (arg->all_read ||
+ __can_do(NIS_READ_ACC, arg->fnr->obj->zo_access,
+ arg->fnr->obj, arg->pname)) {
+ cbarray[queued] = arg->fnr->obj;
+ queued++;
+ } else {
+ d_obj = nis_censor_object_attr(arg->fnr->obj, tc,
+ arg->pname, arg->na + arg->nm, arg->a);
+ if (d_obj) {
+ cbarray[queued] = d_obj;
+ queued++;
+ }
+ nis_destroy_object(arg->fnr->obj);
+ }
+ /*
+ * the object is either already assigned to
+ * cbarray or destroyed.
+ */
+ arg->fnr->obj = 0;
+ if (queued == NO_ENTRY_OBJS) {
+ arg->cbarg.entries.entries_len = NO_ENTRY_OBJS;
+ status = clnt_call(arg->cback, CBPROC_RECEIVE,
+ xdr_cback_data, (char *)&arg->cbarg,
+ xdr_bool, (char *)&cbres, tv);
+ if (verbose)
+ syslog(LOG_INFO,
+ "list: sent entry, status = %s",
+ clnt_sperrno(status));
+ for (i = 0; i < NO_ENTRY_OBJS; i++) {
+ nis_destroy_object(cbarray[i]);
+ cbarray[i] = NULL;
+ }
+ queued = 0;
+ if ((status != RPC_SUCCESS) || cbres) {
+ if ((cbres == 0) && verbose) {
+ syslog(LOG_ERR,
+ "nis_list_svc: callback to %s returned %s",
+ arg->cbhostname, clnt_sperrno(status));
+ }
+ break;
+ }
+ }
+ XFREE(arg->fnr);
+ table = internal_table_name(arg->ibr_name, tblbuf);
+ arg->fnr = db_nextib(arg->ibr_name, &cookie,
+ FN_MANGLE+FN_NORAGS, table);
+ /*
+ * Note: that the db_next_entry function will
+ * free this data. Technically this is an error
+ * but it is convienient.
+ */
+ cookie = arg->fnr->cookie;
+ }
+ if (queued) {
+ arg->cbarg.entries.entries_len = queued;
+ status = clnt_call(arg->cback, CBPROC_RECEIVE,
+ xdr_cback_data, (char *)&arg->cbarg,
+ xdr_bool, (char *)&cbres, tv);
+ if ((status != RPC_SUCCESS) && verbose)
+ syslog(LOG_ERR,
+ "nis_list_svc: callback to %s returned %s",
+ arg->cbhostname, clnt_sperrno(status));
+ for (i = 0; i < queued; i++)
+ nis_destroy_object(cbarray[i]);
+ }
+ tv.tv_sec = 10;
+ tv.tv_usec = 0;
+ if (status != RPC_SUCCESS) {
+ if (status == RPC_AUTHERROR) {
+ syslog(LOG_WARNING,
+ "Authentication ERROR in talking to %s.",
+ arg->cbhostname);
+ auth_destroy(arg->cback->cl_auth);
+ arg->cback->cl_auth = authnone_create();
+ result = NIS_CLNTAUTH;
+ } else {
+ syslog(LOG_WARNING,
+ "RPC ERROR in talking to %s.",
+ arg->cbhostname);
+ result = NIS_RPCERROR;
+ }
+ (void) clnt_call(arg->cback, CBPROC_ERROR,
+ xdr_nis_error, (char *)&result,
+ xdr_void, (char *)(0), tv);
+ } else {
+ (void) clnt_call(arg->cback, CBPROC_FINISH,
+ xdr_void, (char *)(0),
+ xdr_void, (char *)(0), tv);
+ }
+ if (arg->fnr->status == NIS_SUCCESS) {
+ if (arg->fnr->obj)
+ nis_destroy_object(arg->fnr->obj);
+ db_flush(arg->ibr_name, &(arg->fnr->cookie));
+ }
+ if (arg->ib_obj != 0)
+ nis_destroy_object(arg->ib_obj);
+ XFREE(arg->fnr);
+ auth_destroy(arg->cback->cl_auth);
+ clnt_destroy(arg->cback);
+ /* Our parent thread allocated this, but it's our job to deallocate */
+ free(varg);
+
+ nis_delete_callback_id(myself);
+
+ /*
+ * The periodic thread cleanup (or the TSD destructor, if all else
+ * fails) will take care of the loose ends
+ */
+ return (0);
+}
+
+/* From nis_xx_proc.c */
+#define CB_BUF_SIZE 128
+
+/*
+ * Perform the actual dump work on behalf of nis_dump_svc().
+ */
+void *
+dumpsvc_thread(void *varg) {
+
+ dumpsvc_thread_arg_t *arg = varg;
+ __nis_hash_table_mt tables = NIS_HASH_TABLE_MT_INIT;
+ __nis_hash_item_mt *item;
+ int i, queued_objs;
+ nis_fn_result *fnr;
+ netobj cookie;
+ int cbres = 0;
+ char namebuf[1024];
+ int totlobjs = 0;
+ int skipobjs = 0;
+ nis_object *cbarray[CB_BUF_SIZE];
+ struct timeval tv, go;
+ long boost = 0;
+ cback_data cbarg;
+ enum clnt_stat rstat;
+ pthread_t myself = pthread_self();
+
+ if (arg == 0)
+ return (0);
+
+ (void) nis_add_callback_id(myself, arg->pname);
+
+ syslog(LOG_INFO, "nis_dump_svc[%d]: sending full dump of %s to %s",
+ pthread_self(), arg->da_dir, arg->pname);
+
+ cbarg.entries.entries_val = cbarray;
+ nis_insert_name(arg->da_dir, &tables);
+ queued_objs = 0;
+
+ while ((item = __nis_pop_item_mt(&tables)) != 0) {
+ strcpy(arg->pname, item->name);
+ if (verbose)
+ syslog(LOG_INFO, "nis_dump_svc (child) : Dumping '%s'",
+ arg->pname);
+ fnr = db_firstib(arg->pname, 0, NULL, FN_NOMANGLE+FN_NORAGS,
+ NULL);
+ cookie = fnr->cookie;
+ if (fnr->status == NIS_NOSUCHTABLE) {
+ syslog(LOG_INFO,
+ "nis_dump_svc (child): Couldn't read table '%s'",
+ arg->pname);
+ } else if (fnr->status == NIS_SUCCESS) {
+ while (! cbres) {
+ if (__type_of(fnr->obj) == NIS_TABLE_OBJ) {
+ sprintf(namebuf, "%s.%s",
+ fnr->obj->zo_name,
+ fnr->obj->zo_domain);
+ nis_insert_name(namebuf, &tables);
+ }
+ if (fnr->obj->zo_oid.mtime > arg->ttime) {
+ skipobjs++;
+ } else {
+ cbarray[queued_objs++] = fnr->obj;
+ totlobjs++;
+ }
+ /*
+ * This won't be true should we skip
+ * recent updates but let's test for
+ * it anyway.
+ */
+ if (queued_objs == CB_BUF_SIZE) {
+ cbarg.entries.entries_len =
+ CB_BUF_SIZE;
+ tv.tv_sec = 600 > boost ? 600: boost;
+ tv.tv_usec = 0;
+ gettimeofday(&go, NULL);
+ rstat = clnt_call(arg->cback,
+ CBPROC_RECEIVE, xdr_cback_data,
+ (char *)&cbarg, xdr_bool,
+ (char *)&cbres, tv);
+ gettimeofday(&tv, NULL);
+ for (i = 0; i < CB_BUF_SIZE; i++)
+ nis_destroy_object(cbarray[i]);
+
+ if (rstat != RPC_SUCCESS) {
+ syslog(LOG_ERR,
+ "nis_dump_svc (child): Callback failed with RPC error %s.",
+ clnt_sperrno(rstat));
+ auth_destroy(
+ arg->cback->cl_auth);
+ clnt_destroy(arg->cback);
+ free(varg);
+ nis_delete_callback_id(myself);
+ return (0);
+ }
+ queued_objs = 0;
+ boost = 3 * (tv.tv_sec - go.tv_sec);
+ }
+ XFREE(fnr);
+ fnr = db_nextib(arg->pname, &cookie,
+ FN_NORAGS, NULL);
+ cookie = fnr->cookie;
+ if (fnr->status != NIS_SUCCESS)
+ break;
+ }
+ }
+ if (fnr->status == NIS_SUCCESS)
+ XFREE(fnr->cookie.n_bytes);
+ XFREE(fnr);
+ if (queued_objs) {
+ cbarg.entries.entries_len = queued_objs;
+ if (! cbres) {
+ tv.tv_sec = 600 > boost ? 600 : boost;
+ tv.tv_usec = 0;
+ rstat = clnt_call(arg->cback, CBPROC_RECEIVE,
+ xdr_cback_data, (char *)&cbarg,
+ xdr_bool, (char *)&cbres, tv);
+ if (rstat != RPC_SUCCESS) {
+ syslog(LOG_ERR,
+ "nis_dump_svc (child): Callback failed with RPC error %s.",
+ clnt_sperrno(rstat));
+ auth_destroy(arg->cback->cl_auth);
+ clnt_destroy(arg->cback);
+ free(varg);
+ nis_delete_callback_id(myself);
+ return (0);
+ }
+ }
+ for (i = 0; i < queued_objs; i++)
+ nis_destroy_object(cbarray[i]);
+
+ queued_objs = 0;
+ }
+
+ free(item->name);
+ free(item);
+
+ boost = 0; /* on to next table */
+ }
+
+ syslog(LOG_INFO, "nis_dump_svc[%d]: good dump of %s, %d total objects "
+ "(%d deferred)", pthread_self(), arg->da_dir, totlobjs, skipobjs);
+ if (verbose)
+ syslog(LOG_INFO, "nis_dump_svc (child): dump complete.");
+
+ tv.tv_sec = 3;
+ tv.tv_usec = 0;
+ rstat = clnt_call(arg->cback, CBPROC_FINISH, xdr_void, 0, xdr_void, 0,
+ tv);
+ if (rstat != RPC_SUCCESS && rstat != RPC_TIMEDOUT) {
+
+ /*
+ * Since the callback function handling this FINISH rpc
+ * call does not reply, so we basically ignore it if a
+ * RPC_TIMEDOUT message is returned. Only log other types
+ * of non-successful messages.
+ */
+ syslog(LOG_WARNING,
+ "nis_dump_svc[%d]: Finish handshake returned %s",
+ pthread_self(), clnt_sperrno(rstat));
+ }
+ auth_destroy(arg->cback->cl_auth);
+ clnt_destroy(arg->cback);
+ free(varg);
+
+ nis_delete_callback_id(myself);
+
+ return (0);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_xx_proc.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_xx_proc.c
new file mode 100644
index 0000000000..9c45816c14
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nis_xx_proc.c
@@ -0,0 +1,1936 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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";
+ *
+ * nis_xx_proc.c
+ *
+ * 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/rpc.h>
+#include <rpc/key_prot.h>
+#include <rpcsvc/nis.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
+__sign_off(requester, res)
+ nis_name requester;
+ fd_result *res;
+{
+ char s_netname[MAXNETNAMELEN+1];
+ char pkey[HEXKEYBYTES+1];
+ unsigned char *digest;
+ unsigned int digestlen;
+ des_block deskey;
+ keybuf requester_pkey;
+ int err;
+ nis_attr qry[3];
+ char srch[2048];
+ nis_object *d_obj;
+ nis_name dirname;
+ struct ticks t;
+ nis_db_list_result *dbres;
+
+ /*
+ * 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
+ */
+
+ if ((strcmp(requester, "broadcast") == 0) ||
+ (strcmp(requester, "nobody") == 0))
+ return (0);
+
+ /* get the netname and the publickey of the requester machine */
+ if (!host2netname(s_netname, requester, NULL)) {
+ if (verbose)
+ syslog(LOG_ERR,
+ "__sign_off: host2netname failed for: %s", requester);
+ return (0);
+ }
+ dirname = nis_domain_of(requester);
+ if (! dirname || (*dirname == '.'))
+ return (0);
+
+ err = __directory_object(dirname, &t, 0, &d_obj);
+ pkey[0] = 0;
+ /* we serve this directory so we "know" the public key. */
+ if (d_obj) {
+ qry[0].zattr_ndx = "auth_name";
+ qry[0].zattr_val.zattr_val_len = strlen(s_netname) + 1;
+ qry[0].zattr_val.zattr_val_val = &s_netname[0];
+ qry[1].zattr_ndx = "auth_type";
+ qry[1].zattr_val.zattr_val_len = 4;
+ qry[1].zattr_val.zattr_val_val = "DES";
+ sprintf(srch, "cred.org_dir.%s", nis_domain_of(requester));
+ dbres = db_list(srch, 2, &qry[0]);
+ if (dbres->status == NIS_SUCCESS) {
+ strncpy(pkey, ENTRY_VAL(dbres->objs[0].o, 3),
+ HEXKEYBYTES);
+ }
+ } else {
+ getpublickey(s_netname, pkey);
+ }
+ if (pkey[0] == 0) {
+ syslog(LOG_ERR,
+ "__sign_off: Unable to get publickey for: %s",
+ s_netname);
+ return (0);
+ }
+ /* get the conversation key from the keyserver */
+ memset((void *)requester_pkey, 0, sizeof (keybuf));
+ memcpy((char *)requester_pkey, pkey, HEXKEYBYTES);
+ if (key_get_conv(requester_pkey, &deskey) != 0) {
+ if (verbose)
+ syslog(LOG_ERR,
+ "__sign_off: Could not get conversation key for: %s",
+ requester);
+ return (0);
+ }
+ /* calculate the md5 checksum of XDR'ed data and encrypt it */
+ digest = NULL;
+ __nis_calculate_encrypted_cksum(res->dir_data.dir_data_len,
+ res->dir_data.dir_data_val,
+ (char *)&deskey, &digestlen, &digest);
+ if (!digest)
+ return (0);
+
+ res->signature.signature_val = (char *)digest;
+ res->signature.signature_len = digestlen;
+ return (1);
+}
+#else
+static int
+__sign_off(req, res)
+ nis_name req;
+ fd_result *res;
+{
+ return (1);
+}
+#endif
+
+
+/*
+ * Return a Find Directory result.
+ */
+fd_result *
+__fd_res(requester, stat, o)
+ nis_name requester;
+ nis_error stat;
+ directory_obj *o;
+{
+ fd_result *res;
+ int status, size1;
+ XDR xdrs;
+ static fd_result mem_err = {NIS_NOMEMORY, 0};
+
+ res = (fd_result *)XCALLOC(1, sizeof (fd_result));
+ if (! res) {
+ syslog(LOG_CRIT,
+ "find_directory_svc: Out of memory, location request aborted.");
+ return (&mem_err);
+ }
+ add_cleanup((void (*)())XFREE, (char *)res, "fd_res: result");
+
+ res->status = stat;
+ res->source = nis_local_principal();
+ if (res->source == NULL) {
+ res->status = NIS_NOMEMORY; /* probable cause */
+ return (res);
+ }
+ /*
+ * If this is the answer, or "closer" to the
+ * name they want then just return it.
+ */
+ if (o) {
+ size1 = xdr_sizeof(xdr_directory_obj, o);
+ res->dir_data.dir_data_val = (char *)XMALLOC(size1);
+ if (res->dir_data.dir_data_val) {
+ add_cleanup((void (*)())XFREE,
+ (char *)res->dir_data.dir_data_val,
+ "fd_res: data");
+
+ xdrmem_create(&xdrs, res->dir_data.dir_data_val,
+ size1, XDR_ENCODE);
+ status = xdr_directory_obj(&xdrs, o);
+ if (!status)
+ syslog(LOG_ERR,
+ "Unable to encode resulting directory object.");
+ res->dir_data.dir_data_len = size1;
+ res->signature.signature_val = NULL;
+ res->signature.signature_len = 0;
+ /* sign the result */
+ __sign_off(requester, res);
+
+ } else {
+ syslog(LOG_CRIT,
+ "find_directory_svc: Out of memory, location request aborted.");
+ res->status = NIS_NOMEMORY;
+ 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.
+ *
+ */
+
+fd_result *
+nis_finddirectory_svc(argp, reqstp)
+ fd_args *argp;
+ struct svc_req *reqstp;
+{
+ directory_obj *dobj; /* The proposed answer */
+ char *in, *s, *t;
+ name_pos p;
+ int i_serve, i;
+ struct fd_result *res;
+ nis_db_result *dbres = NULL;
+ nis_error stat;
+ static fd_result mem_err = {NIS_NOMEMORY, 0};
+
+ if (verbose)
+ syslog(LOG_INFO,
+ "FINDDIR_SVC: Location request for directory %s",
+ argp->dir_name);
+
+ /*
+ * 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.
+ */
+ {
+ nis_object *tmpobj;
+ struct ticks ticks;
+ nis_error dostatus;
+
+ dostatus = __directory_object_msg(argp->dir_name, &ticks, 0,
+ &tmpobj, 0);
+ if (dostatus == NIS_SUCCESS) {
+ return (__fd_res(argp->requester, NIS_SUCCESS,
+ &(tmpobj->DI_data)));
+ }
+ }
+
+ /* Allocate some memory to hold our directory object */
+ dobj = (directory_obj *)XMALLOC(sizeof (*dobj));
+ if (! dobj) {
+ syslog(LOG_CRIT, "find_directory_svc: No memory.");
+ return (&mem_err);
+ }
+
+ /* Read the cache first (won't recurse) */
+ stat = __nis_CacheSearch(argp->dir_name, dobj);
+ if (stat != NIS_SUCCESS) {
+ XFREE(dobj);
+ syslog(LOG_ERR, "Location cache failure on server:%s",
+ nis_sperrno(stat));
+ return (__fd_res(argp->requester, stat, NULL));
+ }
+
+ /*
+ * The location algorithm works as follows: start on the
+ * easy cases and then work up to the hard ones.
+ */
+ p = nis_dir_cmp(argp->dir_name, dobj->do_name);
+ if (p == BAD_NAME) {
+ xdr_free(xdr_directory_obj, (char *)(dobj));
+ XFREE(dobj);
+ return (__fd_res(argp->requester, NIS_BADNAME, NULL));
+ } else if (p == SAME_NAME) {
+ /* Real easy, the cache returned the answer. */
+ add_xdr_cleanup(xdr_directory_obj, (char *)dobj,
+ "fd_res: dirobj");
+ return (__fd_res(argp->requester, NIS_SUCCESS, dobj));
+ }
+
+ 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) &&
+ (nis_dir_cmp(dobj->do_name, __nis_rpc_domain())
+ == HIGHER_NAME)) {
+ add_xdr_cleanup(xdr_directory_obj, (char *)dobj,
+ "fd_res: dirobj");
+ return (__fd_res(argp->requester, NIS_SUCCESS, dobj));
+ }
+
+ /* determine whether I serve this directory */
+ for (i = 0; i < dobj->do_servers.do_servers_len; i++) {
+ t = dobj->do_servers.do_servers_val[i].name;
+ if (nis_dir_cmp(s, t) == SAME_NAME)
+ break;
+ }
+ i_serve = (i < dobj->do_servers.do_servers_len);
+
+ /*
+ * 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 */
+ add_xdr_cleanup(xdr_directory_obj, (char *)dobj,
+ "fd_res: dobj (cache result)");
+ return (__fd_res(argp->requester, NIS_SUCCESS, dobj));
+ }
+ }
+ /* free data in the previous directory object */
+ xdr_free(xdr_directory_obj, (char *)(dobj));
+
+ if (nis_dir_cmp(argp->dir_name, __nis_rpc_domain()) == HIGHER_NAME) {
+ if (root_server) {
+ nis_object *obj;
+
+ /* Won't need the directory object anymore */
+ XFREE(dobj);
+
+ obj = nis_read_obj(nis_data(PARENT_OBJ));
+ if (! obj)
+ return (__fd_res(argp->requester,
+ NIS_NOSUCHNAME, NULL));
+
+ add_xdr_cleanup(xdr_nis_object, (char *)obj,
+ "fd_res: parent obj (cache result)");
+ if (obj->DI_data.do_type == NIS)
+ return (__fd_res(argp->requester, NIS_SUCCESS,
+ &(obj->DI_data)));
+ else
+ return (__fd_res(argp->requester, NIS_FOREIGNNS,
+ &(obj->DI_data)));
+ } else {
+ stat = __nis_CacheSearch(__nis_rpc_domain(), dobj);
+ if (stat == NIS_SUCCESS) {
+ add_xdr_cleanup(xdr_directory_obj,
+ (char *)dobj, "fd_res: dobj (parent)");
+ return (__fd_res(argp->requester, NIS_SUCCESS,
+ dobj));
+ } else {
+ XFREE(dobj);
+ return (__fd_res(argp->requester, stat, NULL));
+ }
+ }
+ }
+
+
+ if (nis_dir_cmp(argp->dir_name, __nis_rpc_domain()) ==
+ NOT_SEQUENTIAL) {
+ if (root_server) {
+ XFREE(dobj);
+ return (__fd_res(argp->requester,
+ 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.
+ */
+
+ in = argp->dir_name;
+ while (nis_dir_cmp(in, __nis_rpc_domain()) == LOWER_NAME) {
+ if (verbose)
+ syslog(LOG_INFO, "Locating %s in the database.", in);
+ dbres = db_lookup(in);
+ if (dbres->status == NIS_SUCCESS) {
+
+ /*
+ * POLICY : You can't link directories with
+ * LINK objects.
+ * ANSWER : Because the recursion could become
+ * infinite.
+ */
+ XFREE(dobj); /* won't be needing this any more */
+ if (__type_of(dbres->obj) != NIS_DIRECTORY_OBJ) {
+ if (verbose)
+ syslog(LOG_INFO,
+ "Object %s isn't a directory.",
+ dbres->obj->zo_name);
+ res = __fd_res(argp->requester, NIS_BADOBJECT,
+ NULL);
+ } else {
+ /*
+ * we found a directory object in the "path"
+ * of the desired object.
+ */
+ if (verbose)
+ syslog(LOG_INFO,
+ "Found a directory %s",
+ dbres->obj->zo_name);
+ dobj = &(dbres->obj->DI_data);
+ p = nis_dir_cmp(dobj->do_name, argp->dir_name);
+ if (p == SAME_NAME) {
+ res = __fd_res(argp->requester,
+ 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 < dobj->do_servers.do_servers_len;
+ i++) {
+ t =
+ dobj->do_servers.do_servers_val[i].name;
+ if (nis_dir_cmp(s, t)
+ == SAME_NAME)
+ break;
+ }
+ if (i < dobj->do_servers.do_servers_len)
+ res = __fd_res(argp->requester,
+ NIS_NOSUCHNAME, NULL);
+ else
+ res = __fd_res(argp->requester,
+ NIS_SUCCESS, dobj);
+ }
+ }
+ return (res);
+ } else if (dbres->status == NIS_NOTFOUND) {
+ XFREE(dobj);
+ return (__fd_res(argp->requester, NIS_NOSUCHNAME,
+ NULL));
+ }
+ in = nis_domain_of(in);
+ }
+
+ if (verbose) {
+ syslog(LOG_INFO, "Unable to locate directory : %s",
+ argp->dir_name);
+ }
+
+ /* Option #2, let my parent deal with it. */
+ stat = __nis_CacheSearch(__nis_rpc_domain(), dobj);
+ if (stat == NIS_SUCCESS) {
+ add_xdr_cleanup(xdr_directory_obj, (char *)dobj,
+ "fd_res: dobj (parent)");
+ return (__fd_res(argp->requester, NIS_SUCCESS, dobj));
+ }
+ XFREE(dobj);
+ return (__fd_res(argp->requester, stat, NULL));
+}
+
+/*
+ * nis_callback_svc
+ *
+ * Check on the state of a child process or thread who is calling back to the
+ * client.
+ */
+
+bool_t *
+nis_callback_svc(argp, reqstp)
+ netobj *argp;
+ struct svc_req *reqstp;
+{
+#define res (__nis_get_tsd()->nis_callback_svc_res)
+ pthread_t id;
+ anonid_t anonid;
+ char pname[1024], id_pname[1024];
+
+ if (argp->n_bytes == 0 || argp->n_len != sizeof (anonid_t)) {
+ syslog(LOG_ERR, "CALLBACK_SVC: bad argument");
+ res = FALSE;
+ return (&res);
+ }
+
+ /* Get name of principal calling us */
+ nis_getprincipal(pname, reqstp);
+
+ /*
+ * 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 */
+ anonid = *((anonid_t *)argp->n_bytes);
+ id_pname[0] = '\0';
+ id = nis_get_callback_id(anonid, id_pname, sizeof (id_pname));
+
+ if (verbose)
+ syslog(LOG_INFO,
+ "CALLBACK_SVC: ID = %ld, anon id = %ld, pname = %s, id_pname = %s",
+ id, anonid, pname, id_pname);
+
+ /* Verify caller principal same as pid principal */
+ if (secure_level >= 2 && strcmp(pname, id_pname) != 0 &&
+ strcmp("nobody", id_pname) != 0) {
+ res = FALSE;
+ return (&res);
+ }
+
+ res = (pthread_kill(id, 0) == 0);
+
+ return (&res);
+}
+
+#undef 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;
+ struct ops_stats *op;
+ ulong_t avgtime = 0;
+ static char opbuf[80];
+
+ if (p < 0 || p >= (sizeof (opnames) / sizeof (opnames[0]))) {
+ sprintf(opbuf, "<unknown operation %d>", p);
+ return (opbuf);
+ }
+ RLOCK(nisopstats);
+ op = &(nisopstats[p]);
+ for (i = 0; (i < 16) && op->tsamps[i]; i++)
+ avgtime += op->tsamps[i];
+ if (i)
+ avgtime = avgtime / i;
+ sprintf(opbuf, "\nOP=%s:C=%d:E=%d:T=%d", opnames[p], op->calls,
+ op->errors, avgtime);
+ RULOCK(nisopstats);
+ return (opbuf);
+}
+
+extern struct timeval start_time;
+
+static void
+up_since(s)
+ char *s;
+{
+ struct timeval tv;
+ long days, hrs, mins, secs;
+
+ gettimeofday(&tv, NULL);
+ secs = tv.tv_sec - start_time.tv_sec;
+ days = secs / 86400;
+ hrs = (secs - (days * 86400)) / 3600;
+ mins = (secs - (days * 86400) - (hrs * 3600)) / 60;
+ secs = secs % 60;
+ sprintf(s, "up %dD, %02d:%02d:%02d", days, hrs, mins, secs);
+}
+
+static void
+free_taglist(nis_taglist *tl)
+{
+ nis_freetags(tl->tags.tags_val, tl->tags.tags_len);
+ free((void *)tl);
+}
+
+#define CHECK_TAG_S_ACC(tag) \
+ if (!nis_op_access("NIS_STATUS", tag, 0, 0, \
+ reqstp)) { \
+ strcpy(tag_data, "<permission denied>"); \
+ break; \
+ }
+
+/*
+ * nis_status, return status on the nis server.
+ */
+nis_taglist *
+nis_status_svc(argp, reqstp)
+ nis_taglist *argp;
+ struct svc_req *reqstp;
+{
+ int i, nt, j;
+ nis_tag *tlist;
+ char tag_data[1024]; /* Enough for any tag other than DIR_LIST */
+ char *dirs; /* malloc:ed string for DIR_LIST */
+ nis_taglist *res;
+ int grpcachecall;
+ int grpcachemiss;
+ int grpcachehit;
+ extern int __nis_group_cache_stats(int *, int *, int *);
+ extern unsigned int heap_start;
+
+ if (verbose)
+ syslog(LOG_INFO, "STATUS_SVC: %d tags passed",
+ argp->tags.tags_len);
+ nt = argp->tags.tags_len;
+ tlist = argp->tags.tags_val;
+
+ /* malloc memory for result */
+ res = (nis_taglist *)XCALLOC(1, sizeof (nis_taglist));
+ if (res == NULL) {
+ syslog(LOG_ERR, "nis_status_svc: no memory.");
+ return (NULL);
+ }
+ add_cleanup((void (*)())free_taglist, res, "tag list");
+
+ res->tags.tags_val = (nis_tag *)XCALLOC(nt, sizeof (nis_tag));
+ res->tags.tags_len = nt;
+ if (res->tags.tags_val == NULL) {
+ syslog(LOG_ERR, "nis_status_svc: no memory.");
+ return (NULL);
+ }
+
+ for (i = 0; i < nt; i++) {
+ switch (tlist[i].tag_type) {
+ case TAG_UPTIME :
+ CHECK_TAG_S_ACC("TAG_UPTIME");
+ up_since(tag_data);
+ break;
+ case TAG_S_DCACHE :
+ CHECK_TAG_S_ACC("TAG_S_DCACHE");
+ RLOCK(dircachestats);
+ if (dircachecall)
+ sprintf(tag_data,
+ "C=%d:H=%d:M=%d:HR=%2.0f%%",
+ dircachecall, dircachehit,
+ dircachemiss,
+ ((dircachecall-dircachemiss)*(float)100)/
+ dircachecall);
+ else
+ sprintf(tag_data,
+ "C=0:H=0:M=0:HR=100%%");
+ RULOCK(dircachestats);
+ break;
+ case TAG_S_STORAGE :
+ CHECK_TAG_S_ACC("TAG_S_STORAGE");
+ sprintf(tag_data, "%d", __nis_ss_used);
+ break;
+ case TAG_S_GCACHE :
+ CHECK_TAG_S_ACC("TAG_S_GCACHE");
+ if (__nis_group_cache_stats(&grpcachecall,
+ &grpcachehit, &grpcachemiss) &&
+ grpcachecall != 0)
+ sprintf(tag_data,
+ "C=%d:H=%d:M=%d:HR=%2.0f%%",
+ grpcachecall, grpcachehit,
+ grpcachemiss,
+ ((grpcachecall-grpcachemiss)*(float)100)/
+ grpcachecall);
+ else
+ sprintf(tag_data,
+ "C=0:H=0:M=0:HR=100%%");
+ break;
+ case TAG_OPSTATS :
+ CHECK_TAG_S_ACC("TAG_OPSTATS");
+ if ((*(tlist[i].tag_val) == '\0') ||
+ (strcmp(tlist[i].tag_val, "all") == 0)) {
+ tag_data[0] = '\0';
+ for (j = 0; j < 24; j++) {
+ if (! nisopstats[j].calls)
+ continue;
+ strcat(tag_data, prtop(j));
+ }
+ } else {
+ j = atoi(tlist[i].tag_val);
+ sprintf(tag_data, "%s", prtop(j));
+ }
+ break;
+ case TAG_HEAP :
+ CHECK_TAG_S_ACC("TAG_HEAP");
+ sprintf(tag_data, "%u",
+ (unsigned int) sbrk(0) - heap_start);
+ break;
+ case TAG_DIRLIST:
+ CHECK_TAG_S_ACC("TAG_DIRLIST");
+ if (nis_server_control(SERVING_LIST,
+ DIR_GETLIST, &dirs) == 0) {
+ dirs = NULL;
+ }
+ break;
+ case TAG_NISCOMPAT:
+ CHECK_TAG_S_ACC("TAG_NISCOMPAT");
+ if (emulate_yp)
+ strcpy(tag_data, "ON");
+ else
+ strcpy(tag_data, "OFF");
+ break;
+ case TAG_DNSFORWARDING:
+ CHECK_TAG_S_ACC("TAG_DNSFORWARDING");
+ if (resolv_flag)
+ strcpy(tag_data, "ON");
+ else
+ strcpy(tag_data, "OFF");
+ break;
+ case TAG_SECURITY_LEVEL:
+ CHECK_TAG_S_ACC("TAG_SECURITY_LEVEL");
+ sprintf(tag_data, "%d", secure_level);
+ break;
+ case TAG_ROOTSERVER:
+ CHECK_TAG_S_ACC("TAG_ROOTSERVER");
+ if (root_server)
+ strcpy(tag_data, "ON");
+ else
+ strcpy(tag_data, "OFF");
+ break;
+ default :
+ strcpy(tag_data, "<Unknown Statistic>");
+ break;
+ }
+ if (tlist[i].tag_type == TAG_DIRLIST)
+ res->tags.tags_val[i].tag_val = dirs;
+ else
+ res->tags.tags_val[i].tag_val = strdup(tag_data);
+ if (res->tags.tags_val[i].tag_val == NULL) {
+ syslog(LOG_ERR, "nis_status_svc: no memory.");
+ return (NULL);
+ }
+ res->tags.tags_val[i].tag_type = tlist[i].tag_type;
+ }
+ 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 *
+nis_cptime_svc(argp, reqstp)
+ nis_name *argp;
+ struct svc_req *reqstp;
+{
+#define res (__nis_get_tsd()->nis_cptime_svc_res)
+
+ if (verbose)
+ syslog(LOG_INFO, "CPTIME_SVC: '%s'", *argp);
+
+ if (!nis_op_access("NIS_CPTIME", 0, *argp, 0, reqstp)) {
+ if (verbose)
+ syslog(LOG_INFO,
+ "CPTIME_SVC: authorization error on \"%s\"", *argp);
+ res = 0;
+ return (&res);
+ }
+
+ res = last_update(*argp);
+ return (&res);
+}
+
+#undef 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.
+ *
+ */
+cp_result *
+nis_checkpoint_svc(argp, reqstp)
+ nis_name *argp;
+ struct svc_req *reqstp;
+{
+ cp_result *res;
+ nis_error stat;
+ struct ticks t;
+ nis_object *dobj;
+ /* xxx static should be const */
+ static cp_result mem_err = {NIS_NOMEMORY, 0, 0};
+
+ if (verbose)
+ syslog(LOG_INFO, "CHECKPOINT_SVC: '%s'", *argp);
+
+ res = (cp_result *)XCALLOC(1, sizeof (cp_result));
+ if (! res)
+ return (&mem_err);
+ add_cleanup((void (*)())XFREE, (char *)res, "chkpnt result");
+
+ if (!nis_op_access("NIS_CHECKPOINT", 0, *argp, 0, reqstp)) {
+ if (verbose)
+ syslog(LOG_INFO,
+ "CHECKPOINT_SVC: authorization error on \"%s\"",
+ *argp);
+ res->cp_status = NIS_PERMISSION;
+ 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.
+ */
+ if (*argp == 0 || **argp == 0) {
+ clear_checkpoint_list(); /* delete remembered items */
+ checkpoint_all = 1;
+ } else {
+ /* Otherwise, specified directory. make sure we serve it. */
+ stat = __directory_object(*argp, &t, 0, &dobj);
+ if (stat != NIS_SUCCESS) {
+ res->cp_status = stat;
+ res->cp_zticks = __stop_clock(0);
+ return (res);
+ }
+
+ /* Only add to list if we are not going to cp entire db */
+ if (!checkpoint_all)
+ add_pingitem_with_name(*argp, 0, 0, &checkpoint_list);
+ }
+
+ /*
+ * Set status to SUCCESS here to indicate that directory
+ * object is valid and will be checkpointed.
+ */
+
+ res->cp_status = NIS_SUCCESS;
+ res->cp_zticks = __stop_clock(0);
+ 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.
+ */
+ wakeup_servloop();
+
+ return (res);
+}
+
+/*
+ * nis_dumplog, dump the log from the indicated timestamp on.
+ */
+log_result *
+nis_dumplog_svc(argp, reqstp)
+ dump_args *argp;
+ struct svc_req *reqstp;
+{
+ log_result *tmp;
+ char pname[1024];
+ nis_object *dobj;
+ nis_result *lres;
+ nis_server *srvs;
+ int ns;
+ int i;
+
+
+
+ tmp = (log_result *)XCALLOC(1, sizeof (log_result));
+ add_xdr_cleanup(xdr_log_result, (char *)tmp, "dumplog result");
+
+ if (argp->da_time == 0) {
+ if (verbose)
+ syslog(LOG_INFO,
+ "nis_dumplog_svc: replica asking for time 0. RESYNC.");
+ tmp->lr_status = NIS_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.
+ */
+ lres = nis_lookup(argp->da_dir, MASTER_ONLY);
+ if (lres->status != NIS_SUCCESS) {
+ if (verbose)
+ syslog(LOG_INFO,
+ "nis_dump_svc: No directory object, error %s.",
+ nis_sperrno(lres->status));
+ tmp->lr_status = lres->status;
+ nis_freeresult(lres);
+ return (tmp);
+ }
+
+ /*
+ * Make sure it is a directory they want dumped.
+ */
+ dobj = lres->objects.objects_val;
+ if (__type_of(dobj) != NIS_DIRECTORY_OBJ) {
+ syslog(LOG_ERR,
+ "nis_dump_svc: request to dump %s.%s which isn't a directory!",
+ dobj->zo_name, dobj->zo_domain);
+ tmp->lr_status = NIS_BADOBJECT;
+ nis_freeresult(lres);
+ return (tmp);
+ }
+
+ /*
+ * Make sure that we are the master server for that directory.
+ */
+ ns = dobj->DI_data.do_servers.do_servers_len;
+ srvs = dobj->DI_data.do_servers.do_servers_val;
+ if (nis_dir_cmp(srvs[0].name, nis_local_host()) != SAME_NAME) {
+ nis_freeresult(lres);
+ tmp->lr_status = NIS_NOTMASTER;
+ return (tmp);
+ }
+
+ /*
+ * Check to see that a valid replica is asking for the dump
+ */
+ nis_getprincipal(pname, reqstp);
+ if (secure_level) {
+ for (i = 1; (i < ns) &&
+ (nis_dir_cmp(pname, srvs[i].name) != SAME_NAME); i++)
+ ;
+
+ if (i == ns) {
+ if (verbose)
+ syslog(LOG_INFO,
+ "nis_dump_svc: invalid replica '%s'", pname);
+ nis_freeresult(lres);
+ tmp->lr_status = NIS_PERMISSION;
+ return (tmp);
+ }
+ }
+
+ if (verbose)
+ syslog(LOG_INFO, "DUMPLOG_SVC : dumping '%s' to host '%s'",
+ argp->da_dir, pname);
+
+ /*
+ * Pass the whole directory object so that entries_since()
+ * can add it to the ping list should it not return all
+ * entries this time.
+ */
+ entries_since(dobj, argp->da_time, tmp);
+
+ /* don't need this anymore */
+ nis_freeresult(lres);
+
+ if (verbose)
+ syslog(LOG_INFO,
+ "nis_dumplog_svc: returning status of '%s', and %d deltas.",
+ nis_sperrno(tmp->lr_status),
+ tmp->lr_entries.lr_entries_len);
+ return (tmp);
+}
+
+/*
+ * nis_dump_svc, dump the entire contents of the named directory.
+ */
+#define CB_BUF_SIZE 128
+
+log_result *
+nis_dump_svc(argp, reqstp)
+ dump_args *argp;
+ struct svc_req *reqstp;
+{
+ log_result *res;
+ CLIENT *cback = NULL;
+ char pname[1024];
+ dumpsvc_thread_arg_t *mtdumparg;
+ pthread_t tid;
+ pthread_attr_t attr;
+ int stat;
+ int i;
+ nis_server *srvs;
+ int ns;
+ log_entry *le = NULL;
+ nis_object *dobj;
+ ulong_t ttime;
+ nis_result *lres;
+ nis_server cbsrv;
+ struct netbuf *rpc_origin;
+ endpoint *org_endpoint, *alt_endpoint, epbuf;
+ char uaddrbuf[256], *uaddr = uaddrbuf;
+
+
+ if (verbose)
+ syslog(LOG_INFO, "DUMP_SVC : Dump directory '%s'",
+ argp->da_dir);
+ res = (log_result *) XCALLOC(1, sizeof (log_result));
+ add_cleanup((void (*)())XFREE, (char *)res, "dump result");
+
+ /*
+ * Start the series of checks that need to be met before dumping
+ * can begin.
+ */
+ if (argp->da_cbhost.da_cbhost_len != 1) {
+ if (verbose)
+ syslog(LOG_INFO, "nis_dump_svc: missing callback.");
+ res->lr_status = NIS_CBERROR;
+ 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.
+ */
+ lres = nis_lookup(argp->da_dir, MASTER_ONLY);
+ if (lres->status != NIS_SUCCESS) {
+ if (verbose)
+ syslog(LOG_INFO,
+ "nis_dump_svc: No directory object, error %s.",
+ nis_sperrno(lres->status));
+ res->lr_status = lres->status;
+ nis_freeresult(lres);
+ return (res);
+ }
+
+ /*
+ * Make sure it is a directory they want dumped.
+ */
+ dobj = lres->objects.objects_val;
+ if (__type_of(dobj) != NIS_DIRECTORY_OBJ) {
+ syslog(LOG_ERR,
+ "nis_dump_svc: request to dump %s.%s which isn't a directory!",
+ dobj->zo_name, dobj->zo_domain);
+ res->lr_status = NIS_BADOBJECT;
+ nis_freeresult(lres);
+ return (res);
+ }
+
+ /*
+ * Make sure that we are the master server for that directory.
+ */
+ ns = dobj->DI_data.do_servers.do_servers_len;
+ srvs = dobj->DI_data.do_servers.do_servers_val;
+ if (nis_dir_cmp(srvs[0].name, nis_local_host()) != SAME_NAME) {
+ nis_freeresult(lres);
+ res->lr_status = NIS_NOTMASTER;
+ return (res);
+ }
+
+ /*
+ * Check to see that a valid replica is asking for the dump
+ */
+ nis_getprincipal(pname, reqstp);
+ if (secure_level) {
+ for (i = 1; (i < ns) &&
+ (nis_dir_cmp(pname, srvs[i].name) != SAME_NAME); i++)
+ ;
+
+ if (i == ns) {
+ if (verbose)
+ syslog(LOG_INFO,
+ "nis_dump_svc: invalid replica '%s'", pname);
+ res->lr_status = NIS_PERMISSION;
+ 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.
+ */
+ cbsrv = *argp->da_cbhost.da_cbhost_val;
+ org_endpoint = cbsrv.ep.ep_val;
+ rpc_origin = svc_getrpccaller(reqstp->rq_xprt);
+ if (argp->da_cbhost.da_cbhost_val->ep.ep_len == 1 &&
+ (alt_endpoint = __nis_alt_callback_server(org_endpoint,
+ argp->da_cbhost.da_cbhost_val->ep.ep_len,
+ rpc_origin,
+ &uaddr, sizeof (uaddrbuf))) != 0) {
+ epbuf = alt_endpoint[0];
+ cbsrv.ep.ep_val = &epbuf;
+ free(alt_endpoint);
+ }
+
+ /*
+ * XXX now, do we use the handle that was passed us? or do we use
+ * the one in the object?
+ */
+ cback = nis_make_rpchandle(argp->da_cbhost.da_cbhost_val, 1,
+ CB_PROG, 1, ZMH_VC+ZMH_AUTH, 128, 8192);
+
+ if (! cback) {
+ if (verbose)
+ syslog(LOG_INFO,
+ "nis_dump_svc: unable to create callback.");
+ res->lr_status = NIS_CBERROR;
+ nis_freeresult(lres);
+ return (res);
+ }
+
+ /* don't need this anymore */
+ nis_freeresult(lres);
+
+ ttime = last_update(argp->da_dir);
+ if (ttime == time(0)) {
+ syslog(LOG_INFO,
+ "nis_dump_svc: Current updates found, try later");
+ res->lr_status = NIS_DUMPLATER;
+ return (res);
+ } else if (ttime == 0)
+ syslog(LOG_INFO,
+ "nis_dump_svc: directory %s has no update timestamp.",
+ argp->da_dir);
+ else {
+ le = (log_entry *) XCALLOC(1, sizeof (log_entry));
+ if (! le) {
+ syslog(LOG_INFO, "nis_dump_svc: out of memory.");
+ res->lr_status = NIS_NOMEMORY;
+ auth_destroy(cback->cl_auth);
+ clnt_destroy(cback);
+ return (res);
+ }
+ add_cleanup((void (*)())XFREE, (char *)le, "dump logent");
+ le->le_princp = XSTRDUP(nis_local_principal());
+ add_cleanup((void (*)())XFREE, (char *)le->le_princp,
+ "dump logent->princp");
+ le->le_time = ttime;
+ le->le_type = UPD_STAMP;
+ le->le_name = XSTRDUP(argp->da_dir);
+ add_cleanup((void (*)())XFREE, (char *)le->le_name,
+ "dump logent->name");
+ __type_of(&(le->le_object)) = NIS_NO_OBJ;
+ le->le_object.zo_name = "";
+ le->le_object.zo_owner = "";
+ le->le_object.zo_group = "";
+ le->le_object.zo_domain = le->le_name;
+ res->lr_entries.lr_entries_len = 1;
+ res->lr_entries.lr_entries_val = le;
+ }
+
+ if ((mtdumparg = calloc(1, sizeof (*mtdumparg))) == 0) {
+ syslog(LOG_WARNING,
+ "nis_dump_svc: memory allocation failed for %d bytes",
+ sizeof (*mtdumparg));
+ res->lr_status = NIS_NOMEMORY;
+ auth_destroy(cback->cl_auth);
+ clnt_destroy(cback);
+ return (res);
+ }
+ (void) strcpy(mtdumparg->da_dir, argp->da_dir);
+ (void) strcpy(mtdumparg->pname, pname);
+ mtdumparg->cback = cback;
+ mtdumparg->ttime = ttime;
+
+ (void) pthread_attr_init(&attr);
+ (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if ((stat = pthread_create(&tid, &attr, dumpsvc_thread, mtdumparg)) ==
+ 0) {
+ if (verbose)
+ syslog(LOG_INFO,
+ "nis_dump_svc: created callback thread %d", tid);
+ res->lr_status = NIS_CBRESULTS;
+ res->lr_cookie.n_bytes = (char *)malloc(sizeof (anonid_t));
+ if (res->lr_cookie.n_bytes != NULL) {
+ add_cleanup((void (*)())XFREE, res->lr_cookie.n_bytes,
+ "anonid");
+ res->lr_cookie.n_len = sizeof (anonid_t);
+ memcpy(res->lr_cookie.n_bytes, &tid,
+ sizeof (anonid_t));
+ } else {
+ res->lr_cookie.n_len = 0;
+ syslog(LOG_WARNING,
+ "nis_dump_svc: no memory for callback id cookie");
+ }
+ if (verbose)
+ syslog(LOG_INFO,
+ "nis_dump_svc: Parent thread returning");
+ } else {
+ if (verbose)
+ syslog(LOG_ERR,
+ "nis_dump_svc: callback thread create failed: %d",
+ stat);
+ auth_destroy(cback->cl_auth);
+ clnt_destroy(cback);
+ res->lr_status = NIS_TRYAGAIN;
+ }
+ (void) pthread_attr_destroy(&attr);
+ return (res);
+}
+
+/*
+ * nis_ping()
+ *
+ * This function receives a "ping" and schedules an update session.
+ */
+void *
+nis_ping_svc(argp, reqstp)
+ ping_args *argp;
+ struct svc_req *reqstp;
+{
+ static int foo;
+ struct ticks t;
+ nis_object *obj;
+ nis_error err;
+
+ if (verbose)
+ syslog(LOG_INFO, "PING_SVC: dir = '%s'", argp->dir);
+
+ if (!nis_op_access("NIS_PING", 0, argp->dir, 0, reqstp)) {
+ if (verbose)
+ syslog(LOG_INFO,
+ "PING_SVC: unauthorized ping for \"%s\" ignored", argp->dir);
+ return (0);
+ }
+
+ /* Check to see if we're replicating the root object */
+ if (root_object_p(argp->dir)) {
+ /*
+ * 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;
+ if (last_update(argp->dir) < argp->stamp)
+ add_pingitem_with_name(argp->dir,
+ 0, argp->stamp, &upd_list);
+ } else {
+ err = __directory_object(argp->dir, &t, NO_CACHE, &obj);
+ if ((err == NIS_SUCCESS) && (obj != NULL) &&
+ (last_update(argp->dir) < argp->stamp))
+ add_pingitem(obj, argp->stamp, &upd_list);
+ }
+
+ if ((verbose) && (err != NIS_SUCCESS))
+ syslog(LOG_INFO, "PING_SVC: error %s.", nis_sperrno(err));
+ else if (verbose)
+ syslog(LOG_INFO, "PING_SVC: done.");
+
+ /*
+ * 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.
+ */
+ wakeup_servloop();
+
+ return (&foo);
+}
+
+#define TAG_VAL(n) argp->tags.tags_val[n].tag_val
+#define TAG_TYPE(n) argp->tags.tags_val[n].tag_type
+#define CHECK_TAG_ACC(tag) \
+ if (!nis_op_access("NIS_SERVSTATE", tag, 0, 0, \
+ reqstp)) { \
+ free(TAG_VAL(i)); \
+ TAG_VAL(i) = strdup("<permission denied>"); \
+ 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.
+ */
+nis_taglist *
+nis_servstate_svc(argp, reqstp)
+ nis_taglist *argp;
+ struct svc_req *reqstp;
+{
+ int i;
+
+ if (verbose)
+ syslog(LOG_INFO, "SERVSTATE_SVC: %d tags.",
+ argp->tags.tags_len);
+
+ if (argp->tags.tags_len > 0) {
+ for (i = 0; i < argp->tags.tags_len; i++) {
+ switch (TAG_TYPE(i)) {
+ case TAG_DEBUG :
+ CHECK_TAG_ACC("TAG_DEBUG");
+ if (strcmp(TAG_VAL(i), "on") == 0)
+ verbose = 1;
+ else if (strcmp(TAG_VAL(i), "off") == 0)
+ verbose = 0;
+ else {
+ free(TAG_VAL(i));
+ TAG_VAL(i) = strdup("unchanged");
+ }
+ break;
+ case TAG_DCACHE_ONE:
+ CHECK_TAG_ACC("TAG_DCACHE_ONE");
+ if (TAG_VAL(i) && strlen(TAG_VAL(i))) {
+ flush_dircache(TAG_VAL(i), NULL);
+ } else {
+ free(TAG_VAL(i));
+ TAG_VAL(i) = strdup("Name missing");
+ }
+ break;
+ case TAG_DCACHE_ONE_REFRESH:
+ CHECK_TAG_ACC("TAG_DCACHE_ONE_REFRESH");
+ if (TAG_VAL(i) && strlen(TAG_VAL(i))) {
+ flush_dircache_refresh(TAG_VAL(i));
+ } else {
+ free(TAG_VAL(i));
+ TAG_VAL(i) = strdup("Name missing");
+ }
+ break;
+ case TAG_DCACHE_ALL:
+ /* This flushes _all_ directory objects */
+ CHECK_TAG_ACC("TAG_DCACHE_ALL");
+ flush_dircache_all();
+ break;
+ case TAG_TCACHE_ONE :
+ CHECK_TAG_ACC("TAG_TCACHE_ONE");
+ if (TAG_VAL(i) && strlen(TAG_VAL(i))) {
+ flush_tablecache(TAG_VAL(i));
+ } else {
+ free(TAG_VAL(i));
+ TAG_VAL(i) = strdup("Name missing");
+ }
+ break;
+ case TAG_TCACHE_ALL:
+ /* This flushes _all_ table caches */
+ CHECK_TAG_ACC("TAG_TCACHE_ALL");
+ flush_tablecache_all();
+ break;
+ case TAG_GCACHE_ONE :
+ CHECK_TAG_ACC("TAG_GCACHE_ONE");
+ if (TAG_VAL(i) && strlen(TAG_VAL(i))) {
+ flush_groupcache(TAG_VAL(i));
+ } else {
+ free(TAG_VAL(i));
+ TAG_VAL(i) = strdup("Name missing");
+ }
+ break;
+ case TAG_GCACHE_ALL :
+ CHECK_TAG_ACC("TAG_GCACHE_ALL");
+ flush_groupcache_all();
+ break;
+ case TAG_HEAP :
+ CHECK_TAG_ACC("TAG_HEAP");
+#ifdef MEM_DEBUG
+ xdump();
+#else /* MEM_DEBUG */
+ TAG_VAL(i) = strdup("<Not enabled>");
+#endif /* MEM_DEBUG */
+ break;
+ case TAG_READONLY:
+ /* Disables updates, used for nisbackup(1M) */
+ CHECK_TAG_ACC("TAG_READONLY");
+ if ((CHILDPROC) || (readonly)) {
+ TAG_VAL(i) = strdup("Try again");
+ break;
+ }
+ readonly = 1;
+ break;
+ case TAG_READWRITE:
+ /* Reset to read/write, used by nisbackup(1M) */
+ CHECK_TAG_ACC("TAG_READWRITE");
+ if ((CHILDPROC) || (readonly == 0)) {
+ TAG_VAL(i) = strdup("Invalid request");
+ break;
+ }
+ readonly = 0;
+ break;
+
+ default :
+ syslog(LOG_INFO, "Unknown tag %d",
+ TAG_TYPE(i));
+ TAG_VAL(i) = strdup("<Unknown tag>");
+ 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.
+ */
+nis_error *
+nis_mkdir_svc(argp, rqstp)
+ nis_name *argp;
+ struct svc_req *rqstp;
+{
+#define result (__nis_get_tsd()->nis_mkdir_svc_result)
+ directory_obj *da;
+ nis_object *obj;
+ nis_result *res;
+ int i;
+ time_t ttime;
+ nis_server *srvs;
+ nis_name s;
+ name_pos p;
+
+
+ /* Go look for the object for this new directory. */
+ if (verbose)
+ syslog(LOG_INFO, "MKDIR_SVC : Creating directory : %s", *argp);
+
+ if (readonly) {
+ syslog(LOG_INFO,
+ "nis_mkdir_svc: readonly child called to mkdir, ignored.");
+ result = NIS_TRYAGAIN;
+ return (&result);
+ }
+
+ /* Check NIS_MKDIR access to parent directory */
+ if (!nis_op_access("NIS_MKDIR", 0, nis_domain_of(*argp), 0, rqstp)) {
+ if (verbose)
+ syslog(LOG_INFO,
+ "MKDIR_SVC : authorization error on \"%s\"",
+ nis_domain_of(*argp));
+ result = NIS_PERMISSION;
+ 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.
+ */
+ res = nis_lookup(*argp, MASTER_ONLY);
+ /* Schedule this to be cleaned up when we return */
+ add_cleanup(nis_freeresult, (char *)res, "mkdir lookup res");
+ result = res->status;
+ if (result != NIS_SUCCESS) {
+ if (verbose)
+ syslog(LOG_WARNING,
+ "nis_mkdir_svc: could not get the directory object for %s: %s",
+ *argp, nis_sperrno(result));
+ return (&result);
+ }
+
+ obj = res->objects.objects_val;
+ /*
+ * Now we either have an object that is a directory object or
+ * we don't.
+ */
+ if (__type_of(obj) != NIS_DIRECTORY_OBJ) {
+ result = NIS_BADOBJECT;
+ syslog(LOG_ERR, "nis_mkdir_svc: %s is not a directory object",
+ *argp);
+ return (&result);
+ }
+
+ /*
+ * verify that we serve it.
+ */
+ da = &(obj->DI_data);
+ s = nis_local_host(); /* optimization */
+ srvs = da->do_servers.do_servers_val;
+ for (i = 0; i < da->do_servers.do_servers_len; i++) {
+ if (nis_dir_cmp(srvs[i].name, s) == SAME_NAME)
+ break;
+ }
+
+ if (i == da->do_servers.do_servers_len) {
+ syslog(LOG_ERR,
+ "nis_mkdir_svc: Attempt to add a directory %s which we don't serve!",
+ *argp);
+ result = NIS_NOT_ME;
+ return (&result);
+ }
+
+ p = nis_dir_cmp(*argp, __nis_rpc_domain());
+ if (p != LOWER_NAME) {
+ syslog(LOG_ERR,
+ "nis_mkdir_svc: Attempt to create illegal directory %s",
+ *argp);
+ result = NIS_BADNAME;
+ return (&result);
+ }
+
+ /*
+ * Now we check to see if we can read anything out of the
+ * directories database.
+ */
+ result = db_find_table(*argp);
+ if (result == NIS_NOSUCHTABLE) {
+ if ((result = db_create(*argp, &tbl_prototype)) !=
+ NIS_SUCCESS) {
+ syslog(LOG_ERR, "Unable to create table %s: %s.",
+ *argp, nis_sperrno(result));
+ return (&result);
+ }
+ /* Give it a stability timestamp. */
+ ttime = time(0);
+ make_stamp(*argp, ttime);
+ /* Add it to the list of directories that it serves */
+ nis_server_control(SERVING_LIST, DIR_ADD, *argp);
+ }
+ if (verbose)
+ syslog(LOG_INFO, "Successful creation.");
+
+ return (&result);
+}
+
+#undef 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.
+ */
+nis_error *
+nis_rmdir_svc(argp, rqstp)
+ nis_name *argp;
+ struct svc_req *rqstp;
+{
+#define result (__nis_get_tsd()->nis_rmdir_svc_result)
+ directory_obj *da; /* Temp pointing to our data */
+ int i; /* Loop counter */
+ nis_name s; /* Another temporary */
+ nis_server *srvs; /* Another temporary */
+ nis_result *res; /* Result of nis lookup */
+ nis_object *obj; /* object temporary */
+ nis_db_result *dbres, /* Result db lookup */
+ *rrs; /* From removing entries */
+ nis_fn_result *fnr, /* Result of iteration */
+ *tlist;
+ char namebuf[1024]; /* buffer where we cons name */
+ nis_error error;
+ int xid;
+ int complete_removal = 1;
+ char *principal = 0;
+
+ /* Go look for the object for this new directory. */
+ if (verbose)
+ syslog(LOG_INFO, "RMDIR_SVC : Removing directory : %s", *argp);
+
+ if (readonly) {
+ syslog(LOG_INFO,
+ "nis_rmdir_svc: readonly child called to rmdir, ignored.");
+ result = NIS_TRYAGAIN;
+ return (&result);
+ }
+
+ /*
+ * If 'rqstp' is NULL, we've been called internally, and supply the
+ * local principal.
+ */
+ if (rqstp == 0)
+ principal = nis_local_principal();
+
+ /* Check NIS_RMDIR access to directory */
+ if (!nis_op_access("NIS_RMDIR", 0, *argp, principal, rqstp)) {
+ if (verbose)
+ syslog(LOG_INFO,
+ "RMDIR_SVC : authorization error on \"%s\"", *argp);
+ result = NIS_PERMISSION;
+ 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.
+ */
+ res = nis_lookup(*argp, MASTER_ONLY);
+
+ /* schedule a cleanup of these results */
+ result = res->status;
+ obj = res->objects.objects_val;
+
+ if ((result != NIS_SUCCESS) && (result != NIS_NOTFOUND))
+ return (&result);
+
+ /*
+ * Now determine whether or not the object we lookup up is a
+ * directory and that we no longer serve it.
+ */
+ if (res->status == NIS_SUCCESS) {
+ if (__type_of(obj) != NIS_DIRECTORY_OBJ) {
+ result = NIS_BADOBJECT;
+ return (&result);
+ }
+
+ da = &(obj->DI_data);
+ s = nis_local_host(); /* optimization */
+ srvs = da->do_servers.do_servers_val;
+ for (i = 0; i < da->do_servers.do_servers_len; i++) {
+ if (nis_dir_cmp(srvs[i].name, s) == SAME_NAME) {
+ syslog(LOG_ERR,
+ "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.
+ */
+ fnr = db_firstib(*argp, 0, NULL,
+ FN_NORAGS+FN_NOMANGLE+FN_NOERROR, NULL);
+ result = NIS_SUCCESS;
+ if (fnr->status == NIS_NOSUCHTABLE) {
+ XFREE(fnr);
+ return (&result);
+ } else if (fnr->status == NIS_NOTFOUND) {
+ /* table is empty */
+ if ((result = db_destroy(*argp)) != NIS_SUCCESS) {
+ syslog(LOG_WARNING,
+ "nis_rmdir_svc: Could not remove directory table %s: %s.",
+ *argp, nis_sperrno(result));
+ result = NIS_S_SUCCESS;
+ }
+ make_stamp(*argp, 0); /* Make a tombstone. */
+ XFREE(fnr);
+ rmdir_update(*argp);
+ return (&result);
+ } else if (fnr->status != NIS_SUCCESS) {
+ result = fnr->status;
+ XFREE(fnr);
+ return (&result);
+ }
+ error = NIS_SUCCESS;
+ xid = begin_transaction(nis_local_principal());
+ if (! xid) {
+ XFREE(fnr->cookie.n_bytes);
+ nis_destroy_object(fnr->obj);
+ XFREE(fnr);
+ result = NIS_TRYAGAIN;
+ return (&result);
+ }
+ while (fnr->status == NIS_SUCCESS) {
+ sprintf(namebuf, "%s.%s", fnr->obj->zo_name,
+ fnr->obj->zo_domain);
+ /*
+ * If we are getting rid of a directory with subdirectories,
+ * abort.
+ */
+ if (__type_of(fnr->obj) == NIS_DIRECTORY_OBJ &&
+ complete_removal) {
+ error = NIS_NOTEMPTY;
+ break;
+ }
+ if (__type_of(fnr->obj) == NIS_TABLE_OBJ) {
+ tlist = db_firstib(namebuf, 0, NULL,
+ FN_NORAGS+FN_NOERROR, NULL);
+ while (tlist->status == NIS_SUCCESS) {
+ struct obj_list ol;
+
+ ol.o = tlist->obj;
+ ol.r = 1;
+ /* free this since we won't be using it */
+ rrs = db_remib(namebuf, 0, NULL, &ol, 1,
+ fnr->obj, (ulong_t)time(0));
+ if (rrs->status != NIS_SUCCESS) {
+ error = NIS_FAIL;
+ 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.
+ */
+ XFREE(tlist->cookie.n_bytes);
+ nis_destroy_object(tlist->obj);
+ XFREE(tlist);
+ tlist = db_firstib(namebuf, 0, NULL,
+ FN_NORAGS+FN_NOERROR, NULL);
+ }
+ if (tlist->status == NIS_SUCCESS) {
+ XFREE(tlist->cookie.n_bytes);
+ nis_destroy_object(tlist->obj);
+ }
+ if (tlist->status != NIS_NOTFOUND)
+ error = NIS_FAIL;
+ XFREE(tlist);
+ }
+ if (error != NIS_SUCCESS)
+ break;
+ /* This remove will also destroy the table */
+ dbres = db_remove(namebuf, fnr->obj, (ulong_t)time(0));
+ if (dbres->status != NIS_SUCCESS) {
+ error = dbres->status;
+ break;
+ }
+ /* free this because we aren't going to call nextib */
+ XFREE(fnr->cookie.n_bytes);
+ nis_destroy_object(fnr->obj);
+ XFREE(fnr);
+ fnr = db_firstib(*argp, 0, NULL, FN_NORAGS+FN_NOERROR, NULL);
+ }
+ if (fnr->status == NIS_SUCCESS) {
+ XFREE(fnr->cookie.n_bytes);
+ nis_destroy_object(fnr->obj);
+ }
+ if (error != NIS_SUCCESS) {
+ syslog(LOG_ERR,
+ "nis_rmdir_svc: Could not remove directory table %s: %s.",
+ *argp, nis_sperrno(error));
+ abort_transaction(xid);
+ result = error;
+ } else {
+ end_transaction(xid);
+ if ((result = db_destroy(*argp)) != NIS_SUCCESS) {
+ syslog(LOG_WARNING,
+ "nis_rmdir_svc: Could not remove directory table %s: %s.",
+ *argp, nis_sperrno(result));
+ result = NIS_S_SUCCESS;
+ }
+ make_stamp(*argp, 0); /* Make a tombstone. */
+ rmdir_update(*argp);
+ }
+
+ XFREE(fnr);
+ return (&result);
+}
+
+#undef 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
+rmdir_update(name)
+ char *name;
+{
+ nis_server *srv;
+ nis_object *rootobj = get_root_object();
+ nis_object *d_obj = NULL;
+ nis_result *lres = NULL;
+ char *parent = nis_domain_of(name);
+
+ if (rootobj &&
+ nis_dir_cmp(rootobj->DI_data.do_name, name) == SAME_NAME) {
+ parent = name;
+ d_obj = rootobj;
+ } else if (parent) {
+ lres = nis_lookup(parent, MASTER_ONLY);
+ if (lres && lres->status == NIS_SUCCESS) {
+ d_obj = lres->objects.objects_val;
+ if (__type_of(d_obj) != DIRECTORY_OBJ)
+ d_obj = NULL;
+ }
+ }
+
+ /* if we can't get the directory object, we can't do an update */
+ if (d_obj == NULL)
+ goto skip_update;
+
+ /*
+ * If we are running on the master server, or if we don't
+ * serve the parent directory, then skip the update.
+ */
+ srv = &d_obj->DI_data.do_servers.do_servers_val[0];
+ if (nis_dir_cmp(srv->name, nis_local_host()) == SAME_NAME ||
+ nis_server_control(SERVING_LIST, DIR_SERVED, parent) == 0)
+ goto skip_update;
+
+ /*
+ * If we get here, then we are a replica and we need to update
+ * the parent directory.
+ */
+ if (d_obj == rootobj) {
+ /*
+ * Updating root object.
+ */
+ root_replica_update();
+ } else {
+ /*
+ * Use a very large timestamp to force an
+ * update to the directory.
+ */
+ add_pingitem_with_name(parent, d_obj, 0xffffffff, &upd_list);
+ }
+
+skip_update:
+ if (rootobj)
+ nis_destroy_object(rootobj);
+ if (lres)
+ nis_freeresult(lres);
+
+ /* Delete name from the list of directories that we serve */
+ nis_server_control(SERVING_LIST, DIR_DELETE, name);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nisinit.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nisinit.c
new file mode 100644
index 0000000000..e292596653
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nisinit.c
@@ -0,0 +1,746 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * nisinit.c
+ *
+ * Copyright (c) 1991-1999 by Sun Microsystems, Inc.
+ * All Rights Reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This module contains the nis initialization code. It is designed to
+ * initialize a client or server. The server can be a master server or
+ * a slave server (replicate). NOTE: This file defines what the "psuedo"
+ * tables look like when defined by the server. This means that this
+ * module and the nis_db.c module must track each other. This module
+ * gets linked to nis_db.o and the database library.
+ *
+ * This is version 3 of this file, most of it has been taken over
+ * by nismkdir and the service itself.
+ * This is version 2 of this file, version 1 was a major hack this
+ * is more structured but still pretty hackish.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <string.h>
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <netdb.h>
+#include <netdir.h>
+#include <netconfig.h>
+#include <nsswitch.h>
+#include <sys/socket.h>
+#include <rpcsvc/nis.h>
+#include <netinet/in.h>
+#include "nis_svc.h"
+#include "nis_proc.h"
+
+#define COLDSTART "/var/nis/NIS_COLD_START"
+
+extern void __nis_netconfig2ep(struct netconfig *, endpoint *);
+
+extern int errno;
+extern int optind; /* getopt counter */
+extern char *optarg; /* getopt pointer */
+
+directory_obj bc_dir;
+char valid_dir_obj = 0;
+char *local_dir;
+char *secure_dir;
+
+/*
+ * get_server()
+ *
+ * This function constructs a local server description of the current
+ * server and returns it as a nis_server structure. This is then added
+ * to the list of servers who serve this directory.
+ */
+nis_server *
+get_server(host)
+ char *host;
+{
+#define INC_SIZE 512
+ int myaddr_size = INC_SIZE;
+ endpoint *myaddr;
+ static nis_server myself;
+ char hname[256];
+ int num_ep = 0, i;
+ struct netconfig *nc;
+ void *nch;
+ struct nd_hostserv hs;
+ struct nd_addrlist *addrs;
+
+ myaddr = (endpoint *) calloc(myaddr_size, sizeof (endpoint));
+ if (myaddr == NULL) {
+ fprintf(stderr, "Error: out of memory\n");
+ exit(1);
+ }
+ gethostname(hname, 256);
+ hs.h_host = (host) ? host : hname;
+ hs.h_serv = "rpcbind";
+ nch = setnetconfig();
+ while (nc = getnetconfig(nch)) {
+ if (! netdir_getbyname(nc, &hs, &addrs)) {
+ for (i = 0; i < addrs->n_cnt; i++, num_ep++) {
+ if (num_ep == myaddr_size) {
+ myaddr_size += INC_SIZE;
+ myaddr = (endpoint *) realloc(
+ (void *)myaddr,
+ myaddr_size * sizeof (endpoint));
+ if (myaddr == NULL) {
+ fprintf(stderr,
+ "Error: out of memory\n");
+ exit(1);
+ }
+ }
+ myaddr[num_ep].uaddr =
+ taddr2uaddr(nc, &(addrs->n_addrs[i]));
+ __nis_netconfig2ep(nc, &(myaddr[num_ep]));
+ }
+ netdir_free((char *)addrs, ND_ADDRLIST);
+ }
+ }
+ endnetconfig(nch);
+
+ if (! num_ep) {
+ fprintf(stderr,
+ "\nError: Unable to construct an address from name '%s'\n",
+ hs.h_host);
+ fprintf(stderr,
+ "Please check that this hostname exists in /etc/hosts\n");
+ exit(1);
+ }
+ myself.name = (host) ? strdup(host) : strdup(nis_local_host());
+ myself.ep.ep_len = num_ep;
+ myself.ep.ep_val = &myaddr[0];
+ myself.key_type = NIS_PK_NONE;
+ myself.pkey.n_bytes = NULL;
+ myself.pkey.n_len = 0;
+ return (&myself);
+}
+
+/*
+ * This function initializes a directory object. It is used when
+ * creating the object for master servers, and for creating a
+ * prototype coldstart file which is then used to build the
+ * real coldstart file. It returns false if it couldn't get
+ * a nis_server structure for the passed hostname. If this
+ * name is a null pointer it builds the directory object for
+ * the local host and cannot fail.
+ */
+void
+init_data(dd, host)
+ directory_obj *dd; /* Directory data. */
+ char *host; /* Optional host */
+{
+ dd->do_type = NIS;
+ dd->do_name = strdup(local_dir);
+ dd->do_servers.do_servers_len = 1;
+ dd->do_servers.do_servers_val = get_server(host);
+ dd->do_ttl = 12*60*60; /* this should track the object */
+ dd->do_armask.do_armask_len = 0;
+ dd->do_armask.do_armask_val = NULL;
+}
+
+static
+int
+decode_dir(res)
+ fd_result *res;
+{
+ int stat;
+ XDR mem;
+
+ if (res->status != NIS_SUCCESS)
+ return (0);
+
+ memset((char *)&bc_dir, 0, sizeof (bc_dir));
+ xdrmem_create(&mem, res->dir_data.dir_data_val,
+ res->dir_data.dir_data_len, XDR_DECODE);
+ stat = xdr_directory_obj(&mem, &bc_dir);
+ if (! stat) {
+ fprintf(stderr,
+ "\nError: Couldn't decode returned object.\n");
+ return (0);
+ }
+ if (nis_dir_cmp(local_dir, bc_dir.do_name) != SAME_NAME) {
+ xdr_free(xdr_directory_obj, (char *)&bc_dir);
+ return (0);
+ }
+
+ valid_dir_obj = 1;
+ return (1);
+}
+
+/*
+ * The bc_init_data() function is called by the callback function of
+ * the RPC broadcast function.
+ */
+bool_t
+bc_init_data(x, haddr, nc)
+ caddr_t x;
+ struct netbuf *haddr; /* host that answered : UNUSED */
+ struct netconfig *nc; /* its endpoint: UNUSED */
+{
+ fd_result *res = (fd_result *) x;
+
+ return (decode_dir(res));
+}
+
+static
+int
+check_coldstart(dir)
+ char *dir;
+{
+ directory_obj dobj;
+
+ if (!__readColdStartFile(&dobj))
+ return (1);
+
+ if (nis_dir_cmp(dobj.do_name, dir) != SAME_NAME) {
+ return (0);
+ }
+
+ return (1);
+}
+
+void
+usage(s)
+ char *s;
+{
+ fprintf(stderr,
+ "usage: \t%s [-k key_domain] -c -H host|-B|-C coldstart\n", s);
+ fprintf(stderr, "\t%s -r\n", s);
+ fprintf(stderr, "\t%s -p Y|D|N parent_domain host ...\n", s);
+ exit(1);
+}
+
+enum make_op {MAKE_NONE, MAKE_ROOT, MAKE_PARENT, MAKE_CLIENT };
+enum obj_src {SRC_NONE, SRC_BCAST, SRC_MCAST, SRC_CSTART, SRC_HNAME };
+
+void
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ enum clnt_stat dummy; /* rc for rpc_broadcast() */
+ int c; /* Option character */
+ enum name_pos pos;
+ uint32_t exptime;
+ struct stat s;
+ char buf[1024];
+ char *directory = nis_local_directory(),
+ *hostname = nis_local_host(),
+ *coldstart = NULL;
+ enum make_op op = MAKE_NONE;
+ enum obj_src src = SRC_NONE;
+ nstype ns_type = SUNYP;
+ nis_object d_obj, *n_obj;
+ int sfd, dfd, i, status, nhosts = 1;
+ directory_obj *d_data;
+ nis_server *srvs;
+ unsigned char a1, a2, a3, a4;
+ struct hostent *he;
+ struct in6_addr addr;
+ nis_result *res;
+ fd_args fdarg;
+ fd_result fdres;
+ int ss;
+ char *tmpp;
+ FILE *fw;
+ struct __nsw_switchconfig *conf;
+ enum __nsw_parse_err perr;
+ int print_warn = 0;
+ int heerr;
+ sa_family_t af;
+
+ /*
+ * Make sure that files created by stdio don't
+ * have extra permission. We allow group and world
+ * read because the files and directories we create
+ * need to be world readable.
+ */
+ (void) umask(022);
+
+ memset((char *)(&d_obj), 0, sizeof (nis_object));
+ if (geteuid() != 0) {
+ fprintf(stderr, "This program must be run as superuser.\n");
+ exit(1);
+ }
+
+ local_dir = nis_local_directory();
+
+ /*
+ * By default Solaris2.0/SunOS 5.0 is setup as an NIS (YP) client.
+ * In order to use NIS+, publickeys must be gotten from NIS+ if
+ * NIS+ is running in secure mode.
+ */
+ conf = __nsw_getconfig("publickey", &perr);
+ if ((conf == NULL) || (conf->lookups == NULL)) {
+ fprintf(stderr,
+ "Warning: There is no publickey entry in %s.\n",
+ __NSW_CONFIG_FILE);
+ fprintf(stderr,
+ "The default publickey policy is \"publickey: nis\".\n");
+ print_warn = 1;
+ } else if ((strcmp(conf->lookups->service_name, "nisplus") != 0) ||
+ (conf->lookups->next != NULL)) {
+ struct __nsw_lookup *look;
+
+ fprintf(stderr,
+ "Warning: The publickey entry in %s is \"publickey: ",
+ __NSW_CONFIG_FILE);
+ for (look = conf->lookups; look; look = look->next) {
+ fprintf(stderr, "%s", look->service_name);
+ if (look->next)
+ fprintf(stderr, " ");
+ }
+ fprintf(stderr, "\"\n");
+ print_warn = 1;
+ }
+
+ if (print_warn == 1) {
+ fprintf(stderr,
+ "In order to use NIS+, it should be \"publickey: nisplus\".\n");
+ fprintf(stderr,
+"For more information, see secure_rpc(3N), publickey(3N), & nisaddcred(1).\n");
+ }
+
+ op = MAKE_NONE;
+ src = SRC_NONE;
+ while ((c = getopt(argc, argv, "rck:p:C:BMH:")) != -1) {
+ switch (c) {
+ case 'k':
+ secure_dir = optarg;
+ break;
+ case 'r' :
+ if (op != MAKE_NONE)
+ usage(argv[0]);
+ op = MAKE_ROOT;
+ break;
+ case 'c':
+ if (op != MAKE_NONE)
+ usage(argv[0]);
+ op = MAKE_CLIENT;
+ break;
+ case 'p' :
+ if (op != MAKE_NONE)
+ usage(argv[0]);
+ if (*optarg == '-') /* skip '-' character */
+ optarg++;
+ switch (*optarg) {
+ case 'Y' :
+ ns_type = SUNYP;
+ break;
+ case 'D' :
+ ns_type = DNS;
+ break;
+ case 'N' :
+ ns_type = NIS;
+ break;
+ default :
+ fprintf(stderr,
+ "unrecognized name service type, use one of:\n");
+ fprintf(stderr,
+ " Y = Sun YP\n D = Domain Name Service\n");
+ fprintf(stderr,
+ " N = Sun NIS+\n");
+ usage(argv[0]);
+ }
+ op = MAKE_PARENT;
+ break;
+ case 'C' :
+ if (src != SRC_NONE)
+ usage(argv[0]);
+ src = SRC_CSTART;
+ coldstart = optarg;
+ break;
+ case 'H' :
+ if (src != SRC_NONE)
+ usage(argv[0]);
+ src = SRC_HNAME;
+ hostname = optarg;
+ break;
+ case 'B' :
+ if (src != SRC_NONE)
+ usage(argv[0]);
+ src = SRC_BCAST;
+ break;
+#ifdef NIS_MCAST_SUPPORT
+ case 'M' :
+ if (src != SRC_NONE)
+ usage(argv[0]);
+ src = SRC_MCAST;
+ break;
+#endif
+ case '?' :
+ default :
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ if (op == MAKE_NONE) {
+ fprintf(stderr, "One of -c, -r or -p must be specified.\n");
+ usage(argv[0]);
+ }
+
+ /* Only making the parent requires extra data (host name) */
+ if ((optind < argc) && (op != MAKE_PARENT)) {
+ fprintf(stderr,
+ "Error: Extra input at the end of the command.\n");
+ usage(argv[0]);
+ } else if ((optind <= argc) && (op == MAKE_PARENT)) {
+ if ((argc - optind) < 2) {
+ fprintf(stderr,
+"Error: Make parent (-p) requires parent domain and at least one host.\n");
+ usage(argv[0]);
+ }
+ directory = argv[optind++];
+ nhosts = argc - optind;
+ }
+
+ /* /var/nis directory should exist. If not, create it. */
+ strcpy(buf, nis_data(NULL));
+ tmpp = strrchr(buf, '/');
+ *tmpp = NULL;
+ ss = stat(buf, &s);
+ if (ss == -1 && errno == ENOENT) {
+ if (mkdir(buf, 0755)) {
+ perror("mkdir");
+ fprintf(stderr, "Error: Unable to create \"%s\"\n",
+ buf);
+ exit(1);
+ }
+ } else if (ss == -1) {
+ perror("stat");
+ fprintf(stderr, "Error: Unable to stat \"%s\"\n", buf);
+ exit(1);
+ }
+
+ /*
+ * For root and parent operations, the /var/nis directory should
+ * exist with the correct permissions.
+ */
+ switch (op) {
+ case MAKE_ROOT:
+ case MAKE_PARENT:
+ strcpy(buf, nis_data(NULL));
+ ss = stat(buf, &s);
+ if (ss == -1 && errno == ENOENT) {
+ if (op == MAKE_PARENT) {
+ fprintf(stderr,
+"Error: Parent object cannot be created before creating a root object.\n");
+ exit(1);
+ }
+ if (mkdir(buf, 0744)) {
+ perror("mkdir");
+ fprintf(stderr, "Error: Unable to create "
+ "\"%s\"\n", buf);
+ exit(1);
+ }
+ } else if (ss == -1) {
+ perror("stat");
+ fprintf(stderr, "Error: Unable to stat \"%s\"\n", buf);
+ exit(1);
+ } else {
+ if ((s.st_mode & 0777) != 0744) {
+ fprintf(stderr,
+ "Warning: Bad permissions (%o) on the %s directory.\n",
+ s.st_mode & 0777, buf);
+ fprintf(stderr, "Should have been 744\n\n");
+ }
+ if (op == MAKE_ROOT)
+ /* Some data is already in this directory */
+ fprintf(stderr,
+ "Warning: Old data exists under "
+ "the \"%s\" directory.\n\n", buf);
+ else {
+ /* make sure that the root_object is there */
+ ss = stat(nis_data(ROOT_OBJ), &s);
+ if (ss == -1 && errno == ENOENT) {
+ fprintf(stderr,
+"Error: Parent object cannot be created before creating a root object.\n");
+ exit(1);
+ } else if (ss == -1) {
+ perror("stat");
+ fprintf(stderr,
+ "Error: Unable to stat \"%s\"\n",
+ nis_data(ROOT_OBJ));
+ exit(1);
+ }
+ }
+ }
+ }
+
+ printf(
+ "This machine is in the \"%s\" NIS+ domain.\n", local_dir);
+ switch (op) {
+ case MAKE_ROOT :
+ printf("Setting up root server ...\n");
+ /*
+ * Step 0. See if we have the info we need.
+ */
+
+ /*
+ * Step 1. Create Directory object
+ * Since we are the master server, the d_obj should
+ * not pre-exist.
+ */
+ d_obj.zo_oid.ctime = time(0);
+ d_obj.zo_name = strdup(nis_leaf_of(directory));
+ d_obj.zo_domain = nis_domain_of(directory);
+ d_obj.zo_owner = nis_local_principal();
+ d_obj.zo_group = nis_local_group();
+ /* Make this object readable by nobody */
+ d_obj.zo_access = DEFAULT_RIGHTS | (NIS_READ_ACC << 24);
+ if (*d_obj.zo_group != NULL)
+ d_obj.zo_access |= ((NIS_READ_ACC +
+ NIS_MODIFY_ACC + NIS_CREATE_ACC +
+ NIS_DESTROY_ACC) << 8);
+ d_obj.zo_ttl = 24 * 60 * 60;
+ d_obj.zo_data.zo_type = NIS_DIRECTORY_OBJ;
+ d_data = &(d_obj.DI_data);
+ init_data(d_data, (char *)NULL);
+ strcpy(buf, nis_data(ROOT_OBJ));
+ status = nis_write_obj(buf, &d_obj);
+ if (! status) {
+ fprintf(stderr,
+ "\nError: Unable to write root object.\n");
+ exit(1);
+ }
+ /* Write cold start file */
+ __nis_CacheRestart(); /* in case cachemgr running */
+ unlink(COLDSTART);
+ writeColdStartFile(d_data);
+ __nis_CacheRestart();
+
+ /* Create the serving list file */
+ strcpy(buf, nis_data("serving_list"));
+ fw = fopen(buf, "a+");
+ if (fw == NULL) {
+ fprintf(stderr,
+ "\nERROR: could not open file \"%s\" for storing directories served.\n",
+ buf);
+ exit(1);
+ }
+ fprintf(fw, "%s\n", directory);
+ fclose(fw);
+
+ break;
+ case MAKE_PARENT :
+ printf("Setting up parent object ...\n");
+ /*
+ * Step 1. Create Directory object
+ * Since we are the master server, the d_obj should
+ * not pre-exist.
+ */
+ strcpy(buf, nis_domain_of(local_dir));
+ d_obj.zo_name = strdup(nis_leaf_of(buf));
+ d_obj.zo_domain = strdup(nis_domain_of(buf));
+ d_obj.zo_owner = strdup(nis_local_principal());
+ d_obj.zo_group = "";
+ d_obj.zo_access = 0x01010101; /* r---r---r---r--- */
+ d_obj.zo_ttl = 7*24*3600;
+
+ d_obj.zo_data.zo_type = NIS_DIRECTORY_OBJ;
+ d_data = &(d_obj.DI_data);
+ d_data->do_type = ns_type;
+ d_data->do_name = directory;
+ d_data->do_servers.do_servers_len = nhosts;
+ d_data->do_servers.do_servers_val = (nis_server *)
+ calloc(nhosts, sizeof (nis_server));
+ srvs = d_data->do_servers.do_servers_val;
+ for (i = 0; i < nhosts; i++) {
+ extern char *inet_ntoa(struct in_addr);
+ srvs[i].name = strdup(argv[optind + i]);
+ srvs[i].key_type = NIS_PK_NONE;
+ srvs[i].ep.ep_len = 1;
+ srvs[i].ep.ep_val = (endpoint *)
+ calloc(1, sizeof (endpoint));
+ he = getipnodebyname(srvs[i].name, AF_INET6,
+ AI_DEFAULT, &heerr);
+ if (! he) {
+ fprintf(stderr,
+ "\nError: Couldn't locate address information for \"%s\".\n",
+ srvs[i].name);
+ exit(1);
+ }
+ addr =
+ ((struct sockaddr_in6 *)(he->h_addr_list[0]))->sin6_addr;
+ if (IN6_IS_ADDR_V4MAPPED(&addr)) {
+ struct in_addr in4;
+ af = AF_INET;
+ IN6_V4MAPPED_TO_INADDR(&addr, &in4);
+ strcpy(buf, inet_ntoa(in4));
+ } else {
+ af = AF_INET6;
+ (void) inet_ntop(AF_INET6, &addr,
+ buf, sizeof (buf));
+ }
+ switch (d_data->do_type) {
+ case NIS:
+ case SUNYP:
+ strcat(buf, ".0.111");
+ break;
+ case DNS:
+ strcat(buf, ".0.53");
+ break;
+ }
+ srvs[i].ep.ep_val->uaddr = strdup(buf);
+ srvs[i].ep.ep_val->family =
+ (af == AF_INET6) ? NC_INET6 : NC_INET;
+ if (d_data->do_type != NIS)
+ srvs[i].ep.ep_val->proto =
+ (af == AF_INET6) ? "udp6" : "udp";
+ else
+ srvs[i].ep.ep_val->proto =
+ (af == AF_INET6) ? "tcp6" : "tcp";
+ }
+ d_data->do_ttl = d_obj.zo_ttl;
+ d_data->do_armask.do_armask_len = 0;
+ d_data->do_armask.do_armask_val = NULL;
+ strcpy(buf, nis_data(PARENT_OBJ));
+ status = nis_write_obj(buf, &d_obj);
+ if (! status) {
+ fprintf(stderr,
+ "\nError: Unable to write parent object.\n");
+ exit(1);
+ }
+ break;
+ case MAKE_CLIENT :
+ printf("Setting up NIS+ client ...\n");
+ if (secure_dir == NULL) {
+ if (!check_coldstart(local_dir)) {
+ fprintf(stderr,
+ "\nError: system domain name doesn't match that stored in \"%s\"\n",
+ COLDSTART);
+ fprintf(stderr,
+ " use the -k option to specify the domain where root's");
+ fprintf(stderr,
+ " key is stored.\n");
+ exit(1);
+ }
+ } else {
+ pos = nis_dir_cmp(local_dir, secure_dir);
+ if (pos != SAME_NAME && pos != LOWER_NAME) {
+ fprintf(stderr,
+ "\nError: system domain name must be the same as or lower than the\n");
+ fprintf(stderr,
+ " domain in which root's key is stored.\n");
+ exit(1);
+ }
+ local_dir = secure_dir;
+ }
+ (void) unlink(COLDSTART); /* just in case */
+ switch (src) {
+ case SRC_NONE :
+ fprintf(stderr,
+ "\nError: Missing source for client setup.\n");
+ usage(argv[0]);
+ break;
+ case SRC_HNAME :
+ d_data = &(d_obj.DI_data);
+ init_data(d_data, hostname);
+ writeColdStartFile(d_data);
+ break;
+ case SRC_CSTART :
+ sfd = open(coldstart, O_RDONLY, 0);
+ if (sfd == -1) {
+ fprintf(stderr,
+ "\nError: Can't open file \"%s\" "
+ "for reading.\n", coldstart);
+ exit(1);
+ }
+ dfd = open(COLDSTART, O_WRONLY+O_CREAT, 0644);
+ if (dfd == -1) {
+ fprintf(stderr,
+ "\nError: Can't open file \"%s\" "
+ "for writing.\n", COLDSTART);
+ exit(1);
+ }
+ while ((i = read(sfd, buf, 1024)) > 0)
+ write(dfd, buf, i);
+ close(dfd);
+ close(sfd);
+ break;
+ case SRC_BCAST :
+ fdarg.dir_name = local_dir;
+ fdarg.requester = "broadcast";
+ memset((char *)&fdres, 0, sizeof (fdres));
+ dummy = rpc_broadcast(NIS_PROG, NIS_VERSION,
+ NIS_FINDDIRECTORY,
+ xdr_fd_args, (char *)&fdarg,
+ xdr_fd_result, (char *)&fdres,
+ (resultproc_t)bc_init_data,
+ NULL);
+ if ((fdres.status == NIS_SUCCESS) &&
+ (dummy == RPC_SUCCESS) &&
+ (valid_dir_obj))
+ writeColdStartFile(&bc_dir);
+ else {
+ fprintf(stderr,
+ "\nError: No servers responding, use -H option \n");
+ exit(1);
+ }
+ break;
+ case SRC_MCAST :
+ break;
+ default :
+ break;
+ }
+
+ /*
+ * At this point there should be something of a
+ * coldstart file in the /var/nis directory. If
+ * not then we're hosed.
+ *
+ * We switch to a local cache so that there is
+ * no conflict if nis_cachemgr is still running
+ * with a copy of an old domain name.
+ */
+ __nis_CacheLocalInit(&exptime);
+ unlink(COLDSTART);
+ res = nis_lookup(local_dir, NO_AUTHINFO);
+ if (res->status != NIS_SUCCESS) {
+ fprintf(stderr,
+ "\nError: Could not create a valid NIS+ coldstart file\n");
+ nis_perror(res->status, local_dir);
+ exit(1);
+ }
+ n_obj = res->objects.objects_val;
+ writeColdStartFile(&(n_obj->DI_data));
+ __nis_CacheRestart();
+ printf("All done.\n");
+ exit(0);
+ default :
+ printf("\n");
+ usage(argv[0]);
+ }
+ printf("All done.\n");
+ exit(0);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nisldapmaptest.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nisldapmaptest.c
new file mode 100644
index 0000000000..f81e534b77
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nisldapmaptest.c
@@ -0,0 +1,389 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <strings.h>
+#include <malloc.h>
+
+#include <lber.h>
+#include <ldap.h>
+
+#include <sys/param.h>
+#include <rpcsvc/nis.h>
+#include "nis_proc.h"
+#include "nis_ldap.h"
+
+#include "ldap_util.h"
+#include "ldap_structs.h"
+#include "ldap_print.h"
+#include "ldap_attr.h"
+#include "ldap_nisdbquery.h"
+#include "ldap_ruleval.h"
+#include "ldap_op.h"
+#include "ldap_map.h"
+#include "ldap_nisplus.h"
+
+#include "nis_parse_ldap_conf.h"
+
+
+/* Avoid having to link in nis_cleanup.o */
+void
+do_cleanup(cleanup *stuff) {
+}
+
+/* Parser assumes existence of 'verbose' */
+int verbose = 0;
+/* ... as well as 'cons' */
+FILE *cons = stdout;
+
+/* 'justTesting' means we can live with some fudging */
+int justTesting = 0;
+
+/* 'setColumnsDuringConfig' is OK for us */
+int setColumnsDuringConfig = 1;
+
+
+void
+usage(char *prog) {
+ fprintf(stderr,
+ "Usage: %s [ -s | -r | -d ] ", prog);
+ fprintf(stderr, "[ -l | -t table ] [ -v ] ");
+ fprintf(stderr, "[ -i ] [ -o ] ");
+ fprintf(stderr, "[ -m conffile ] ");
+ fprintf(stderr, "[ -x attr=val ... ] ");
+ fprintf(stderr, "[ col=val ...]\n");
+}
+
+int
+main(int argc, char *argv[]) {
+
+ int ret, i, j, numVals, numEntries = 0, nq = 0;
+ __nis_table_mapping_t *t;
+ db_query **q, **qr;
+ int modify = 0, delete = 0, list = 0, asObj = 0;
+ char *myself = "main";
+ char *table = 0;
+ int freeTable = 0;
+ char *ldapConfFile = 0;
+ char **ldapCLA = 0;
+ int numLA = 0;
+ int c, saveVerbose;
+ __nis_obj_attr_t *attr;
+ entry_obj **ea = 0;
+ int numEa;
+
+ while ((c = getopt(argc, argv, "srdlviom:x:t:")) != -1) {
+ switch (c) {
+ case 's':
+ /* Search */
+ modify = 0;
+ delete = 0;
+ break;
+ case 'r':
+ /* Replace/Modify/Add */
+ modify = 1;
+ delete = 0;
+ break;
+ case 'd':
+ /* Delete */
+ modify = 0;
+ delete = 1;
+ break;
+ case 'l':
+ /* List parser structures */
+ list = 1;
+ break;
+ case 'v':
+ /* Verbose mode */
+ verbose = 1;
+ break;
+ case 'i':
+ /*
+ * Allow fudging (i.e., guessing) at information
+ * that can't be retrieved (such as column names).
+ */
+ justTesting = 1;
+ break;
+ case 'o':
+ /* Work on object; affects tables */
+ asObj = 1;
+ break;
+ case 'm':
+ /* Config file name */
+ ldapConfFile = optarg;
+ break;
+ case 'x':
+ /* Attribute assignment */
+ ldapCLA = realloc(ldapCLA,
+ (numLA + 2) * sizeof (ldapCLA[0]));
+ if (ldapCLA == 0) {
+ fprintf(stderr,
+ "Out of memory. realloc(%d) => NULL\n",
+ (numLA + 2) * sizeof (ldapCLA[0]));
+ return (-1);
+ }
+ ldapCLA[numLA++] = optarg;
+ ldapCLA[numLA] = 0;
+ break;
+ case 't':
+ /* NIS+ object */
+ table = optarg;
+ break;
+ case '?':
+ default:
+ usage(argv[0]);
+ return (-1);
+ }
+ }
+
+ /*
+ * Make /var/nis our CWD, so that config files without a dir
+ * path get the correct default.
+ */
+#define OURCWD "/var/nis"
+ if (chdir(OURCWD) != 0) {
+ fprintf(stderr, "Failure setting CWD to ");
+ perror(OURCWD);
+ }
+
+ /*
+ * If 'verbose' is on, the parser spits out a listing of the
+ * mapping config file. However, since nisldapmaptest has an
+ * option specifically for that purpose, we unset 'verbose'
+ * while parsing.
+ */
+ saveVerbose = verbose;
+ verbose = 0;
+ ret = parseConfig(ldapCLA, ldapConfFile);
+ verbose = saveVerbose;
+ if (ret == 1) {
+ fprintf(stderr, "Mapping inactive\n");
+ return (1);
+ } else if (ret != 0) {
+ fprintf(stderr, "Parse error for \"%s\" => %d\n",
+ ldapConfFile ? ldapConfFile :
+ "/var/nis/NIS+LDAPmapping",
+ ret);
+ return (ret);
+ }
+
+ if (list) {
+ /* Don't bother locking the table for traversal */
+ for (t = (__nis_table_mapping_t *)ldapMappingList.first;
+ t != 0;
+ t = (__nis_table_mapping_t *)t->item.nxt_item) {
+ __nis_table_mapping_t *m;
+ for (m = t; m != 0; m = m->next) {
+ printTableMapping(m);
+ printbuf();
+ }
+ }
+ }
+
+ if (table != 0) {
+ int len = 0;
+ char *objPath = 0;
+
+ table = fullObjName(F, table);
+ if (table != 0) {
+ freeTable = 1;
+ len = strlen(table);
+ objPath = calloc(1, len + MAXPATHLEN + 1);
+ }
+ if (table == 0 || objPath == 0 ||
+ internal_table_name(table, objPath) == 0) {
+ fprintf(stderr,
+ "Unable to obtain internal object name for \"%s\"\n",
+ table);
+ if (freeTable)
+ free(table);
+ return (-1);
+ }
+ t = __nis_find_item_mt(objPath, &ldapMappingList, 1, 0);
+ if (t == 0) {
+ fprintf(stderr, "No mapping for \"%s\" (%s)\n",
+ table, NIL(objPath));
+ if (objPath != 0)
+ free(objPath);
+ if (freeTable)
+ free(table);
+ return (-1);
+ }
+ if (objPath != 0)
+ free(objPath);
+ } else {
+ if (list)
+ exit(0);
+ usage(argv[0]);
+ exit(-1);
+ }
+
+ if (asObj || t->objType != NIS_TABLE_OBJ) {
+ char *op;
+ nis_object *no = 0;
+
+ /*
+ * Work on object (even if it's a table); ignore excess
+ * arguments
+ */
+ if (modify) {
+ ret = objToLDAP(t, 0, 0, 0);
+ op = "objToLDAP";
+ } else if (delete) {
+ ret = deleteLDAPobj(t);
+ op = "deleteLDAPobj";
+ } else {
+ ret = objFromLDAP(t, &no, &ea, &numEa);
+ op = "objFromLDAP";
+ }
+ if (ret == LDAP_SUCCESS) {
+ if (no != 0) {
+ nis_print_object(no);
+ if (no->zo_data.zo_type == NIS_DIRECTORY_OBJ &&
+ ea != 0) {
+ p2buf(myself, "Directory entries:\n");
+ for (i = 0; i < numEa; i++) {
+ p2buf(myself, "\t");
+ sc2buf(myself,
+ ea[i]->en_cols.en_cols_val[1].ec_value.ec_value_val,
+ ea[i]->en_cols.en_cols_val[1].ec_value.ec_value_len);
+ p2buf(myself, "\n");
+ }
+ }
+ nis_destroy_object(no);
+ }
+ if (ea != 0)
+ freeEntryObjArray(ea, numEa);
+ } else {
+ p2buf(myself, "%s(\"%s\") => %d (%s)\n",
+ op, t->objName, ret,
+ ldap_err2string(ret));
+ }
+ printbuf();
+ } else if (optind < argc) {
+ __nis_rule_value_t *rv = 0;
+ int nv = 0;
+ __nis_obj_attr_t **attr = 0;
+ /*
+ * Non-option arguments specify column names/values in
+ * "name=value" format.
+ */
+ q = createQuery(argc-optind, &argv[optind], t,
+ (modify || delete) ? &rv : 0, &nv);
+ if (q == 0) {
+ p2buf(myself, "Unable to create LDAP %s parameters\n",
+ modify ? "modify" :
+ (delete ? "delete" : "search"));
+ if (freeTable)
+ free(table);
+ exit(-1);
+ }
+ if (modify) {
+ ret = mapToLDAP(t, nv, 0, q, rv, 0, 0);
+ if (ret != LDAP_SUCCESS) {
+ p2buf(myself,
+ "mapToLDAP(<modify>) => %d (%s)\n",
+ ret, ldap_err2string(ret));
+ }
+ } else if (delete) {
+ ret = mapToLDAP(t, nv, q, 0, rv, 0, 0);
+ if (ret != LDAP_SUCCESS) {
+ p2buf(myself,
+ "mapToLDAP(<delete>) => %d (%s)\n",
+ ret, ldap_err2string(ret));
+ }
+ } else {
+ qr = mapFromLDAP(t, q[0], &nq, 0, &ret, &attr);
+ if (qr != 0) {
+ for (i = 0; i < nq; i++) {
+ printQuery(qr[i], t);
+ printbuf();
+ freeQuery(qr[i]);
+ if (attr != 0) {
+ printObjAttr(attr[i]);
+ }
+ }
+ free(qr);
+ freeObjAttr(attr, nq);
+ } else if (ret != LDAP_SUCCESS) {
+ p2buf(myself,
+ "mapFromLDAP() => %d (%s)\n",
+ ret, ldap_err2string(ret));
+ }
+ }
+ for (i = 0; i < nv; i++)
+ freeQuery(q[i]);
+ freeRuleValue(rv, nv);
+ printbuf();
+ } else if (!modify && !delete) {
+ __nis_obj_attr_t **attr = 0;
+ /* Enumeration */
+ qr = mapFromLDAP(t, 0, &nq, 0, &ret, &attr);
+ if (qr != 0) {
+ for (i = 0; i < nq; i++) {
+ printQuery(qr[i], t);
+ printbuf();
+ freeQuery(qr[i]);
+ if (attr != 0) {
+ printObjAttr(attr[i]);
+ }
+ }
+ freeObjAttr(attr, nq);
+ free(qr);
+ } else if (ret != LDAP_SUCCESS) {
+ p2buf(myself,
+ "mapFromLDAP() => %d (%s)\n",
+ ret, ldap_err2string(ret));
+ }
+ printbuf();
+ } else {
+ usage(argv[0]);
+ }
+
+ if (freeTable)
+ free(table);
+ if (t != 0)
+ __nis_release_item(t, &ldapMappingList, 1);
+
+ if (numMisaligned > 0)
+ fprintf(stderr, "numMisaligned = %d\n", numMisaligned);
+
+ return (0);
+}
+
+/*
+ * As long as we're single-threaded, we need a private version of
+ * assertExclusive that always returns success.
+ */
+int
+assertExclusive(void *unused) {
+ return (1);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/nislog.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nislog.c
new file mode 100644
index 0000000000..3a0f8c125e
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/nislog.c
@@ -0,0 +1,290 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2001 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * nislog.c
+ *
+ * This module simply reads the internal format of the log and prints
+ * it out for the user to stdout. The format of the transaction log
+ * is an *INTERNAL* interface and will change from time to time. The
+ * service uses the log specific functions defined in the nis_log.c
+ * module. They are reproduced here for this test code.
+ *
+ */
+
+#include <stdio.h>
+#include <syslog.h>
+#include <values.h> /* MAXINT define */
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <signal.h>
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpcsvc/nis.h>
+#include "log.h"
+
+/*
+ * nislog(1M) doesn't use 'cons', but we need it defined following the
+ * code rearrangements for the MT rpc.nisd.
+ */
+FILE *cons = NULL;
+
+extern unsigned long __maxloglen;
+
+extern int optind, opterr;
+extern char *optarg;
+int verbose = 0;
+/*
+ * This macro returns true if the two objects have identical OIDs
+ */
+#define sameobj(o1, o2) (((o1)->zo_oid.ctime == (o2)->zo_oid.ctime) && \
+ ((o1)->zo_oid.mtime == (o2)->zo_oid.mtime))
+
+int
+abort_transaction(xid)
+ int xid;
+{
+ return (0);
+}
+
+
+void
+print_transaction(cur, num)
+ log_upd *cur;
+ int num;
+{
+ XDR xdrs;
+ log_entry le;
+
+ printf("@@@@@@@@@@@@@@@@ Transaction @@@@@@@@@@@@@@@@@@\n");
+ printf("#%05d, XID : %d\n", num, cur->lu_xid);
+ printf("Time : %s\n", ctime((time_t *)&(cur->lu_time)));
+ printf("Directory : %s\n", cur->lu_dirname);
+ memset((char *)&le, 0, sizeof (le));
+ xdrmem_create(&xdrs, (char *)(cur->lu_data), cur->lu_size, XDR_DECODE);
+ xdr_log_entry(&xdrs, &le);
+ printf("Entry type : ");
+ switch (le.le_type) {
+ case ADD_NAME :
+ printf("ADD Name\n");
+ break;
+ case REM_NAME :
+ printf("REMOVE Name\n");
+ break;
+ case ADD_IBASE :
+ printf("ADD Entry\n");
+ break;
+ case REM_IBASE :
+ printf("REMOVE Entry\n");
+ break;
+ case MOD_NAME_OLD :
+ printf("MODIFY (Original Value)\n");
+ break;
+ case MOD_NAME_NEW :
+ printf("MODIFY (New Value)\n");
+ break;
+ case UPD_STAMP :
+ printf("UPDATE time stamp.\n");
+ break;
+ default:
+ printf("Unknown (%d)!\n", le.le_type);
+ break;
+ }
+ printf("Entry timestamp : %s", ctime((time_t *)&(le.le_time)));
+ printf("Principal : %s\n", le.le_princp);
+ printf("Object name : %s\n", __make_name(&le));
+ printf(".................. Object .....................\n");
+ nis_print_object(&le.le_object);
+ printf("...............................................\n");
+ xdr_free(xdr_log_entry, (char *)&le);
+}
+
+static
+void
+buserr_exit()
+{
+ printf("Transaction log checkpointed while reading.\n");
+ exit(0);
+}
+
+usage(s)
+ char *s;
+{
+ if ((strcmp(s, "loghead") == 0) || (strcmp(s, "logtail") == 0))
+ fprintf(stderr, "usage: nislog %s [-v] num\n", s);
+ else
+ fprintf(stderr, "usage: %s [-h [num] | -t [num] ] [-v] \n", s);
+ exit(1);
+}
+
+char *directories[128];
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ log_upd *cur;
+ char **dir;
+ int entries = MAXINT;
+ char *cmd;
+ int i, c;
+ struct sigaction sa;
+ int tail_only = 0;
+ char logname[NIS_MAXNAMELEN];
+
+ if (geteuid() != (uid_t)0) {
+ fprintf(stderr, "nislog must be run as root.\n");
+ exit(1);
+ }
+
+ memset(logname, 0, sizeof (logname));
+ printf("NIS Log printing facility.\n");
+ opterr = 0;
+ /*
+ * if the cmdname is "logtail" or "loghead" change the
+ * arguments a bit, any other name and we default to
+ * "nislog" behaviour.
+ */
+ cmd = (char *) strrchr(argv[0], '/');
+ if (! cmd)
+ cmd = argv[0];
+ if ((strcmp(cmd, "logtail") == 0) ||
+ (strcmp(cmd, "loghead") == 0)) {
+ if (strcmp(cmd, "logtail") == 0)
+ tail_only = 1;
+
+ while ((c = getopt(argc, argv, "v")) != -1) {
+ switch (c) {
+ case 'v' :
+ verbose = 1;
+ break;
+ case '?' :
+ usage(cmd);
+ break;
+ }
+ }
+ if (optind < argc) {
+ entries = atoi(argv[optind]);
+ optind++;
+ }
+ i = 0;
+ while (optind < argc)
+ directories[i++] = argv[optind++];
+ } else {
+ while ((c = getopt(argc, argv, "h:t:v")) != -1) {
+ switch (c) {
+ case 'h' :
+ entries = atoi(optarg);
+ break;
+ case 't' :
+ tail_only = 1;
+ entries = atoi(optarg);
+ break;
+ case 'v' :
+ verbose = 1;
+ break;
+ case '?' :
+ usage(cmd);
+ break;
+ }
+ }
+ i = 0;
+ while (optind < argc)
+ directories[i++] = argv[optind++];
+ }
+
+ /*
+ * If we have a long transaction log mmapped and rpc.nisd
+ * truncates the log before we have hit all of the pages,
+ * then when we hit a page past the current end of file for
+ * the first time, we will get a SIGBUS. We simple set a
+ * signal handler to catch the signal and exit with status 0.
+ */
+ memset((char *)&sa, 0, sizeof (sa));
+ sa.sa_handler = buserr_exit;
+ sigaction(SIGBUS, &sa, NULL);
+
+ sprintf(logname, "%s", LOG_FILE);
+ if (map_log(logname, FNISLOG)) {
+ fprintf(stderr, "Unable to map log!\n");
+ exit(1);
+ }
+ printf("NIS Log dump :\n");
+ printf("\tLog state : ");
+ switch (__nis_log->lh_state) {
+
+ case LOG_STABLE :
+ printf("STABLE.\n");
+ break;
+ case LOG_RESYNC :
+ printf("RESYNCING.\n");
+ break;
+ case LOG_UPDATE :
+ printf("UPDATING.\n");
+ break;
+ case LOG_CHECKPOINT :
+ printf("CHECKPOINTING.\n");
+ break;
+ }
+ printf("Number of updates : %d\n", __nis_log->lh_num);
+ printf("Current XID : %d\n", __nis_log->lh_xid);
+ printf("Size of Log in bytes : %d\n",
+ (__nis_log->lh_tail) ? LOG_SIZE(__nis_log) : sizeof (log_hdr));
+ printf("*** UPDATES ***\n");
+ if (! __nis_log->lh_num) {
+ printf("--None--\n");
+ exit(0);
+ }
+ dir = directories;
+ do {
+ cur = (tail_only) ? __nis_log->lh_tail : __nis_log->lh_head;
+
+ if (tail_only) {
+ for (i = 0; cur && (i < entries); cur = cur->lu_prev) {
+ if ((*dir) &&
+ (nis_dir_cmp(*dir, cur->lu_dirname)
+ != SAME_NAME))
+ continue;
+ print_transaction(cur, __nis_log->lh_num-i-1);
+ i++;
+ }
+ } else {
+ for (i = 0; cur && (i < entries); cur = cur->lu_next) {
+ if ((*dir) &&
+ (nis_dir_cmp((*dir), cur->lu_dirname)
+ != SAME_NAME))
+ continue;
+ print_transaction(cur, i);
+ i++;
+ }
+ }
+ dir++;
+ } while (*dir != NULL);
+ exit(0);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/req.flg b/usr/src/cmd/rpcsvc/nis/rpc.nisd/req.flg
new file mode 100644
index 0000000000..6bf46e9493
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/req.flg
@@ -0,0 +1,50 @@
+#!/bin/sh
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+
+
+echo_file usr/src/lib/libnsl/include/rpcsvc/nis_dhext.h
+echo_file usr/src/lib/libnisdb/nisdb_ldap.h
+echo_file usr/src/lib/libnisdb/ldap_attr.h
+echo_file usr/src/lib/libnisdb/ldap_map.h
+echo_file usr/src/lib/libnisdb/ldap_nisdbquery.h
+echo_file usr/src/lib/libnisdb/ldap_nisplus.h
+echo_file usr/src/lib/libnisdb/ldap_op.h
+echo_file usr/src/lib/libnisdb/ldap_parse.h
+echo_file usr/src/lib/libnisdb/ldap_print.h
+echo_file usr/src/lib/libnisdb/ldap_ruleval.h
+echo_file usr/src/lib/libnisdb/ldap_structs.h
+echo_file usr/src/lib/libnisdb/ldap_util.h
+echo_file usr/src/lib/libnisdb/ldap_xdr.h
+echo_file usr/src/lib/libnisdb/nis_hashitem.h
+echo_file usr/src/lib/libnisdb/nis_ldap.h
+echo_file usr/src/lib/libnisdb/nis_parse_ldap_conf.h
+echo_file usr/src/lib/libnisdb/nis_servlist.h
+echo_file usr/src/lib/libnisdb/nisdb_ldap.h
+echo_file usr/src/lib/libnisdb/nisdb_mt.h
+echo_file usr/src/lib/libnisdb/nisdb_rw.h
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv.c
new file mode 100644
index 0000000000..5bafc52962
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv.c
@@ -0,0 +1,405 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 1993-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <rpc/rpc.h>
+#include <syslog.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <errno.h>
+#include <sys/systeminfo.h>
+#include <netconfig.h>
+#include <netdir.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/nis.h>
+#include "resolv_common.h"
+#include "nis_proc.h"
+
+#define YPDNSVERS 2L
+#define RESOLV_EXEC_PATH "/usr/sbin/rpc.nisd_resolv"
+#define RESOLV_EXEC_ERR "can't exec /usr/sbin/rpc.nisd_resolv: %s\n"
+
+/*
+ * Need to protect against inadvertently starting more than one rpc.nis_resolv.
+ * Thus, apply this lock before checking if rpc.nisd_resolv is running,
+ * and hold it until setup_resolv() (if called) has completed.
+ */
+DECLMUTEXLOCK(setup_resolv);
+
+extern int verbose;
+
+void
+setup_resolv(fwding, child, client, tp_type, prognum)
+int *fwding;
+int *child;
+CLIENT **client;
+char *tp_type;
+long prognum; /* use transient if this not set */
+{
+ enum clnt_stat stat;
+ struct timeval tv;
+ char prog_str[15], fd_str[5];
+ SVCXPRT *xprt = NULL;
+ char *tp;
+ char name[257];
+ struct netconfig *nc;
+ void *h;
+
+ if (!*fwding)
+ return;
+
+ /* try the specified netid (default ticots), then any loopback */
+ tp = (tp_type && *tp_type) ? tp_type : "ticots";
+ if (!getconf(tp, &h, &nc)) { /* dont forget endnetconfig() */
+ syslog(LOG_ERR, "can't get resolv_clnt netconf %s.\n", tp);
+ *fwding = FALSE;
+ return;
+ }
+ tp = nc->nc_netid;
+
+ /*
+ * Startup the resolv server: use transient prognum if prognum
+ * isn't set. Using transient means we create mapping then
+ * pass child the fd to use for service.
+ */
+ if (!getprognum(&prognum, &xprt, fd_str, prog_str, YPDNSVERS, tp)) {
+ syslog(LOG_ERR, "can't create resolv xprt for transient.\n");
+ *fwding = FALSE;
+ endnetconfig(h);
+ return;
+ }
+ switch (*child = vfork()) {
+ case -1: /* error */
+ syslog(LOG_ERR, "can't startup resolv daemon\n");
+ endnetconfig(h);
+ *fwding = FALSE;
+ return;
+ case 0: /* child */
+ /*
+ * if using transient we must maintain fd across
+ * exec cause unset/set on prognum isn't automic.
+ *
+ * if using transient we'll just do svc_tli_create
+ * in child on our bound fd.
+ */
+ execlp(RESOLV_EXEC_PATH, "rpc.nisd_resolv",
+ "-F", /* forground */
+ "-C", fd_str, /* dont close */
+ "-p", prog_str, /* prognum */
+ "-t", tp, /* tp type */
+ NULL);
+ syslog(LOG_ERR, RESOLV_EXEC_ERR, strerror(errno));
+ /*
+ * vfork(2) says the forked child should call _exit(),
+ * not exit(), if exec() fails.
+ */
+ _exit(1);
+ default: /* parent */
+ /* close fd, free xprt, but leave mapping */
+ if (xprt) svc_destroy(xprt);
+
+ /* let it crank up before we create client */
+ sleep(4);
+ }
+ if (sysinfo(SI_HOSTNAME, name, sizeof (name)-1) == -1) {
+ syslog(LOG_ERR, "can't get local hostname.\n");
+ (void) kill (*child, SIGINT);
+ endnetconfig(h);
+ *fwding = FALSE;
+ return;
+ }
+ if ((*client = clnt_tp_create(HOST_SELF_CONNECT, prognum,
+ YPDNSVERS, nc)) == NULL) {
+ syslog(LOG_ERR, "can't create resolv_clnt\n");
+ (void) kill (*child, SIGINT);
+ endnetconfig(h);
+ *fwding = FALSE;
+ return;
+ }
+ endnetconfig(h);
+
+ /* ping for comfort */
+ tv.tv_sec = 10; tv.tv_usec = 0;
+ if ((stat = clnt_call(*client, 0, xdr_void, 0,
+ xdr_void, 0, tv)) != RPC_SUCCESS) {
+ syslog(LOG_ERR, "can't talk with resolv server\n");
+ clnt_destroy (*client);
+ (void) kill (*child, SIGINT);
+ *fwding = FALSE;
+ return;
+ }
+
+ if (verbose)
+ syslog(LOG_INFO, "finished setup for dns fwding.\n");
+}
+
+int
+getprognum(prognum, xprt, fd_str, prog_str, vers, tp_type)
+long *prognum;
+SVCXPRT **xprt;
+char *fd_str;
+char *prog_str;
+long vers;
+char *tp_type;
+{
+ static u_long start = 0x40000000;
+ int fd;
+ struct netconfig *nc;
+ struct netbuf *nb;
+
+ /* If prognum specified, use it instead of transient hassel. */
+ if (*prognum) {
+ *xprt = NULL;
+ sprintf(fd_str, "-1"); /* have child close all fds */
+ sprintf(prog_str, "%u", *prognum);
+ return (TRUE);
+ }
+
+ /*
+ * Transient hassel:
+ * - parent must create mapping since someone else could
+ * steal the transient prognum before child created it
+ * - pass the child the fd to use for service
+ * - close the fd (after exec), free xprt, leave mapping intact
+ */
+ /* tp_type is legit: users choice or a loopback netid */
+ if ((nc = getnetconfigent(tp_type)) == NULL)
+ return (FALSE);
+ if ((*xprt = svc_tli_create(RPC_ANYFD, nc, NULL, 0, 0)) == NULL) {
+ freenetconfigent(nc);
+ return (FALSE);
+ }
+ nb = &(*xprt)->xp_ltaddr;
+ fd = (*xprt)->xp_fd;
+ while (!rpcb_set(start, vers, nc, nb))
+ start++;
+ freenetconfigent(nc);
+
+ *prognum = start;
+ sprintf(fd_str, "%u", fd);
+ sprintf(prog_str, "%u", *prognum);
+
+ return (TRUE);
+}
+
+int
+getconf(netid, handle, nconf)
+char *netid;
+void **handle;
+struct netconfig **nconf;
+{
+ struct netconfig *nc, *save = NULL;
+
+ if ((*handle = setnetconfig()) == NULL)
+ return (FALSE);
+
+ while (nc = getnetconfig((void*)*handle)) {
+ /* XXX Shouldn't this be strcmp() == 0 ? */
+ if (strcmp(nc->nc_netid, netid) != 0) {
+ *nconf = nc;
+ return (TRUE);
+ } else if (!save && strcmp(nc->nc_protofmly, "loopback") != 0)
+ save = nc;
+ }
+
+ if (save) {
+ *nconf = save;
+ return (TRUE);
+ } else {
+ endnetconfig(*handle);
+ return (FALSE);
+ }
+}
+
+int
+resolv_req(fwding, client, pid, tp, xprt, req, map)
+int *fwding;
+CLIENT **client;
+int *pid;
+char *tp;
+SVCXPRT *xprt;
+struct ypreq_key *req;
+char *map;
+{
+ enum clnt_stat stat;
+ struct timeval tv;
+ struct ypfwdreq_key4 fwd_req4;
+ struct ypfwdreq_key6 fwd_req6;
+ struct in6_addr in6;
+ int byname, byaddr;
+ int byname6, byaddr6;
+ struct netbuf *nb;
+ char *cp;
+ int i;
+ sa_family_t caller_af = AF_UNSPEC;
+ struct sockaddr_in *sin4;
+ struct sockaddr_in6 *sin6;
+ int savepid;
+
+ if (!*fwding)
+ return (FALSE);
+
+ byname = strcmp(map, "hosts.byname") == 0;
+ byaddr = strcmp(map, "hosts.byaddr") == 0;
+ byname6 = strcmp(map, "ipnodes.byname") == 0;
+ byaddr6 = strcmp(map, "ipnodes.byaddr") == 0;
+ if ((!byname && !byaddr && !byname6 && !byaddr6) ||
+ req->keydat.dsize == 0 ||
+ req->keydat.dptr[0] == '\0' ||
+ !isascii(req->keydat.dptr[0]) ||
+ !isgraph(req->keydat.dptr[0])) {
+ /* default status is YP_NOKEY */
+ return (FALSE);
+ }
+
+ /*
+ * In order to tell if we have an IPv4 or IPv6 caller address,
+ * we must know that nb->buf is a (sockaddr_in *) or a
+ * (sockaddr_in6 *). Hence, we might as well dispense with the
+ * conversion to uaddr and parsing of same that this section
+ * of the code previously involved itself in.
+ */
+ nb = svc_getrpccaller(xprt);
+ if (nb != 0)
+ caller_af = ((struct sockaddr_storage *)nb->buf)->ss_family;
+
+ if (caller_af == AF_INET6) {
+ fwd_req6.map = map;
+ fwd_req6.keydat = req->keydat;
+ fwd_req6.xid = svc_getxid(xprt);
+ sin6 = (struct sockaddr_in6 *)nb->buf;
+ fwd_req6.addr = (uint32_t *)&in6;
+ memcpy(fwd_req6.addr, sin6->sin6_addr.s6_addr,
+ sizeof (in6));
+ fwd_req6.port = sin6->sin6_port;
+ } else if (caller_af == AF_INET) {
+ fwd_req4.map = map;
+ fwd_req4.keydat = req->keydat;
+ fwd_req4.xid = svc_getxid(xprt);
+ sin4 = (struct sockaddr_in *)nb->buf;
+ fwd_req4.ip = ntohl(sin4->sin_addr.s_addr);
+ fwd_req4.port = sin4->sin_port;
+ } else {
+ syslog(LOG_ERR, "unknown caller IP address family %d",
+ caller_af);
+ return (FALSE);
+ }
+
+ /* Restart resolver if it died. (possible overkill) */
+
+ /*
+ * Since we may end up restarting rpc.nisd_resolv, acquire the
+ * lock.
+ */
+ MUTEXLOCK(setup_resolv, "resolv_req(setup_resolv)");
+
+ if (kill(*pid, 0)) {
+ syslog(LOG_INFO,
+ "Restarting resolv server: old one (pid %d) died.\n", *pid);
+ clnt_destroy (*client);
+ setup_resolv(fwding, pid, client, tp, 0l /* transient p# */);
+ if (!*fwding) {
+ MUTEXUNLOCK(setup_resolv, "resolv_req(setup_resolv)");
+ syslog(LOG_ERR,
+ "can't restart resolver: ending resolv service.\n");
+ return (FALSE);
+ }
+ }
+
+ /*
+ * Save pid of rpc.nisd_resolv, so that we can tell if another
+ * thread performed a restart while we weren't holding the lock.
+ */
+ savepid = *pid;
+ MUTEXUNLOCK(setup_resolv, "resolv_req(setup_resolv)");
+
+ /* may need to up timeout */
+ tv.tv_sec = 10; tv.tv_usec = 0;
+ if (caller_af == AF_INET6) {
+ stat = clnt_call(*client, YPDNSPROC6, xdr_ypfwdreq_key6,
+ (char *) &fwd_req6, xdr_void, 0, tv);
+ } else {
+ stat = clnt_call(*client, YPDNSPROC4, xdr_ypfwdreq_key4,
+ (char *) &fwd_req4, xdr_void, 0, tv);
+ }
+ if (stat == RPC_SUCCESS) /* expected */
+ return (TRUE);
+
+ else { /* Over kill error recovery */
+ MUTEXLOCK(setup_resolv, "resolv_req(setup_resolv)");
+ /*
+ * If the pid of rpc.nisd_resolv has changed, another thread
+ * has already attempted a restart, and there's little point
+ * in our doing the same. Just repeat the clnt_call().
+ */
+ if (*pid == savepid) {
+ /*
+ * make one attempt to restart service before turning
+ * off
+ */
+ syslog(LOG_INFO,
+ "Restarting resolv server: old one not responding.\n");
+
+ if (!kill(*pid, 0))
+ kill (*pid, SIGINT); /* cleanup old one */
+
+ clnt_destroy (*client);
+ setup_resolv(fwding, pid, client, tp,
+ 0l /* transient p# */);
+ if (!*fwding) {
+ MUTEXUNLOCK(setup_resolv,
+ "resolv_req(setup_resolv)");
+ syslog(LOG_ERR,
+ "can't restart resolver: ending resolv service.\n");
+ return (FALSE);
+ }
+ }
+ MUTEXUNLOCK(setup_resolv, "resolv_req(setup_resolv)");
+
+ if (caller_af == AF_INET6) {
+ stat = clnt_call(*client, YPDNSPROC6, xdr_ypfwdreq_key6,
+ (char *) &fwd_req6, xdr_void, 0, tv);
+ } else {
+ stat = clnt_call(*client, YPDNSPROC4, xdr_ypfwdreq_key4,
+ (char *) &fwd_req4, xdr_void, 0, tv);
+ }
+ if (stat == RPC_SUCCESS) /* expected */
+ return (TRUE);
+ else {
+ /* no more restarts */
+ clnt_destroy (*client);
+ *fwding = FALSE; /* turn off fwd'ing */
+ syslog(LOG_ERR,
+ "restarted resolver not responding: ending resolv service.\n");
+ return (FALSE);
+ }
+ }
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_common.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_common.c
new file mode 100644
index 0000000000..71517dae8e
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_common.c
@@ -0,0 +1,74 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 1993-1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines used by rpc.nisd and by rpc.nisd_resolv
+ */
+
+#include <rpc/rpc.h>
+#include <netdb.h>
+#include <rpcsvc/yp_prot.h>
+#include <errno.h>
+#include <sys/types.h>
+#include "resolv_common.h"
+
+bool_t
+xdr_ypfwdreq_key4(XDR *xdrs, struct ypfwdreq_key4 *ps)
+{
+ return (xdr_ypmap_wrap_string(xdrs, &ps->map) &&
+ xdr_datum(xdrs, &ps->keydat) &&
+ xdr_u_long(xdrs, &ps->xid) &&
+ xdr_u_long(xdrs, &ps->ip) &&
+ xdr_u_short(xdrs, &ps->port));
+}
+
+
+bool_t
+xdr_ypfwdreq_key6(XDR *xdrs, struct ypfwdreq_key6 *ps)
+{
+ u_int addrsize = sizeof (struct in6_addr)/sizeof (uint32_t);
+ char **addrp = (caddr_t *)&(ps->addr);
+
+ return (xdr_ypmap_wrap_string(xdrs, &ps->map) &&
+ xdr_datum(xdrs, &ps->keydat) &&
+ xdr_u_long(xdrs, &ps->xid) &&
+ xdr_array(xdrs, addrp, &addrsize, addrsize,
+ sizeof (uint32_t), xdr_uint32_t) &&
+ xdr_u_short(xdrs, &ps->port));
+}
+
+
+u_long
+svc_getxid(xprt)
+register SVCXPRT *xprt;
+{
+ register struct bogus_data *su = getbogus_data(xprt);
+ if (su == NULL)
+ return (0);
+ return (su->su_xid);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_common.h b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_common.h
new file mode 100644
index 0000000000..1e8eb7316b
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_common.h
@@ -0,0 +1,90 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _RESOLV_COMMON_H
+#define _RESOLV_COMMON_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+/*
+ * Definitions common to rpc.nisd resolv and rpc.resolv code.
+ * Stolen from rpcbind and is used to access xprt->xp_p2 fields.
+ */
+
+#define YPDNSPROC4 1L
+#define YPDNSPROC6 2L
+
+#define MAX_UADDR 52
+#define GETCALLER(xprt) svc_getrpccaller(xprt)
+#define SETCALLER(xprt, nbufp) xprt->xp_rtaddr.len = nbufp->len; \
+ memcpy(xprt->xp_rtaddr.buf, nbufp->buf, nbufp->len);
+#define MAX_OPT_WORDS 128
+#define RPC_BUF_MAX 32768
+struct bogus_data {
+ /* XXX: optbuf should be the first field, used by ti_opts.c code */
+ struct netbuf optbuf; /* netbuf for options */
+ long opts[MAX_OPT_WORDS]; /* options */
+ u_int su_iosz; /* size of send.recv buffer */
+ u_long su_xid; /* transaction id */
+ XDR su_xdrs; /* XDR handle */
+ char su_verfbody[MAX_AUTH_BYTES]; /* verifier body */
+ char *su_cache; /* cached data, NULL if none */
+ struct t_unitdata su_tudata; /* tu_data for recv */
+};
+#define getbogus_data(xprt) ((struct bogus_data *)(xprt->xp_p2))
+
+
+struct ypfwdreq_key4 {
+ char *map;
+ datum keydat;
+ unsigned long xid;
+ unsigned long ip;
+ unsigned short port;
+};
+
+struct ypfwdreq_key6 {
+ char *map;
+ datum keydat;
+ unsigned long xid;
+ uint32_t *addr;
+ in_port_t port;
+};
+
+extern ulong_t svc_getxid(SVCXPRT *);
+extern bool_t xdr_ypfwdreq_key4(XDR *, struct ypfwdreq_key4 *);
+extern bool_t xdr_ypfwdreq_key6(XDR *, struct ypfwdreq_key6 *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RESOLV_COMMON_H */
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/DNS_FWD b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/DNS_FWD
new file mode 100644
index 0000000000..2ae1c97574
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/DNS_FWD
@@ -0,0 +1,84 @@
+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
+or http://www.opensolaris.org/os/licensing.
+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
+
+ RPC.NISD operation on ypproc_match_svc() host match failure
+
+
+ |---------------|
+ | rpc.nisd -YX | |---------|
+ | | rpc |nisd_fwd | res_search
+ | --------> | --------------------> | | -------->
+ | | if no | parms: caller | |
+ | | match & | xid | | <--------
+ | | hosts.xx| map | | response
+ | | | key | |
+ |---------------| |---------|
+ ^ |
+ | |
+ clnt_call | caller/xid |
+ (udp) | map & key | sendreply
+ | | (udp)
+ | |
+ |---------------| |
+ | yp_match() | <--------------------------|
+ | hosts |
+ |---------------|
+
+Notes:
+ 1. separate daemon isolates code and minimizes impact on rpc.nisd
+ 2. 4.x and 5.x rpc.nisd dns forwarding support
+ 3. rpc.nisd default operation has not changed, user must select dns
+ resolution at rpc.nisd startup
+
+
+rpc.nisd: (modifications)
+ - added option to select DNS fwding (-B [-t netid])
+ - added setup_resolv() (uses transient p#)
+ - added resolv_req() in ypproc_match_svc() to forward req
+
+rpc.nisd_resolv: (implementation)
+ - leverage ypserv asynch resolv code for minimum risk
+ - non-blocking daemon
+ - SIGUSR1 toggles verbose/debugging info
+ - cleanup/exit if parent dies
+ - cleanup/exit on INT, QUIT, TERM
+
+
+Testing included:
+
+ Normal Operation:
+ - udp,tcp,ticots,ticlts -t selections
+ - DNS match
+ - no DNS match
+ - continuous forwarded requests for 24hrs (5 per sec)
+ - 4.1 rpc.nisd
+ - 5.2 rpc.nisd
+
+ Error Recovery:
+ - default to 1st loopback on invalid -t xxx selection
+ - missing /etc/resolv.conf
+ - down DNS server
+ - wedged rpc.nisd_resolv
+ - killed rpc.nisd_resolv
+ - killed rpc.nisd, rpc.nisd_resolv cleanup
+
+ Error Checking:
+ - usage: using -t without -B and using -B without -Y
+
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/Makefile b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/Makefile
new file mode 100644
index 0000000000..b86aaf456f
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/Makefile
@@ -0,0 +1,74 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+# Makefile for the nis+ dns fwd daemon
+#
+
+SRVOBJS= main.o \
+ ypresolv_proc.o \
+ rpc_as.o \
+ svc_run_as.o \
+ ngethostbyname.o \
+ nget_answer.o \
+ nres_search.o \
+ nres_rcv.o \
+ nres_send.o
+
+COMMONOBJS= resolv_common.o
+
+OBJS= $(SRVOBJS) $(COMMONOBJS)
+SRCS= $(OBJS:.o=.c)
+
+include $(SRC)/cmd/Makefile.cmd
+
+PROG= rpc.nisd_resolv
+
+CFLAGS += $(CCVERBOSE)
+
+# We don't use -lsocket for 4.x build
+LDLIBS += -lnsl -lresolv -lsocket
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTUSRSBINPROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+resolv_common.o: ../resolv_common.c
+ $(COMPILE.c) ../resolv_common.c
+
+lint:
+ $(LINT.c) $(SRVOBJS:.o=.c) ../resolv_common.c $(LDLIBS)
+
+clobber: clean
+ $(RM) $(PROG)
+
+clean:
+ $(RM) $(OBJS)
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/main.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/main.c
new file mode 100644
index 0000000000..36931ee5cf
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/main.c
@@ -0,0 +1,298 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 1993-2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdarg.h>
+#include <sys/systeminfo.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/errno.h>
+#include <errno.h>
+#include <stdio.h>
+#include <signal.h>
+#include <time.h>
+#include <string.h>
+#include <rpc/rpc.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <syslog.h> /* 5.x includes stdarg.h, varargs.h */
+#include <rpcsvc/yp_prot.h>
+#include "prnt.h"
+
+#define YPDNSPROG 123456L /* default prognum: not used */
+#define YPDNSVERS 2L
+
+extern void dispatch(struct svc_req *rqstp, SVCXPRT *transp);
+extern void svc_run_as(void);
+
+static struct timeval start_time; /* Time service started running. */
+int verbose = 0; /* Verbose mode, 0=off, 1=on */
+int verbose_out = 0; /* Verbose ouput, 0=log, 1=stdout */
+static int background = TRUE; /* Forground or bkgrd */
+pid_t ppid = 0; /* for terminating if nisd dies */
+static long prognum = YPDNSPROG; /* for cleanup (may want transient) */
+SVCXPRT *reply_xprt4; /* used for sendreply (IPv4) */
+struct netconfig *udp_nconf4; /* used to set xprt caller in dispatch */
+SVCXPRT *reply_xprt6; /* used for sendreply (IPv6) */
+struct netconfig *udp_nconf6; /* used to set xprt caller in dispatch */
+
+/*
+ * The -v -V is used to distinguiish between verbose to stdout
+ * versus verbose to syslog rather then the background flag
+ * because this may be started from a daemon (nisd). When started
+ * from a daemon we can't use forground to print to stdout.
+ */
+static void usage()
+{
+ (void) fprintf(stderr,
+ "usage: rpc.nisd_resolv [-v|-V] [-F[-C xx]] [-t xx] [-p yy]\n");
+ (void) fprintf(stderr, "Options supported by this version :\n");
+ (void) fprintf(stderr, "\tF - run in forground\n");
+ (void) fprintf(stderr,
+ "\tC fd - use fd for service xprt (from nisd)\n");
+ (void) fprintf(stderr, "\tv - verbose syslog\n");
+ (void) fprintf(stderr, "\tV - verbose stdout\n");
+ (void) fprintf(stderr, "\tt xx - use transport xxx\n");
+ (void) fprintf(stderr, "\tp yy - use transient program# yyy\n");
+ (void) fprintf(stderr, "Use SIGUSR1 to toggle verbose mode.\n");
+ exit(1);
+}
+
+void
+prnt(int info_or_err, char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ if (info_or_err == 1 /* error */) {
+ if (verbose_out) {
+ (void) vfprintf(stderr, format, ap);
+ } else
+ (void) vsyslog(LOG_ERR, format, ap);
+ } else if (verbose) {
+ if (verbose_out) {
+ (void) vfprintf(stdout, format, ap);
+ } else
+ (void) vsyslog(LOG_INFO, format, ap);
+ }
+
+ va_end(ap);
+}
+
+void
+cleanup(int sig)
+{
+ /* unreg prog */
+ if (sig) prnt(P_INFO, "unregistering resolv server and exiting.\n");
+ (void) rpcb_unset(prognum, YPDNSVERS, NULL);
+ exit(0);
+}
+
+/* ARGSUSED */
+static void
+toggle_verbose(int sig)
+{
+ (void) signal(SIGUSR1, toggle_verbose);
+
+ if (verbose) {
+ prnt(P_INFO, "verbose turned off.\n");
+ verbose = FALSE;
+ } else {
+ verbose = TRUE;
+ prnt(P_INFO, "verbose turned on.\n");
+ }
+}
+
+/*
+ * Callback function passed to fdwalk() to close all files.
+ */
+static int
+void_close(void *use_fdp, int fd)
+{
+ if (fd != *(int *)use_fdp)
+ (void) close(fd);
+ return (0);
+}
+
+main(int argc, char *argv[])
+{
+ int i;
+ int use_fd = -2; /* not set */
+ int c;
+ SVCXPRT *transp;
+ char *t_type = "ticots";
+ struct netconfig *nc;
+ int connmaxrec = RPC_MAXDATASIZE;
+
+ /*
+ * Process the command line arguments
+ */
+ opterr = 0;
+ (void) chdir("/var/nis");
+ while ((c = getopt(argc, argv, "vVFC:p:t:")) != -1) {
+ switch (c) {
+ case 'v' : /* syslog */
+ verbose = TRUE;
+ verbose_out = 0;
+ break;
+ case 'V' : /* stdout */
+ verbose = TRUE;
+ verbose_out = 1;
+ break;
+ case 'F' :
+ background = FALSE;
+ break;
+ case 'C' :
+ use_fd = atoi(optarg);
+ break;
+ case 't' :
+ t_type = optarg;
+ break;
+ case 'p' :
+ prognum = atol(optarg);
+ break;
+ case '?':
+ usage();
+ }
+ }
+
+ if (background) {
+ switch (fork()) {
+ case -1:
+ (void) fprintf(stderr,
+ "Couldn't fork a process: exiting.\n");
+ exit(1);
+ /* NOTREACHED */
+ case 0:
+ break;
+ default:
+ exit(0);
+ }
+
+ closefrom(0);
+ (void) open("/dev/null", O_RDONLY);
+ (void) open("/dev/null", O_WRONLY);
+ (void) dup(1);
+ } else { /* foreground */
+ /* pardon the mess: due to transient p# requirement */
+ switch (use_fd) {
+ case -2: /* -C not used: just close stdin */
+ (void) close(0);
+ break;
+ case -1: /* close all (nisd non transient) */
+ closefrom(0);
+ break;
+ default: /* use use_fd as svc fd; close others (nisd trans) */
+ ppid = getppid();
+ (void) fdwalk(void_close, &use_fd);
+ break;
+ }
+ }
+
+ if (!verbose_out)
+ openlog("rpc.nisd_resolv", LOG_PID+LOG_NOWAIT, LOG_DAEMON);
+
+ (void) signal(SIGUSR1, toggle_verbose);
+ (void) signal(SIGINT, cleanup);
+ (void) signal(SIGQUIT, cleanup);
+ (void) signal(SIGTERM, cleanup);
+
+ (void) gettimeofday(&start_time, 0);
+ prnt(P_INFO, "Starting nisd resolv server: %s",
+ ctime((time_t *)&start_time.tv_sec));
+
+ /*
+ * Set non-blocking mode and maximum record size for
+ * connection oriented RPC transports.
+ */
+ if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &connmaxrec)) {
+ prnt(P_INFO, "unable to set maximum RPC record size\n");
+ }
+
+ /* no check for already running since using transient */
+
+ if ((nc = getnetconfigent(t_type)) == NULL) {
+ prnt(P_ERR, "cannot get %s netconf.\n", t_type);
+ exit(1);
+ }
+ if (use_fd != -1 && use_fd != -2) {
+ /* use passed in fd = use_fd */
+ transp = svc_tli_create(use_fd, nc, NULL, YPMSGSZ, YPMSGSZ);
+ if (transp == NULL) {
+ prnt(P_ERR, "cannot create service xprt.\n");
+ exit(1);
+ }
+ /* parent did rpcb_set(): just add to callout */
+ if (!svc_reg(transp, prognum, YPDNSVERS, dispatch, NULL)) {
+ prnt(P_ERR, "cannot register service xprt.\n");
+ exit(1);
+ }
+ } else {
+ (void) rpcb_unset(prognum, YPDNSVERS, NULL);
+ if (!svc_tp_create(dispatch, prognum, YPDNSVERS, nc)) {
+ prnt(P_ERR, "cannot create resolv service.\n");
+ exit(1);
+ }
+ }
+ freenetconfigent(nc);
+
+ /* Need udp xprt for sendreply()s, but don't reg it. */
+ udp_nconf4 = getnetconfigent("udp");
+ udp_nconf6 = getnetconfigent("udp6");
+ if (udp_nconf4 == 0 && udp_nconf6 == 0) {
+ prnt(P_ERR, "cannot get udp/udp6 netconf.\n");
+ exit(1);
+ }
+
+ if (udp_nconf4 != 0)
+ reply_xprt4 = svc_tli_create(RPC_ANYFD, udp_nconf4, NULL,
+ YPMSGSZ, YPMSGSZ);
+ else
+ reply_xprt4 = 0;
+
+
+ if (udp_nconf6 != 0)
+ reply_xprt6 = svc_tli_create(RPC_ANYFD, udp_nconf6, NULL,
+ YPMSGSZ, YPMSGSZ);
+ else
+ reply_xprt6 = 0;
+
+ if (reply_xprt4 == NULL && reply_xprt6 == NULL) {
+ prnt(P_ERR, "cannot create udp/udp6 xprt.\n");
+ exit(1);
+ } else
+ prnt(P_INFO, "created sendreply handle(s).\n");
+
+ /* keep udp_nconf[46] which is used later to create nbuf from uaddr */
+
+ prnt(P_INFO, "entering loop.\n");
+
+ svc_run_as();
+
+ return (0);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nget_answer.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nget_answer.c
new file mode 100644
index 0000000000..8d668a31ef
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nget_answer.c
@@ -0,0 +1,421 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 1993,1998 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* Taken from 4.1.3 ypserv resolver code. */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <strings.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include "nres.h"
+#include "prnt.h"
+
+#ifndef NO_DATA
+#define NO_DATA NO_ADDRESS
+#endif
+
+#ifndef LOG_AUTH
+#define LOG_AUTH 0
+#endif
+
+typedef union {
+ HEADER hdr;
+ uchar_t buf[MAXPACKET];
+} querybuf;
+
+typedef union {
+ int32_t al;
+ char ac;
+} align;
+
+extern int h_errno;
+
+static struct hostent *getanswer(const querybuf *, int, const char *, int);
+extern int lookup_AF_type(struct cache_ent *chl);
+extern uint16_t _getshort(const uchar_t *);
+extern uint32_t _getlong(const uchar_t *);
+
+#define MAXALIASES 35
+#define MAXADDRS 35
+
+static const char AskedForGot[] =
+ "gethostby*.getanswer: asked for \"%s\", got \"%s\"";
+
+static char *h_addr_ptrs[MAXADDRS + 1];
+static struct hostent host;
+static char *host_aliases[MAXALIASES];
+static char hostbuf[8*1024];
+
+
+
+struct hostent *
+nres_getanswer(temp)
+struct nres *temp;
+{
+ querybuf *answer;
+ int anslen;
+ char *name;
+ struct hostent *ret;
+
+ answer = (querybuf *)temp->answer;
+ anslen = temp->answer_len;
+ name = temp->name;
+ if (ret = getanswer(answer, anslen, name, temp->qtype)) {
+ temp->h_errno = 0;
+ prnt(P_INFO, "nres_getanswer: return OK.\n");
+ return (ret);
+ }
+
+ temp->h_errno = h_errno;
+ prnt(P_INFO, "nres_getanswer: return FAIL(err=%d).\n", h_errno);
+ return ((struct hostent *)NULL);
+}
+
+
+int
+nres_chkreply(temp)
+struct nres *temp;
+{
+ char *answer;
+ int anslen;
+ HEADER *hp;
+ answer = temp -> answer;
+ anslen = temp -> answer_len;
+
+ /* initialize some hosts variables for getanswer */
+ host.h_addrtype = lookup_AF_type(temp->userinfo);
+ host.h_length = (host.h_addrtype == AF_INET) ? INADDRSZ : IN6ADDRSZ;
+
+ if (anslen <= 0) {
+ prnt(P_INFO, "nres_chkreply: send error.\n");
+ temp->h_errno = TRY_AGAIN;
+ return (anslen);
+ }
+ hp = (HEADER *) answer;
+ if (hp->rcode != NOERROR || ntohs(hp->ancount) == 0) {
+ prnt(P_INFO, "rcode = %d, ancount=%d.\n", hp->rcode,
+ ntohs(hp->ancount));
+ switch (hp->rcode) {
+ case NXDOMAIN:
+ temp->h_errno = HOST_NOT_FOUND;
+ break;
+ case SERVFAIL:
+ temp->h_errno = TRY_AGAIN;
+ break;
+ case NOERROR:
+ temp->h_errno = NO_DATA;
+ break;
+ case FORMERR:
+ case NOTIMP:
+ case REFUSED:
+ default:
+ temp->h_errno = NO_RECOVERY;
+ break;
+ }
+ return (-1);
+ }
+ return (anslen);
+}
+
+
+
+
+
+/*
+ * getanswer() is copied from usr/src/lib/libresolv2/common/gethnamaddr.c.
+ */
+
+static struct hostent *
+getanswer(answer, anslen, qname, qtype)
+ const querybuf *answer;
+ int anslen;
+ const char *qname;
+ int qtype;
+{
+ const HEADER *hp;
+ const uchar_t *cp;
+ int n;
+ const uchar_t *eom;
+ char *bp, **ap, **hap;
+ int type, class, buflen, ancount, qdcount;
+ int haveanswer, had_error;
+ int toobig = 0;
+ char tbuf[MAXDNAME+1];
+ const char *tname;
+ int (*name_ok) __P((const char *));
+
+ prnt(P_INFO, "getanswer: qname=%s\n", qname);
+ tname = qname;
+ host.h_name = NULL;
+ eom = answer->buf + anslen;
+ switch (qtype) {
+ case T_A:
+ case T_AAAA:
+ name_ok = res_hnok;
+ break;
+ case T_PTR:
+ name_ok = res_dnok;
+ break;
+ default:
+ return (NULL);
+ }
+ /*
+ * find first satisfactory answer
+ */
+ hp = &answer->hdr;
+ ancount = ntohs(hp->ancount);
+ qdcount = ntohs(hp->qdcount);
+ bp = hostbuf;
+ buflen = sizeof (hostbuf);
+ cp = answer->buf + HFIXEDSZ;
+ if (qdcount != 1) {
+ h_errno = NO_RECOVERY;
+ return (NULL);
+ }
+ n = dn_expand(answer->buf, eom, cp, bp, buflen);
+ if ((n < 0) || !(*name_ok)(bp)) {
+ h_errno = NO_RECOVERY;
+ return (NULL);
+ }
+ cp += n + QFIXEDSZ;
+ if (qtype == T_A || qtype == T_AAAA) {
+ /*
+ * res_send() has already verified that the query name is the
+ * same as the one we sent; this just gets the expanded name
+ * (i.e., with the succeeding search-domain tacked on).
+ */
+ n = strlen(bp) + 1; /* for the \0 */
+ host.h_name = bp;
+ bp += n;
+ buflen -= n;
+ /* The qname can be abbreviated, but h_name is now absolute. */
+ qname = host.h_name;
+ }
+ ap = host_aliases;
+ *ap = NULL;
+ host.h_aliases = host_aliases;
+ hap = h_addr_ptrs;
+ *hap = NULL;
+ host.h_addr_list = h_addr_ptrs;
+ haveanswer = 0;
+ had_error = 0;
+ while (ancount-- > 0 && cp < eom && !had_error) {
+ n = dn_expand(answer->buf, eom, cp, bp, buflen);
+ if ((n < 0) || !(*name_ok)(bp)) {
+ had_error++;
+ continue;
+ }
+ cp += n; /* name */
+ type = _getshort(cp);
+ cp += INT16SZ; /* type */
+ class = _getshort(cp);
+ cp += INT16SZ + INT32SZ; /* class, TTL */
+ n = _getshort(cp);
+ cp += INT16SZ; /* len */
+ if (class != C_IN) {
+ /* XXX - debug? syslog? */
+ cp += n;
+ continue; /* XXX - had_error++ ? */
+ }
+ if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME) {
+ if (ap >= &host_aliases[MAXALIASES-1])
+ continue;
+ n = dn_expand(answer->buf, eom, cp, tbuf,
+ sizeof (tbuf));
+ if ((n < 0) || !(*name_ok)(tbuf)) {
+ had_error++;
+ continue;
+ }
+ cp += n;
+ /* Store alias. */
+ *ap++ = bp;
+ n = strlen(bp) + 1; /* for the \0 */
+ bp += n;
+ buflen -= n;
+ /* Get canonical name. */
+ n = strlen(tbuf) + 1; /* for the \0 */
+ if (n > buflen) {
+ had_error++;
+ continue;
+ }
+ strcpy(bp, tbuf);
+ host.h_name = bp;
+ bp += n;
+ buflen -= n;
+ continue;
+ }
+ if (qtype == T_PTR && type == T_CNAME) {
+ n = dn_expand(answer->buf, eom, cp, tbuf,
+ sizeof (tbuf));
+ if ((n < 0) || !res_hnok(tbuf)) {
+ had_error++;
+ continue;
+ }
+ cp += n;
+ /* Get canonical name. */
+ n = strlen(tbuf) + 1; /* for the \0 */
+ if (n > buflen) {
+ had_error++;
+ continue;
+ }
+ strcpy(bp, tbuf);
+ tname = bp;
+ bp += n;
+ buflen -= n;
+ continue;
+ }
+ if (type != qtype) {
+ syslog(LOG_NOTICE|LOG_AUTH,
+ "gethostby*.getanswer: asked for \"%s %s %s\", got type \"%s\"",
+ qname, p_class(C_IN), p_type(qtype),
+ p_type(type));
+ cp += n;
+ continue; /* XXX - had_error++ ? */
+ }
+ switch (type) {
+ case T_PTR:
+ if (strcasecmp(tname, bp) != 0) {
+ syslog(LOG_NOTICE|LOG_AUTH,
+ AskedForGot, tname, bp);
+ cp += n;
+ continue; /* XXX - had_error++ ? */
+ }
+ n = dn_expand(answer->buf, eom, cp, bp, buflen);
+ if ((n < 0) || !res_hnok(bp)) {
+ had_error++;
+ break;
+ }
+/*
+ * #if MULTI_PTRS_ARE_ALIASES
+ * cp += n;
+ * if (!haveanswer)
+ * host.h_name = bp;
+ * else if (ap < &host_aliases[MAXALIASES-1])
+ * *ap++ = bp;
+ * else
+ * n = -1;
+ * if (n != -1) {
+ * n = strlen(bp) + 1; * for the \0 *
+ * bp += n;
+ * buflen -= n;
+ * }
+ * break;
+ * #else
+ */
+ host.h_name = bp;
+ h_errno = NETDB_SUCCESS;
+ return (&host);
+/*
+ * #endif
+ */
+ case T_A:
+ case T_AAAA:
+ if (strcasecmp(host.h_name, bp) != 0) {
+ syslog(LOG_NOTICE|LOG_AUTH,
+ AskedForGot, host.h_name, bp);
+ cp += n;
+ continue; /* XXX - had_error++ ? */
+ }
+#ifdef SUNW_REJECT_BOGUS_H_LENGTH
+ /* Don't accept unexpected address length */
+ if (n != host.h_length) {
+ cp += n;
+ continue;
+ }
+ if (!haveanswer) {
+#else
+ if (haveanswer) {
+ if (n != host.h_length) {
+ cp += n;
+ continue;
+ }
+ } else {
+#endif
+ int nn;
+
+ host.h_name = bp;
+ nn = (int)(strlen(bp) + 1); /* for the \0 */
+ bp += nn;
+ buflen -= nn;
+ }
+
+ bp += sizeof (align) - ((ulong_t)bp % sizeof (align));
+
+ if (bp + n >= &hostbuf[sizeof (hostbuf)]) {
+ prnt(P_INFO, "getanswer: size (%ld) too big\n",
+ (int)n);
+ had_error++;
+ continue;
+ }
+ if (hap >= &h_addr_ptrs[MAXADDRS-1]) {
+ if (!toobig++)
+ prnt(P_INFO,
+ "getanswer: Too many addresses (%d)\n",
+ MAXADDRS);
+ cp += n;
+ continue;
+ }
+ bcopy(cp, *hap++ = bp, n);
+ bp += n;
+ buflen -= n;
+ cp += n;
+ break;
+ default:
+ abort();
+ }
+ if (!had_error)
+ haveanswer++;
+ }
+ if (haveanswer) {
+ *ap = NULL;
+ *hap = NULL;
+ if (!host.h_name) {
+ n = strlen(qname) + 1; /* for the \0 */
+ if (n > buflen)
+ goto try_again;
+ strcpy(bp, qname);
+ host.h_name = bp;
+ bp += n;
+ buflen -= n;
+ }
+ h_errno = NETDB_SUCCESS;
+ return (&host);
+ }
+try_again:
+ h_errno = TRY_AGAIN;
+ return (NULL);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/ngethostbyname.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/ngethostbyname.c
new file mode 100644
index 0000000000..4d89237863
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/ngethostbyname.c
@@ -0,0 +1,536 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 1993,1998 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* Taken from 4.1.3 ypserv resolver code. */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <syslog.h>
+#include "nres.h"
+#include "prnt.h"
+
+#ifndef NO_DATA
+#define NO_DATA NO_ADDRESS
+#endif
+
+int h_errno; /* defined here */
+
+static void nres_abort_xmit(struct nres *);
+static struct nres *nres_setup(char *,
+ void (*)(void *, struct hostent *, ulong_t, struct cache_ent *, int),
+ struct cache_ent *);
+static int nres_dosrch(struct nres *);
+static int nres_register(struct nres *, int);
+extern struct hostent *nres_getanswer(struct nres *);
+extern int lookup_T_type(struct cache_ent *);
+extern int lookup_AF_type(struct cache_ent *chl);
+
+/*
+ * these two routines return immediate errors in h_errno and null
+ * if they fail or the return a struct nres
+ */
+
+struct nres *
+nres_gethostbyname(name, handler, info)
+ char *name;
+ struct cache_ent *info;
+ void (*handler) ();
+{
+ struct nres *temp;
+ char *cp;
+
+
+ /*
+ * disallow names consisting only of digits/dots, unless they end in
+ * a dot.
+ */
+ if (isdigit(name[0])) {
+ cp = name;
+ /* CONSTCOND */
+ while (1) {
+ if (!*cp) {
+ if (*--cp == '.')
+ break;
+ h_errno = HOST_NOT_FOUND;
+ return ((struct nres *)0);
+ }
+ if (!isdigit(*cp) && *cp != '.')
+ break;
+ cp++;
+ }
+ }
+
+ temp = nres_setup(name, handler, info);
+
+ if (temp != NULL) {
+ temp->h_errno = TRY_AGAIN;
+ if (nres_dosrch(temp) >= 0)
+ return (temp);
+ else {
+ if (temp->udp_socket >= 0)
+ (void) close(temp->udp_socket);
+ if (temp->tcp_socket >= 0)
+ (void) close(temp->tcp_socket);
+ h_errno = temp->h_errno;
+ free((char *) temp);
+ return ((struct nres *) 0);
+
+ }
+ } else {
+ prnt(P_INFO, "nres-gethostbyname:setup failed.\n");
+ return ((struct nres *) - 1);
+ }
+
+}
+
+
+/*
+ * NOTE: nres_gethostbyaddr() should never be used to lookup IPv4 mapped
+ * and tunnelled addresses. The client side getXbyY routine should
+ * have already check for this kind of lookup and translated to the
+ * proper IPv4 lookup.
+ */
+struct nres *
+nres_gethostbyaddr(addr, len, type, handler, info)
+ char *addr;
+ int len;
+ int type;
+ struct cache_ent *info;
+ void (*handler) ();
+{
+ struct nres *temp;
+ unsigned char *uaddr = (unsigned char *)addr;
+ char qbuf[MAXDNAME], *qp;
+ int n;
+
+ switch (type) {
+ case AF_INET:
+ (void) sprintf(qbuf, "%d.%d.%d.%d.in-addr.arpa",
+ (uaddr[3] & 0xff), (uaddr[2] & 0xff),
+ (uaddr[1] & 0xff), (uaddr[0] & 0xff));
+ break;
+ case AF_INET6:
+ qp = qbuf;
+ for (n = IN6ADDRSZ - 1; n >= 0; n--) {
+ qp += sprintf(qp, "%x.%x.",
+ uaddr[n] & 0xf, (uaddr[n] >> 4) & 0xf);
+ }
+ strcpy(qp, "ip6.int");
+ break;
+ default:
+ return ((struct nres *) 0);
+ }
+
+ temp = nres_setup(qbuf, handler, info);
+ if (temp != NULL) {
+ temp->reverse = REVERSE_PTR;
+ if (type == AF_INET)
+ (void) memcpy(&(temp->theaddr.s_addr), addr, 4);
+ else
+ (void) memcpy(&(temp->theaddr6.s6_addr), addr, 16);
+ temp->h_errno = TRY_AGAIN;
+ if (nres_dosrch(temp) >= 0)
+ return (temp);
+ else {
+ if (temp->udp_socket >= 0)
+ (void) close(temp->udp_socket);
+ if (temp->tcp_socket >= 0)
+ (void) close(temp->tcp_socket);
+ h_errno = temp->h_errno;
+ free((char *) temp);
+ return ((struct nres *) 0);
+
+ }
+ } else {
+ prnt(P_INFO, "nres-gethostbyaddr:setup failed.\n");
+ return ((struct nres *) - 1);
+ }
+
+}
+
+/*
+ * A timeout has occured -- try to retransmit, if it fails call abort_xmit to
+ * decide to pursue the search or give up
+ */
+
+static void
+nres_dotimeout(as)
+ rpc_as *as;
+{
+
+ struct nres *temp;
+ temp = (struct nres *) as->as_userptr;
+
+ /*
+ * timeout
+ */
+ prnt(P_INFO, "timeout.\n");
+ temp->current_ns = temp->current_ns + 1;
+
+ if (temp->using_tcp) {
+ (void) close(temp->tcp_socket);
+ temp->tcp_socket = -1;
+ (void) rpc_as_unregister(as); /* if you close it */
+ }
+
+ if (nres_xmit(temp) < 0) {
+ temp->h_errno = TRY_AGAIN;
+ (void) rpc_as_unregister(as);
+ nres_abort_xmit(temp);
+ } else {
+ if (temp->using_tcp) {
+ if (nres_register(temp, temp->tcp_socket) < 0) {
+ temp->h_errno = TRY_AGAIN;
+ nres_abort_xmit(temp);
+ }
+ } else {
+ if (nres_register(temp, temp->udp_socket) < 0) {
+ temp->h_errno = TRY_AGAIN;
+ nres_abort_xmit(temp);
+ }
+ }
+ }
+}
+
+/* this advances the search with dosrch or gives up */
+/* if it gives up it calls the users 'done' function */
+/* this is the timeout way to call the user */
+static void
+nres_abort_xmit(temp)
+ struct nres *temp;
+{
+ /* called on timeout */
+ int give_up;
+ prnt(P_INFO, "nres_abort().\n");
+ give_up = 0;
+ if (temp->search_index == 1) {
+ give_up = 1;
+ prnt(P_INFO, "Name server(s) seem to be down.\n");
+ } else if (nres_dosrch(temp) < 0)
+ give_up = 1;
+ if (give_up) {
+ prnt(P_INFO,
+ "more srching aborted: would ret try_again to caller.\n");
+ temp->h_errno = TRY_AGAIN;
+ if (temp->done)
+ (temp->done)((void *)temp, NULL, 0,
+ temp->userinfo, temp->h_errno);
+ if (temp->udp_socket >= 0)
+ (void) close(temp->udp_socket);
+ if (temp->tcp_socket >= 0)
+ (void) close(temp->tcp_socket);
+ free((char *) temp);
+ }
+}
+
+/*
+ * try to pursue the search by calling nres_search and nres_xmit -- if both
+ * work then register an asynch reply to come to nres_dorcv or a timeout to
+ * nres_dotimeout
+ */
+/*
+ * 0 means that the search is continuing -1 means that the search is not
+ * continuing h_errno has the cause.
+ */
+
+static int
+nres_dosrch(temp)
+ struct nres *temp;
+{
+ int type;
+
+ if (nres_search(temp) >= 0) {
+ type = temp->qtype;
+ prnt(P_INFO, "search \'%s\'.\n", temp->search_name);
+ temp->question_len = res_mkquery(QUERY, temp->search_name,
+ C_IN, type, (uchar_t *) NULL, 0, NULL,
+ (uchar_t *)temp->question, MAXPACKET);
+ if (temp->question_len < 0) {
+ temp->h_errno = NO_RECOVERY;
+ prnt(P_INFO, "res_mkquery --NO RECOVERY.\n");
+ return (-1);
+ }
+ if (nres_xmit(temp) < 0) {
+ prnt(P_INFO, "nres_xmit() fails.\n");
+ temp->h_errno = TRY_AGAIN;
+ return (-1);
+ } else {
+ if (temp->using_tcp) {
+ if (nres_register(temp, temp->tcp_socket) < 0) {
+ temp->h_errno = TRY_AGAIN;
+ return (-1);
+ }
+ } else {
+ if (nres_register(temp, temp->udp_socket) < 0) {
+ temp->h_errno = TRY_AGAIN;
+ return (-1);
+ }
+ }
+ }
+ return (0);
+ }
+ return (-1);
+}
+
+
+/*
+ * this processes an answer received asynchronously a nres_rcv is done to
+ * pick up the packet, if it fails we just return, otherwise we unregister
+ * the fd, check the reply. If the reply has an answer we call nres_getanswer
+ * to get the answer, otherwise there is no answer an we call nres_dosrch to
+ * press the search forward, if nres_dosrch works we return. If the search
+ * can not be continued or if we got the answer we call the users done
+ * routine
+ */
+
+static void
+nres_dorecv(as)
+ struct rpc_as *as;
+{
+ struct nres *temp;
+ struct nres *again;
+ struct hostent *theans;
+ int status;
+ struct in_addr **a;
+ struct in6_addr **a6;
+ void (*done) ();
+
+ temp = (struct nres *) as->as_userptr;
+ theans = NULL;
+ errno = 0;
+ status = nres_rcv(temp);
+ if (status > 0) {
+ prnt(P_INFO, "Recieved chars=%d.\n", temp->answer_len);
+ if (verbose && verbose_out) p_query((uchar_t *)temp->answer);
+ } else if (status < 0) {
+ prnt(P_INFO, "nres_rcv() hard fails.\n");
+ (void) rpc_as_unregister(as); /* socket was closed for us */
+ nres_dotimeout(as); /* this may revive it */
+ return; /* keep running */
+ } else {
+ prnt(P_INFO, "nres_rcv() soft fails.\n");
+ return; /* keep running */
+ }
+ (void) rpc_as_unregister(as);
+
+ /* reply part */
+ temp->answer_len = nres_chkreply(temp);
+
+ if (temp->answer_len < 0) {
+ if (errno == ECONNREFUSED) {
+ temp->h_errno = TRY_AGAIN;
+ goto out;
+ }
+ if (temp->h_errno == NO_DATA)
+ temp->got_nodata++;
+ if ((temp->h_errno != HOST_NOT_FOUND &&
+ temp->h_errno != NO_DATA) ||
+ (_res.options & RES_DNSRCH) == 0)
+ goto out;
+ } else {
+ prnt(P_INFO, "nres_getanswer().\n");
+ theans = nres_getanswer(temp);
+ goto out;
+ }
+ prnt(P_INFO, "continuing search... .\n");
+ if (nres_dosrch(temp) < 0)
+ goto out;
+ return; /* keep running with new search */
+
+out:
+ prnt(P_INFO, "done with this case.\n");
+ /* answer resolution */
+ if ((theans == NULL) && temp->got_nodata) {
+ temp->h_errno = NO_DATA;
+ prnt(P_INFO, "no_data.\n");
+ }
+
+
+ done = temp->done;
+ if (theans) {
+ if (temp->reverse == REVERSE_PTR) {
+ /* raise security */
+ theans->h_addrtype = temp->af_type;
+ if (theans->h_addrtype == AF_INET) {
+ theans->h_length = 4;
+ theans->h_addr_list[0] =
+ (char *) &(temp->theaddr);
+ } else {
+ theans->h_length = 16;
+ theans->h_addr_list[0] =
+ (char *) &(temp->theaddr6);
+ }
+ theans->h_addr_list[1] = (char *) 0;
+ if (temp->udp_socket >= 0)
+ (void) close(temp->udp_socket);
+ if (temp->tcp_socket >= 0)
+ (void) close(temp->tcp_socket);
+ again = nres_setup(theans->h_name, temp->done,
+ temp->userinfo);
+ if (again != NULL) {
+ if (again->af_type == AF_INET) {
+ again->qtype = T_A;
+ again->theaddr = temp->theaddr;
+ } else {
+ again->qtype = T_AAAA;
+ again->theaddr6 = temp->theaddr6;
+ }
+ again->reverse = REVERSE_A;
+ again->h_errno = TRY_AGAIN;
+ if (nres_dosrch(again) < 0) {
+ if (done)
+ (*done) (again, NULL, 0,
+ again->userinfo, again->h_errno);
+ if (again->udp_socket >= 0)
+ (void) close(again->udp_socket);
+ if (again->tcp_socket >= 0)
+ (void) close(again->tcp_socket);
+ free((char *) again);
+ }
+ } else {
+ /* memory error */
+ temp->h_errno = TRY_AGAIN;
+ if (done)
+ (*done) (temp, NULL, 0, temp->userinfo,
+ temp->h_errno);
+
+ }
+ free((char *) temp);
+ return;
+ } else if (temp->reverse == REVERSE_A) {
+ int found_addr = FALSE;
+ if (temp->af_type == AF_INET) {
+ for (a = (struct in_addr **)theans->h_addr_list;
+ *a; a++) {
+ if (memcmp(*a, &(temp->theaddr),
+ theans->h_length) == 0) {
+ if (done)
+ (*done) (temp,
+ theans, temp->ttl,
+ temp->userinfo,
+ temp->h_errno);
+ done = NULL;
+ found_addr = TRUE;
+ break;
+ }
+ }
+ if (!found_addr) { /* weve been spoofed */
+ char bb[100];
+ (void) inet_ntop(AF_INET,
+ (void *)&temp->theaddr, bb, 100);
+ prnt(P_ERR,
+ "nres_gethostbyaddr: %s != %s.\n",
+ temp->name, bb);
+ theans = NULL;
+ temp->h_errno = HOST_NOT_FOUND;
+ }
+ } else {
+ /* AF_INET6 */
+ for (a6 = (struct in6_addr **)
+ theans->h_addr_list; *a6; a6++) {
+ if (memcmp(*a6, &(temp->theaddr6),
+ theans->h_length) == 0) {
+ if (done)
+ (*done) (temp,
+ theans, temp->ttl,
+ temp->userinfo,
+ temp->h_errno);
+ done = NULL;
+ found_addr = TRUE;
+ break;
+ }
+ }
+ if (!found_addr) { /* weve been spoofed */
+ char bb[100];
+ (void) inet_ntop(AF_INET6,
+ (void *)&temp->theaddr6, bb, 100);
+ prnt(P_ERR,
+ "nres_gethostbyaddr: %s != %s.\n",
+ temp->name, bb);
+ theans = NULL;
+ temp->h_errno = HOST_NOT_FOUND;
+ }
+ }
+ }
+ }
+ if (done)
+ (*done) (temp, theans, temp->ttl,
+ temp->userinfo, temp->h_errno);
+ if (temp->udp_socket >= 0)
+ (void) close(temp->udp_socket);
+ if (temp->tcp_socket >= 0)
+ (void) close(temp->tcp_socket);
+ free((char *) temp);
+ return; /* done running */
+
+}
+
+static int
+nres_register(a, b)
+ int b;
+ struct nres *a;
+{
+ a->nres_rpc_as.as_fd = b;
+ a->nres_rpc_as.as_timeout_flag = TRUE;
+ a->nres_rpc_as.as_timeout = nres_dotimeout;
+ a->nres_rpc_as.as_recv = nres_dorecv;
+ a->nres_rpc_as.as_userptr = (char *) a;
+ return (rpc_as_register(&(a->nres_rpc_as)));
+}
+
+static struct nres *
+nres_setup(name, done, userinfo)
+ char *name;
+ struct cache_ent *userinfo;
+ void (*done) ();
+{
+ struct nres *tmp;
+
+ tmp = (struct nres *) calloc(1, sizeof (struct nres));
+ if (tmp == NULL)
+ return (tmp);
+ (void) strncpy(tmp->name, name, MAXDNAME);
+ tmp->tcp_socket = -1;
+ tmp->udp_socket = -1;
+ tmp->done = done;
+ tmp->userinfo = userinfo;
+ tmp->qtype = lookup_T_type(userinfo);
+ tmp->af_type = lookup_AF_type(userinfo);
+ return (tmp);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nres.h b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nres.h
new file mode 100644
index 0000000000..b416731f17
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nres.h
@@ -0,0 +1,97 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 1993-1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+/* Taken from 4.1.3 ypserv resolver code. */
+
+#ifndef _NRES_H
+#define _NRES_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <netdb.h>
+#include <arpa/nameser.h>
+#include "rpc_as.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if PACKETSZ > 1024
+#define MAXPACKET PACKETSZ
+#else
+#define MAXPACKET 1024
+#endif
+#define REVERSE_PTR 1
+#define REVERSE_A 2
+
+struct nres {
+ rpc_as nres_rpc_as;
+ struct cache_ent *userinfo;
+ void (*done) (void *, struct hostent *,
+ ulong_t, struct cache_ent *, int);
+ int af_type; /* AF_INET or AF_INET6 */
+ int qtype; /* query type: T_A, T_AAAA, T_PTR */
+
+ int h_errno;
+ int reverse; /* used for gethostbyaddr */
+ struct in_addr theaddr; /* gethostbyaddr */
+ struct in6_addr theaddr6; /* gethostbyaddr IPv6 */
+ char name[MAXDNAME + 1]; /* gethostbyame name */
+ char search_name[2 * MAXDNAME + 2];
+ int search_index; /* 0 up as we chase path */
+ char question[MAXPACKET];
+ char answer[MAXPACKET];
+ int using_tcp; /* 0 ->udp in use */
+ int udp_socket;
+ int tcp_socket;
+ int got_nodata; /* no_data rather than name_not_found */
+ int question_len;
+ int answer_len;
+ int current_ns;
+ int retries;
+ int ttl; /* ttl value from response */
+};
+
+extern struct netconfig *udp_nconf4;
+extern struct netconfig *udp_nconf6;
+
+extern int nres_xmit(struct nres *);
+extern struct hostent *nres_getanswer(struct nres *);
+extern int nres_search(struct nres *);
+extern int nres_rcv(struct nres *);
+extern int nres_chkreply(struct nres *);
+extern struct nres *nres_gethostbyname(char *,
+ void (*)(void *, struct hostent *, ulong_t, struct cache_ent *, int),
+ struct cache_ent *);
+extern struct nres *nres_gethostbyaddr(char *, int, int,
+ void (*)(void *, struct hostent *, ulong_t, struct cache_ent *, int),
+ struct cache_ent *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _NRES_H */
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nres_rcv.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nres_rcv.c
new file mode 100644
index 0000000000..852056cebf
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nres_rcv.c
@@ -0,0 +1,154 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 1993,1998 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* Taken from 4.1.3 ypserv resolver code. */
+
+/*
+ * Send query to name server and wait for reply.
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <netinet/in.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include "nres.h"
+#include "prnt.h"
+
+
+int
+nres_rcv(struct nres *tnr)
+{
+ register int n;
+ int resplen;
+ ushort_t len;
+ char *cp;
+ HEADER *hp = (HEADER *)tnr->question;
+ HEADER *anhp = (HEADER *)tnr->answer;
+ int s;
+ int truncated = 0;
+ char junk[512];
+ if (tnr->using_tcp == 0) {
+ s = tnr->udp_socket;
+
+ if ((resplen = recv(s, tnr->answer, MAXPACKET, 0)) <= 0) {
+ prnt(P_ERR, "recv failed: %s.\n", strerror(errno));
+ return (-1);
+ }
+ if (hp->id != anhp->id) {
+ /*
+ * response from old query, ignore it
+ */
+ prnt(P_INFO, "old answer.\n");
+ if (verbose && verbose_out)
+ p_query((uchar_t *)tnr->answer);
+ return (0); /* wait again */
+ }
+ if (!(_res.options & RES_IGNTC) && anhp->tc) {
+ /*
+ * get rest of answer
+ */
+ prnt(P_INFO, "truncated answer..\n");
+ (void) close(tnr->udp_socket);
+ tnr->udp_socket = -1;
+ tnr->using_tcp = 1;
+ return (-1);
+ }
+ tnr->answer_len = resplen;
+ return (1);
+ } else {
+ /* tcp case */
+ s = tnr->tcp_socket;
+ /*
+ * Receive length & response
+ */
+ cp = tnr->answer;
+ len = sizeof (short);
+ while (len != 0 && (n = read(s, (char *)cp, (int)len)) > 0) {
+ cp += n;
+ len -= n;
+ }
+ if (n <= 0) {
+ prnt(P_ERR, "read failed: %s.\n", strerror(errno));
+ (void) close(s);
+ tnr->tcp_socket = -1;
+ return (-1);
+ }
+ cp = tnr->answer;
+ if ((resplen = ntohs(*(ushort_t *)cp)) > MAXPACKET) {
+ prnt(P_INFO, "response truncated.\n");
+ len = MAXPACKET;
+ truncated = 1;
+ } else
+ len = resplen;
+ while (len != 0 && (n = read(s, (char *)cp, (int)len)) > 0) {
+ cp += n;
+ len -= n;
+ }
+ if (n <= 0) {
+ prnt(P_ERR, "read failed: %s.\n", strerror(errno));
+ (void) close(s);
+ tnr->tcp_socket = -1;
+ return (-1);
+ }
+ if (truncated) {
+ /*
+ * Flush rest of answer so connection stays in synch.
+ */
+ anhp->tc = 1;
+ len = resplen - MAXPACKET;
+ while (len != 0) {
+ n = (len > sizeof (junk) ? sizeof (junk) : len);
+ if ((n = read(s, junk, n)) > 0)
+ len -= n;
+ else
+ break;
+ }
+ }
+ if (hp->id != anhp->id) {
+ /*
+ * response from old query, ignore it
+ */
+ prnt(P_INFO, "old answer.\n");
+ if (verbose && verbose_out)
+ p_query((uchar_t *)tnr->answer);
+ return (0); /* wait again */
+
+ }
+ tnr->answer_len = resplen;
+ return (1);
+ }
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nres_search.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nres_search.c
new file mode 100644
index 0000000000..8c0bc2005b
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nres_search.c
@@ -0,0 +1,157 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 1993,1998 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* Taken from 4.1.3 ypserv resolver code. */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include "nres.h"
+#include "prnt.h"
+
+static void nres_querydomain(char *, char *, char *);
+static char *nres_hostalias(char *);
+
+nres_search(struct nres *block)
+{
+ register char *cp, *domain;
+ int n;
+
+ if ((_res.options & RES_INIT) == 0 && res_init() == -1)
+ return (-1);
+
+ block->retries = 0; /* start clock */
+ if (block->search_index < 0)
+ return (-1);
+ /* only try exact match for reverse cases */
+ if (block->reverse) {
+ (void) nres_querydomain(block->name, (char *)NULL,
+ block->search_name);
+ block->search_index = -1;
+ return (0);
+ }
+
+ for (cp = block->name, n = 0; *cp; cp++)
+ if (*cp == '.')
+ n++;
+ /* n indicates the presence of trailing dots */
+
+ if (block->search_index == 0) {
+ if (n == 0 && (cp = nres_hostalias(block->name))) {
+ (void) strncpy(block->search_name, cp, 2 * MAXDNAME);
+ block->search_index = -1; /* if hostalias try 1 name */
+ return (0);
+ }
+ }
+ if ((n == 0 || *--cp != '.') && (_res.options & RES_DEFNAMES)) {
+ domain = _res.dnsrch[block->search_index];
+ if (domain) {
+ (void) nres_querydomain(block->name, domain,
+ block->search_name);
+ block->search_index++;
+ return (0);
+ }
+ }
+ if (n) {
+ (void) nres_querydomain(block->name, (char *)NULL,
+ block->search_name);
+ block->search_index = -1;
+ return (0);
+ }
+ block->search_index = -1;
+ return (-1);
+}
+
+/*
+ * Perform a call on res_query on the concatenation of name and domain,
+ * removing a trailing dot from name if domain is NULL.
+ */
+static void
+nres_querydomain(char *name, char *domain, char *nbuf)
+{
+ int n;
+
+ if (domain == NULL) {
+ /*
+ * Check for trailing '.'; copy without '.' if present.
+ */
+ n = strlen(name) - 1;
+ if (name[n] == '.') {
+ (void) memcpy(nbuf, name, n);
+ nbuf[n] = '\0';
+ } else
+ (void) strcpy(nbuf, name);
+ } else
+ (void) sprintf(nbuf, "%.*s.%.*s",
+ MAXDNAME, name, MAXDNAME, domain);
+
+ prnt(P_INFO, "nres_querydomain(, %s).\n", nbuf);
+}
+
+static char *
+nres_hostalias(char *name)
+{
+ register char *C1, *C2;
+ FILE *fp;
+ char *file;
+ char buf[BUFSIZ];
+ static char abuf[MAXDNAME];
+
+ file = getenv("HOSTALIASES");
+ if (file == NULL || (fp = fopen(file, "r")) == NULL)
+ return (NULL);
+ buf[sizeof (buf) - 1] = '\0';
+ while (fgets(buf, sizeof (buf), fp)) {
+ for (C1 = buf; *C1 && !isspace(*C1); ++C1);
+ if (!*C1)
+ break;
+ *C1 = '\0';
+ if (!strcasecmp(buf, name)) {
+ while (isspace(*++C1));
+ if (!*C1)
+ break;
+ for (C2 = C1 + 1; *C2 && !isspace(*C2); ++C2);
+ abuf[sizeof (abuf) - 1] = *C2 = '\0';
+ (void) strncpy(abuf, C1, sizeof (abuf) - 1);
+ (void) fclose(fp);
+ return (abuf);
+ }
+ }
+ (void) fclose(fp);
+ return (NULL);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nres_send.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nres_send.c
new file mode 100644
index 0000000000..98a4a5a3e9
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/nres_send.c
@@ -0,0 +1,166 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 1993-1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* Taken from 4.1.3 ypserv resolver code. */
+
+/*
+ * Send query to name server and wait for reply.
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <netinet/in.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <string.h>
+#include <strings.h>
+#include "nres.h"
+#include "prnt.h"
+
+
+nres_xmit(struct nres *tnr)
+{
+ char *buf;
+ int buflen;
+ int v_circuit;
+ ushort_t len;
+
+ struct iovec iov[2];
+
+ buf = tnr->question;
+ buflen = tnr->question_len;
+
+ prnt(P_INFO, "nres_xmit().\n");
+ if (verbose && verbose_out) p_query((uchar_t *)buf);
+ if (!(_res.options & RES_INIT))
+ if (res_init() == -1) {
+ return (-1);
+ }
+ v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ;
+ if (tnr->using_tcp)
+ v_circuit = 1;
+ if (v_circuit)
+ tnr->using_tcp = 1;
+
+ prnt(P_INFO, "this is retry %d.\n", tnr->retries);
+
+ if (_res.nscount <= 0) {
+ prnt(P_INFO, "nres_xmit -- no name servers\n");
+ return (-1);
+ }
+ if (tnr->retries >= _res.retry) {
+ prnt(P_INFO,
+ "nres_xmit -- retries exausted %d.\n", _res.retry);
+ return (-1);
+ }
+ if (tnr->current_ns >= _res.nscount) {
+ tnr->current_ns = 0;
+ tnr->retries = tnr->retries + 1;
+ }
+ tnr->nres_rpc_as.as_timeout_remain.tv_sec = (_res.retrans <<
+ (tnr->retries)) / _res.nscount;
+ tnr->nres_rpc_as.as_timeout_remain.tv_usec = 0;
+ if (tnr->nres_rpc_as.as_timeout_remain.tv_sec < 1)
+ tnr->nres_rpc_as.as_timeout_remain.tv_sec = 1;
+
+ for (; tnr->current_ns < _res.nscount; tnr->current_ns++) {
+ prnt(P_INFO,
+ "Querying server (# %d) address = %s.\n", tnr->current_ns + 1,
+ inet_ntoa(_res.nsaddr_list[tnr->current_ns].sin_addr));
+ if (v_circuit) {
+
+ /*
+ * Use virtual circuit.
+ */
+ if (tnr->tcp_socket < 0) {
+ tnr->tcp_socket = socket(AF_INET,
+ SOCK_STREAM, 0);
+ if (tnr->tcp_socket < 0) {
+ prnt(P_ERR, "socket failed: %s.\n",
+ strerror(errno));
+ if (tnr->udp_socket < 0)
+ return (-1);
+ }
+ if (connect(tnr->tcp_socket,
+ (struct sockaddr *)&(_res.nsaddr_list[tnr->current_ns]),
+ sizeof (struct sockaddr)) < 0) {
+ prnt(P_ERR, "connect failed: %s.\n",
+ strerror(errno));
+ (void) close(tnr->tcp_socket);
+ tnr->tcp_socket = -1;
+ continue;
+ }
+ }
+ /*
+ * Send length & message
+ */
+ len = htons((ushort_t)buflen);
+ iov[0].iov_base = (caddr_t)&len;
+ iov[0].iov_len = sizeof (len);
+ iov[1].iov_base = tnr->question;
+ iov[1].iov_len = tnr->question_len;
+ if (writev(tnr->tcp_socket, iov, 2) !=
+ sizeof (len) + buflen) {
+ prnt(P_ERR, "write failed: %s.\n",
+ strerror(errno));
+ (void) close(tnr->tcp_socket);
+ tnr->tcp_socket = -1;
+ continue;
+ }
+ /* reply will come on tnr->tcp_socket */
+ } else {
+ /*
+ * Use datagrams.
+ */
+ if (tnr->udp_socket < 0)
+ tnr->udp_socket = socket(AF_INET,
+ SOCK_DGRAM, 0);
+ if (tnr->udp_socket < 0)
+ return (-1);
+
+ if (sendto(tnr->udp_socket, buf, buflen, 0,
+ (struct sockaddr *)&_res.nsaddr_list[tnr->current_ns],
+ sizeof (struct sockaddr)) != buflen) {
+ prnt(P_ERR, "sendto failed: %s.\n",
+ strerror(errno));
+ continue;
+ } else {
+ if (tnr->retries == 0)
+ return (0);
+ }
+ }
+ }
+ return (0);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/prnt.h b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/prnt.h
new file mode 100644
index 0000000000..d89a084858
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/prnt.h
@@ -0,0 +1,48 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 1993,1998 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _PRNT_H
+#define _PRNT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define P_INFO 0
+#define P_ERR 1
+
+extern int verbose_out;
+extern int verbose;
+
+extern void prnt(int info_or_err, char *format, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PRNT_H */
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/rpc_as.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/rpc_as.c
new file mode 100644
index 0000000000..a039bcb612
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/rpc_as.c
@@ -0,0 +1,327 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 1993,1998 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* Taken from 4.1.3 ypserv resolver code. */
+
+#include "rpc_as.h"
+#include <sys/types.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <poll.h>
+#include <string.h>
+
+#define CHECK_PARENT_SECS 600 /* every 10 min */
+
+extern pid_t ppid;
+extern void cleanup(int);
+
+#define POLLFD_EXTEND 512
+
+static rpc_as **rpc_as_handles;
+static pollfd_t *rpc_as_pollset;
+static int rpc_as_max_pollfd = -1;
+static int rpc_as_pollfd_allocd = 0;
+
+pollfd_t *
+rpc_as_get_pollset()
+{
+ return (rpc_as_pollset);
+}
+
+int
+rpc_as_get_max_pollfd()
+{
+ return (rpc_as_max_pollfd);
+}
+
+static bool_t rpc_as_init()
+{
+ int i;
+
+ if (rpc_as_handles != NULL)
+ return (TRUE);
+
+ rpc_as_handles = (rpc_as **)
+ calloc(POLLFD_EXTEND, sizeof (rpc_as *));
+ if (rpc_as_handles == NULL) {
+ return (FALSE);
+ }
+
+ rpc_as_pollset = (pollfd_t *)calloc(POLLFD_EXTEND, sizeof (pollfd_t));
+ if (rpc_as_pollset == NULL) {
+ free(rpc_as_handles);
+ rpc_as_handles = NULL;
+ return (FALSE);
+ }
+ rpc_as_pollfd_allocd = POLLFD_EXTEND;
+
+ for (i = 0; i < rpc_as_pollfd_allocd; i++) {
+ rpc_as_pollset[i].fd = -1;
+ rpc_as_pollset[i].events = 0;
+ rpc_as_pollset[i].revents = 0;
+ rpc_as_handles[i] = NULL;
+ }
+
+ return (TRUE);
+}
+
+/*
+ * Activate a asynchronous handle.
+ */
+int
+rpc_as_register(rpc_as *xprt)
+{
+
+ if ((rpc_as_handles == NULL) && (!rpc_as_init()))
+ return (-1);
+
+ if (xprt->as_fd < 0)
+ return (-1); /* can't register less than zero */
+
+ /*
+ * If the descriptor is bigger than the pollset and handles
+ * array, grow it by POLLFD_EXTEND until it is large enough.
+ */
+ if (xprt->as_fd >= rpc_as_pollfd_allocd) {
+ int i = rpc_as_pollfd_allocd;
+ pollfd_t *ptmp;
+ rpc_as **htmp;
+
+ do {
+ rpc_as_pollfd_allocd += POLLFD_EXTEND;
+ } while (xprt->as_fd >= rpc_as_pollfd_allocd);
+
+ ptmp = realloc(rpc_as_pollset,
+ sizeof (pollfd_t) * rpc_as_pollfd_allocd);
+ if (ptmp == NULL) {
+ rpc_as_pollfd_allocd = i;
+ return (-1);
+ }
+
+ htmp = realloc(rpc_as_handles,
+ sizeof (rpc_as *) * rpc_as_pollfd_allocd);
+ if (htmp == NULL) {
+ rpc_as_pollfd_allocd = i;
+ return (-1);
+ }
+
+ /*
+ * Initialize the new elements
+ */
+ rpc_as_pollset = ptmp;
+ rpc_as_handles = htmp;
+ for (; i < rpc_as_pollfd_allocd; i++) {
+ rpc_as_pollset[i].fd = -1;
+ rpc_as_pollset[i].events = 0;
+ rpc_as_pollset[i].revents = 0;
+ rpc_as_handles[i] = NULL;
+ }
+ }
+
+ rpc_as_handles[xprt->as_fd] = xprt;
+ rpc_as_pollset[xprt->as_fd].fd = xprt->as_fd;
+ rpc_as_pollset[xprt->as_fd].events =
+ POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND;
+ rpc_as_pollset[xprt->as_fd].revents = 0;
+ if (xprt->as_fd > rpc_as_max_pollfd)
+ rpc_as_max_pollfd = xprt->as_fd;
+ return (0);
+}
+
+static void
+remove_pollfd(int fd)
+{
+ if (fd < 0 || fd > rpc_as_max_pollfd)
+ return;
+
+ /*
+ * Don't shrink handles, pollset, or rpc_as_max_pollfd for now
+ */
+ rpc_as_handles[fd] = (rpc_as *)0;
+ rpc_as_pollset[fd].fd = -1;
+ rpc_as_pollset[fd].events = 0;
+ rpc_as_pollset[fd].revents = 0;
+}
+
+/*
+ * De-activate an asynchronous handle.
+ */
+int
+rpc_as_unregister(rpc_as *xprt)
+{
+ if ((rpc_as_handles == NULL) && (!rpc_as_init()))
+ return (-1);
+ if (xprt->as_fd < 0)
+ return (-1); /* can't unregister less than zero */
+
+ if ((xprt->as_fd <= rpc_as_max_pollfd) &&
+ (rpc_as_handles[xprt->as_fd] == xprt)) {
+ remove_pollfd(xprt->as_fd);
+ return (0);
+ }
+ return (-1);
+}
+
+/*
+ * Check through each element of the poll set looking for returned
+ * events, if found and it corresponds to an active xprt, call as_recv
+ * and decrement pollretval so a later svc_getreq_poll can
+ * be called.
+ */
+void
+rpc_as_rcvreq_poll(pollfd_t *pollset, int *pollretval)
+{
+ int i;
+ rpc_as *xprt;
+
+ if ((rpc_as_handles == NULL) && (!rpc_as_init()))
+ return;
+
+ for (i = 0; i <= rpc_as_max_pollfd; i++) {
+ pollfd_t *p = &pollset[i];
+ if (p->revents) {
+ /* fd has input waiting */
+ if (p->revents & POLLNVAL) {
+ remove_pollfd(p->fd);
+ *pollretval -= 1;
+ continue;
+ }
+
+ xprt = rpc_as_handles[p->fd];
+ if (xprt) {
+ if (xprt->as_recv)
+ xprt->as_recv(xprt, p->fd);
+ else
+ (void) rpc_as_unregister(xprt);
+ /*
+ * Clear the event
+ */
+ p->revents = 0;
+ *pollretval -= 1;
+ }
+ }
+ }
+}
+
+struct timeval
+rpc_as_get_timeout()
+{
+ int tsecs;
+ int tusecs;
+ struct timeval now;
+ struct timeval ans;
+ static struct timeval last;
+ struct rpc_as **rhd;
+ int sock;
+
+ ans.tv_sec = CHECK_PARENT_SECS; /* check parent time */
+ ans.tv_usec = 0;
+
+ if ((rpc_as_handles == NULL) && (!rpc_as_init()))
+ return (ans);
+
+ /* Calculate elapsed time */
+ (void) gettimeofday(&now, (struct timezone *)0);
+ if (last.tv_sec) {
+ tsecs = now.tv_sec - last.tv_sec;
+ tusecs = now.tv_usec - last.tv_usec;
+ last = now;
+ } else {
+ last = now;
+ tsecs = 0;
+ tusecs = 0;
+ }
+ while (tusecs < 0) {
+ tusecs += 1000000;
+ tsecs--;
+ }
+ if (tsecs < 0)
+ tsecs = 0;
+
+ rhd = rpc_as_handles;
+
+ for (sock = 0; sock <= rpc_as_max_pollfd; sock++) {
+ if (rhd[sock] == (struct rpc_as *)NULL)
+ continue;
+
+ if (rhd[sock]->as_timeout_flag) {
+
+ rhd[sock]->as_timeout_remain.tv_sec -= tsecs;
+ rhd[sock]->as_timeout_remain.tv_usec -= tusecs;
+
+ while (rhd[sock]->as_timeout_remain.tv_usec < 0) {
+ rhd[sock]->as_timeout_remain.tv_sec--;
+ rhd[sock]->as_timeout_remain.tv_usec += 1000000;
+ }
+ if (rhd[sock]->as_timeout_remain.tv_sec < 0) {
+ rhd[sock]->as_timeout_remain.tv_sec = 0;
+ rhd[sock]->as_timeout_remain.tv_usec = 0;
+ }
+
+ if (timercmp(&(rhd[sock]->as_timeout_remain), &ans,
+ < /*EMPTY*/))
+ ans = rhd[sock]->as_timeout_remain;
+ }
+ }
+
+ return (ans);
+}
+
+void
+rpc_as_timeout(struct timeval twaited)
+{
+
+ struct rpc_as **rhd;
+ int sock;
+
+ /* ppid only set when using transient from parent (nisd) */
+ if (ppid && kill(ppid, 0))
+ cleanup(0);
+
+ if ((rpc_as_handles == NULL) && (!rpc_as_init()))
+ return;
+ rhd = rpc_as_handles;
+
+ for (sock = 0; sock <= rpc_as_max_pollfd; sock++) {
+ if (rhd[sock] == (struct rpc_as *)NULL)
+ continue;
+ if (rhd[sock]->as_timeout_flag) {
+ if ((timercmp(&(rhd[sock]->as_timeout_remain), &twaited,
+ < /*EMPTY*/)) ||
+ (timercmp(&(rhd[sock]->as_timeout_remain), &twaited,
+ == /*EMPTY*/))) {
+ if (rhd[sock]->as_timeout)
+ rhd[sock]->as_timeout(rhd[sock]);
+ else
+ (void) rpc_as_unregister(rhd[sock]);
+
+ }
+ }
+ }
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/rpc_as.h b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/rpc_as.h
new file mode 100644
index 0000000000..a7fa56e284
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/rpc_as.h
@@ -0,0 +1,63 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 1993,1998 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+/* Taken from 4.1.3 ypserv resolver code. */
+
+#ifndef _RPC_AS_H
+#define _RPC_AS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <rpc/types.h>
+#include <poll.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct rpc_as {
+ char *as_userptr; /* anything you like */
+ struct timeval as_timeout_remain;
+ int as_fd;
+ bool_t as_timeout_flag; /* set if timeouts wanted */
+ void (*as_timeout)(); /* routine to call if timeouts wanted */
+ void (*as_recv)(); /* routine to call if data is present */
+};
+typedef struct rpc_as rpc_as;
+
+extern struct timeval rpc_as_get_timeout(void);
+extern pollfd_t *rpc_as_get_pollset(void);
+extern int rpc_as_get_max_pollfd(void);
+extern void rpc_as_timeout(struct timeval);
+extern void rpc_as_rcvreq_poll(pollfd_t *, int *);
+extern int rpc_as_register(rpc_as *);
+extern int rpc_as_unregister(rpc_as *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RPC_AS_H */
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/svc_run_as.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/svc_run_as.c
new file mode 100644
index 0000000000..f6df3423c9
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/svc_run_as.c
@@ -0,0 +1,126 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 1993-1998 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* Taken from 4.1.3 ypserv resolver code. */
+
+/*
+ * This is an example of using rpc_as.h an asynchronous polling
+ * mechanism, asynchronously polled fds are combined with the
+ * service fds. The minimum timeout is calculated, and
+ * the user waits for that timeout or activity on either the
+ * async pollset or the svc pollset.
+ */
+
+#include <rpc/rpc.h>
+#include <errno.h>
+#include <stdlib.h>
+#include "rpc_as.h"
+#include <stropts.h>
+#include <string.h>
+#include <poll.h>
+#include "prnt.h"
+
+extern int __rpc_timeval_to_msec(struct timeval *t);
+
+/*
+ * Merge two arrays of pollfd's, assumptions:
+ * Arrays are ordered so element N contains descriptor N
+ * Out array is as large as max(asize, bsize)
+ */
+static void
+merge_pollfds(struct pollfd *out, struct pollfd *a, int asize,
+ struct pollfd *b, int bsize)
+{
+ int i;
+ int outsize;
+
+ outsize = (asize > bsize) ? asize : bsize;
+
+ for (i = 0; i < outsize; i++) {
+ if (i < asize && a[i].fd != -1)
+ out[i] = a[i];
+ else if (i < bsize && b[i].fd != -1)
+ out[i] = b[i];
+ else
+ out[i].fd = -1;
+ }
+}
+
+void
+svc_run_as()
+{
+ struct timeval timeout;
+ struct pollfd *svc_pollset = NULL;
+ struct pollfd *as_pollset;
+ int nfds = 0;
+ int max_fds;
+ int as_max_pollfd;
+ int pollret = 0;
+
+ for (;;) {
+ as_max_pollfd = rpc_as_get_max_pollfd() + 1;
+ as_pollset = rpc_as_get_pollset();
+
+ max_fds = (as_max_pollfd > svc_max_pollfd) ?
+ as_max_pollfd : svc_max_pollfd;
+ if (nfds != max_fds) {
+ svc_pollset = realloc(svc_pollset,
+ sizeof (pollfd_t) * max_fds);
+ nfds = max_fds;
+ }
+
+ if (nfds == 0)
+ break; /* None waiting, hence return */
+
+ /*
+ * Merge together rpc_as_get_pollset and svc_pollfd
+ */
+ merge_pollfds(svc_pollset, as_pollset, as_max_pollfd,
+ svc_pollfd, svc_max_pollfd);
+
+ timeout = rpc_as_get_timeout();
+
+ switch ((pollret = poll(svc_pollset, nfds,
+ __rpc_timeval_to_msec(&timeout)))) {
+ case -1:
+ if (errno == EINTR) {
+ continue;
+ }
+ prnt(P_ERR, "svc_run: - poll failed: %s\n",
+ strerror(errno));
+ return;
+ case 0:
+ rpc_as_timeout(timeout);
+ break;
+ default:
+ rpc_as_rcvreq_poll(svc_pollset, &pollret);
+
+ svc_getreq_poll(svc_pollset, pollret);
+ }
+ }
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/ypresolv_proc.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/ypresolv_proc.c
new file mode 100644
index 0000000000..eedc39cecb
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/resolv_server/ypresolv_proc.c
@@ -0,0 +1,677 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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.
+ */
+
+/*
+ * The resolv code was lifted from 4.1.3 ypserv. References to child/pid
+ * have been changed to cache/nres to reflect what is really happening.
+ *
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* ******************** include headers ****************************** */
+#include <netdb.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/yp_prot.h>
+#include <netdir.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <arpa/inet.h>
+#include "../resolv_common.h"
+#include "prnt.h"
+#include "nres.h"
+
+#define RESP_NOW 0
+#define RESP_LATER 2
+
+#define NQTIME 10 /* minutes */
+#define PQTIME 30 /* minutes */
+
+/*
+ * Cache entries for storing rpc.nisd req and resolv req info
+ */
+struct cache_ent {
+ struct nres *nres;
+ datum key;
+ datum val;
+ char *map;
+ struct timeval enqtime;
+ int h_errno;
+ SVCXPRT *xprt;
+ struct netbuf caller;
+ char buf[MAX_UADDR];
+ unsigned long xid;
+ unsigned long ttl;
+ struct cache_ent *next_cache_ent;
+};
+
+
+int lookup_AF_type(struct cache_ent *);
+
+/* ******************** static vars and funcs ************************ */
+static struct cache_ent *cache_head = NULL;
+static ulong_t svc_setxid(SVCXPRT *, ulong_t);
+static void my_done(void *, struct hostent *, ulong_t, struct cache_ent *,
+ int);
+static int yp_matchdns(char *, datum, datum *, unsigned *, SVCXPRT *);
+static void free_cache_ent(struct cache_ent *x);
+
+/* ******************** extern vars and funcs ************************ */
+extern int verbose;
+extern SVCXPRT *reply_xprt4;
+extern SVCXPRT *reply_xprt6;
+
+static void
+yp_resolv(sa_family_t af, void *req, SVCXPRT *transp)
+{
+ struct ypresp_val resp;
+ int respond = RESP_NOW;
+ int i;
+ char tmp[12]; /* max size of 9 rounded up to multiple of 4 bytes */
+ char buf[MAX_UADDR];
+ struct netbuf *nbuf;
+ struct bogus_data *bd = NULL;
+ struct ypfwdreq_key4 *req4 = (struct ypfwdreq_key4 *)req;
+ struct ypfwdreq_key6 *req6 = (struct ypfwdreq_key6 *)req;
+ in_port_t port;
+
+ resp.valdat.dptr = NULL;
+ resp.valdat.dsize = 0;
+
+ /* Set the reply_xprt: xid and caller here, to fit yp_matchdns() */
+ if (af == AF_INET6) {
+ (void) inet_ntop(AF_INET6, req6->addr, buf, sizeof (buf));
+ port = req6->port;
+ } else {
+ struct in_addr in4;
+ /*
+ * This doesn't make much sense, but the for some reason
+ * the caller converted req->ip to host byte order, and in
+ * the name of backward compatibility...
+ */
+ in4.s_addr = htonl(req4->ip);
+ (void) strcpy(buf, inet_ntoa(in4));
+ port = req4->port;
+ }
+
+ (void) snprintf(tmp, sizeof (tmp), ".%u.%u",
+ (port>>8) & 0x00ff, port & 0x00ff);
+ (void) strcat(buf, tmp);
+ if ((nbuf = uaddr2taddr((af == AF_INET6) ? udp_nconf6 : udp_nconf4,
+ buf)) == NULL) {
+ prnt(P_ERR, "can't get args.\n");
+ return;
+ }
+ if (nbuf->len > MAX_UADDR) { /* added precaution */
+ prnt(P_ERR, "uaddr too big for cache.\n");
+ netdir_free((void*)nbuf, ND_ADDR);
+ return;
+ }
+ SETCALLER(transp, nbuf);
+ /*
+ * Set su_tudata.addr for sendreply() t_sendudata()
+ * since we never did a recv on this unreg'ed xprt.
+ */
+ if (!bd) { /* just set maxlen and buf once */
+ bd = getbogus_data(transp);
+ bd->su_tudata.addr.maxlen = GETCALLER(transp)->maxlen;
+ bd->su_tudata.addr.buf = GETCALLER(transp)->buf;
+ }
+ bd->su_tudata.addr.len = nbuf->len;
+ netdir_free((void*)nbuf, ND_ADDR);
+ (void) svc_setxid(transp, (af == AF_INET6) ? req6->xid : req4->xid);
+
+ respond = yp_matchdns((af == AF_INET6) ? req6->map : req4->map,
+ (af == AF_INET6) ? req6->keydat : req4->keydat,
+ &resp.valdat, &resp.status, transp);
+
+ if (respond == RESP_NOW)
+ if (!svc_sendreply(transp,
+ (xdrproc_t)xdr_ypresp_val, (char *)&resp)) {
+ prnt(P_ERR, "can't respond to rpc request.\n");
+ }
+}
+
+void
+dispatch(rqstp, transp)
+ struct svc_req *rqstp;
+ SVCXPRT *transp;
+{
+ struct ypfwdreq_key4 req4;
+ struct ypfwdreq_key6 req6;
+
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ if (!svc_sendreply(transp, xdr_void, 0))
+ prnt(P_ERR, "can't respond to ping.\n");
+ break;
+ case YPDNSPROC4:
+ req4.map = NULL;
+ req4.keydat.dptr = NULL;
+ req4.xid = 0;
+ req4.ip = 0;
+ req4.port = 0;
+
+ if (!svc_getargs(transp, xdr_ypfwdreq_key4, (char *)&req4)) {
+ prnt(P_ERR, "can't get args.\n");
+ svcerr_decode(transp);
+ return;
+ }
+
+ /* args were ok: don't wait for resolver */
+ if (!svc_sendreply(transp, xdr_void, 0))
+ prnt(P_ERR, "can't ack nisd req.\n");
+
+ yp_resolv(AF_INET, &req4, reply_xprt4);
+
+ if (!svc_freeargs(transp, xdr_ypfwdreq_key4, (char *)&req4)) {
+ prnt(P_ERR, "can't free args.\n");
+ exit(1);
+ }
+
+ break;
+ case YPDNSPROC6:
+ req6.map = NULL;
+ req6.keydat.dptr = NULL;
+ req6.xid = 0;
+ req6.addr = 0;
+ req6.port = 0;
+
+ if (!svc_getargs(transp, xdr_ypfwdreq_key6, (char *)&req6)) {
+ prnt(P_ERR, "can't get args.\n");
+ svcerr_decode(transp);
+ return;
+ }
+
+ /* args were ok: don't wait for resolver */
+ if (!svc_sendreply(transp, xdr_void, 0))
+ prnt(P_ERR, "can't ack nisd req.\n");
+
+ yp_resolv(AF_INET6, &req6, reply_xprt6);
+
+ if (!svc_freeargs(transp, xdr_ypfwdreq_key6, (char *)&req6)) {
+ prnt(P_ERR, "can't free args.\n");
+ exit(1);
+ }
+
+ break;
+ default:
+ prnt(P_ERR, "call to bogus proc.\n");
+ svcerr_noproc(transp);
+ break;
+ }
+}
+
+static struct cache_ent *
+cache_ent_bykey(map, keydat)
+ char *map;
+ datum keydat;
+{
+ struct cache_ent *chl;
+ struct cache_ent *prev;
+ struct timeval now;
+ struct timezone tzp;
+ int secs;
+ if (keydat.dptr == NULL)
+ return (NULL);
+ if (keydat.dsize <= 0)
+ return (NULL);
+ if (map == NULL)
+ return (NULL);
+ (void) gettimeofday(&now, &tzp);
+
+ for (prev = cache_head, chl = cache_head; chl; /* */) {
+ /* check for expiration */
+ if (chl->h_errno == TRY_AGAIN)
+ secs = NQTIME * 60;
+ else
+ secs = chl->ttl;
+ if ((chl->nres == 0) &&
+ (chl->enqtime.tv_sec + secs) < now.tv_sec) {
+ prnt(P_INFO,
+ "bykey:stale cache_ent flushed %x.\n", chl);
+ /* deleteing the first element is tricky */
+ if (chl == cache_head) {
+ cache_head = cache_head->next_cache_ent;
+ free_cache_ent(chl);
+ prev = cache_head;
+ chl = cache_head;
+ continue;
+ } else {
+ /* deleteing a middle element */
+ prev->next_cache_ent = chl->next_cache_ent;
+ free_cache_ent(chl);
+ chl = prev->next_cache_ent;
+ continue;
+ }
+ } else if (chl->map)
+ if (0 == strcmp(map, chl->map))
+ if (chl->key.dptr) {
+ /* supress trailing null */
+ if (keydat.dptr[keydat.dsize - 1] == 0)
+ keydat.dsize--;
+ if ((chl->key.dsize == keydat.dsize))
+ if (!strncasecmp(chl->key.dptr,
+ keydat.dptr, keydat.dsize)) {
+ /* move to beginning */
+ if (chl != cache_head) {
+ prev->next_cache_ent = chl->next_cache_ent;
+ chl->next_cache_ent = cache_head;
+ cache_head = chl;
+ }
+ return (chl);
+ }
+ }
+ prev = chl;
+ chl = chl->next_cache_ent;
+ }
+ return (NULL);
+}
+
+static struct cache_ent *
+new_cache_ent(map, keydat)
+ char *map;
+ datum keydat;
+{
+ struct cache_ent *chl;
+ struct timezone tzp;
+
+ chl = (struct cache_ent *)calloc(1, sizeof (struct cache_ent));
+ if (chl == NULL)
+ return (NULL);
+ prnt(P_INFO, "cache_ent enqed.\n");
+ chl->caller.buf = chl->buf;
+ chl->caller.maxlen = sizeof (chl->buf);
+ chl->map = (char *)strdup(map);
+ if (chl->map == NULL) {
+ free(chl);
+ return (NULL);
+ }
+ chl->key.dptr = (char *)malloc(keydat.dsize + 1);
+ if (chl->key.dptr == NULL) {
+ free(chl->map);
+ free(chl);
+ return (NULL);
+ }
+ if (keydat.dptr != NULL)
+ /* delete trailing null */
+ if (keydat.dptr[keydat.dsize - 1] == 0)
+ keydat.dsize = keydat.dsize - 1;
+ chl->key.dsize = keydat.dsize;
+ chl->key.dptr[keydat.dsize] = '\0';
+ chl->val.dptr = 0;
+ (void) memcpy(chl->key.dptr, keydat.dptr, keydat.dsize);
+ (void) gettimeofday(&(chl->enqtime), &tzp);
+ chl->next_cache_ent = cache_head;
+ cache_head = chl;
+ return (chl);
+}
+
+static struct cache_ent *
+deq_cache_ent(x)
+ struct cache_ent *x;
+{
+ struct cache_ent *chl;
+ struct cache_ent *prev;
+ if (x == cache_head) {
+ cache_head = cache_head->next_cache_ent;
+ x->next_cache_ent = NULL;
+ return (x);
+ }
+ for (chl = cache_head, prev = cache_head; chl;
+ chl = chl->next_cache_ent) {
+ if (chl == x) {
+ /* deq it */
+ prev->next_cache_ent = chl->next_cache_ent;
+ chl->next_cache_ent = NULL;
+ return (chl);
+ }
+ prev = chl;
+ }
+ return (NULL); /* bad */
+}
+
+static void
+free_cache_ent(x)
+ struct cache_ent *x;
+{
+ if (x == NULL)
+ return;
+ if (x->map)
+ free(x->map);
+ if (x->key.dptr)
+ free(x->key.dptr);
+ if (x->val.dptr)
+ free(x->val.dptr);
+ free(x);
+}
+
+static ulong_t
+svc_setxid(xprt, xid)
+ register SVCXPRT *xprt;
+ ulong_t xid;
+{
+ register struct bogus_data *su = getbogus_data(xprt);
+ ulong_t old_xid;
+ if (su == NULL)
+ return (0);
+ old_xid = su->su_xid;
+ su->su_xid = xid;
+ return (old_xid);
+}
+
+
+static int
+yp_matchdns(map, keydat, valdatp, statusp, transp)
+ char *map; /* map name */
+ datum keydat; /* key to match (e.g. host name) */
+ datum *valdatp; /* returned value if found */
+ unsigned *statusp; /* returns the status */
+ SVCXPRT *transp;
+{
+ struct nres *h;
+ int byname, byaddr;
+ int byname_v6, byaddr_v6;
+ struct cache_ent *chl;
+ struct timeval now;
+ struct timezone tzp;
+ int try_again;
+ int af_type;
+
+ try_again = 0;
+ /*
+ * Skip the domain resolution if: 1. it is not turned on 2. map other
+ * than hosts.byXXX 3. a null string (usingypmap() likes to send
+ * these) 4. a single control character (usingypmap() again)
+ */
+ byname = strcmp(map, "hosts.byname") == 0;
+ byaddr = strcmp(map, "hosts.byaddr") == 0;
+ byname_v6 = strcmp(map, "ipnodes.byname") == 0;
+ byaddr_v6 = strcmp(map, "ipnodes.byaddr") == 0;
+ if ((!byname && !byaddr && !byname_v6 && !byaddr_v6) ||
+ keydat.dsize == 0 || keydat.dptr[0] == '\0' ||
+ !isascii(keydat.dptr[0]) || !isgraph(keydat.dptr[0])) {
+ *statusp = (unsigned)YP_NOKEY;
+ return (RESP_NOW);
+ }
+
+ chl = cache_ent_bykey(map, keydat);
+ if (chl) {
+ (void) gettimeofday(&now, &tzp);
+ if (chl->h_errno == TRY_AGAIN)
+ try_again = 1;
+ else if (chl->nres) {
+ /* update xid */
+ if (transp) {
+ chl->xprt = transp;
+ chl->caller.len = transp->xp_rtaddr.len;
+ (void) memcpy(chl->caller.buf,
+ transp->xp_rtaddr.buf,
+ transp->xp_rtaddr.len);
+ chl->xid = svc_getxid(transp);
+ prnt(P_INFO, "cache_ent %s: xid now %d.\n",
+ chl->key.dptr, chl->xid);
+ }
+ return (RESP_LATER); /* drop */
+ }
+ switch (chl->h_errno) {
+ case NO_RECOVERY:
+#ifndef NO_DATA
+#define NO_DATA NO_ADDRESS
+#endif
+ case NO_DATA:
+ case HOST_NOT_FOUND:
+ prnt(P_INFO, "cache NO_KEY.\n");
+ *statusp = (unsigned)YP_NOKEY;
+ return (RESP_NOW);
+
+ case TRY_AGAIN:
+ prnt(P_INFO, "try_again.\n");
+ try_again = 1;
+ break;
+ case 0:
+ prnt(P_INFO, "cache ok.\n");
+ if (chl->val.dptr) {
+ *valdatp = chl->val;
+ *statusp = (unsigned)YP_TRUE;
+ return (RESP_NOW);
+ }
+ break;
+
+ default:
+ free_cache_ent(deq_cache_ent(chl));
+ chl = NULL;
+ break;
+ }
+ }
+ /* have a trier activated -- tell them to try again */
+ if (try_again) {
+ if (chl->nres) {
+ *statusp = (unsigned)YP_NOMORE;
+ /* try_again overloaded */
+ return (RESP_NOW);
+ }
+ }
+ if (chl) {
+ (void) gettimeofday(&(chl->enqtime), &tzp);
+ } else
+ chl = new_cache_ent(map, keydat);
+
+ if (chl == NULL) {
+ perror("new_cache_ent failed");
+ *statusp = (unsigned)YP_YPERR;
+ return (RESP_NOW);
+ }
+ if (byname || byname_v6)
+ h = nres_gethostbyname(chl->key.dptr, my_done, chl);
+ else {
+ struct in_addr addr;
+ struct in6_addr addr6;
+ af_type = (strcmp(chl->map, "ipnodes.byaddr") == 0) ?
+ AF_INET6 : AF_INET;
+ if (af_type == AF_INET6 || af_type == AF_INET) {
+ if (inet_pton(af_type, chl->key.dptr,
+ (af_type == AF_INET6) ? (void *)&addr6 : (void *)&addr)
+ == -1) {
+ *statusp = (unsigned)YP_NOKEY;
+ return (RESP_NOW);
+ }
+ h = nres_gethostbyaddr(
+ (af_type == AF_INET6) ? (void *)&addr6 : (void *)&addr,
+ (af_type == AF_INET6) ? sizeof (addr6) : sizeof (addr),
+ af_type, my_done, chl);
+ } else {
+ *statusp = (unsigned)YP_NOKEY;
+ return (RESP_NOW);
+ }
+ }
+ if (h == 0) { /* definite immediate reject */
+ prnt(P_INFO, "immediate reject.\n");
+ free_cache_ent(deq_cache_ent(chl));
+ *statusp = (unsigned)YP_NOKEY;
+ return (RESP_NOW);
+ } else if (h == (struct nres *)-1) {
+ perror("nres failed\n");
+ *statusp = (unsigned)YP_YPERR;
+ return (RESP_NOW);
+ } else {
+ chl->nres = h;
+ /* should stash transport so my_done can answer */
+ if (try_again) {
+ *statusp = (unsigned)YP_NOMORE;
+ /* try_again overloaded */
+ return (RESP_NOW);
+
+ }
+ chl->xprt = transp;
+ if (transp) {
+ chl->caller.len = transp->xp_rtaddr.len;
+ (void) memcpy(chl->caller.buf, transp->xp_rtaddr.buf,
+ transp->xp_rtaddr.len);
+ chl->xid = svc_getxid(transp);
+ }
+ return (RESP_LATER);
+ }
+}
+
+static void
+my_done(n, h, ttl, chl, errcode)
+ void *n; /* opaque */
+ struct hostent *h;
+ ulong_t ttl;
+ struct cache_ent *chl;
+ int errcode;
+{
+ static char buf[1024];
+ char *endbuf, *tptr;
+ datum valdatp;
+ int i;
+ SVCXPRT *transp;
+ unsigned long xid_hold;
+ struct ypresp_val resp;
+ struct timezone tzp;
+ struct netbuf caller_hold, *addrp;
+ char uaddr[sizeof (struct sockaddr_in6)];
+ int af_type, bsize;
+
+ prnt(P_INFO, "my_done: %s.\n", chl->key.dptr);
+ (void) gettimeofday(&(chl->enqtime), &tzp);
+ chl->nres = 0;
+ caller_hold.maxlen = sizeof (uaddr);
+ caller_hold.buf = uaddr;
+
+ if (h == NULL) {
+ chl->h_errno = errcode;
+ if (chl->h_errno == TRY_AGAIN)
+ resp.status = (unsigned)YP_NOMORE;
+ else
+ resp.status = (unsigned)YP_NOKEY;
+ valdatp.dptr = NULL;
+ valdatp.dsize = 0;
+ } else {
+ chl->h_errno = 0;
+ chl->ttl = (ttl != 0 && ttl < PQTIME*60) ? ttl : (PQTIME*60);
+ endbuf = buf;
+ bsize = sizeof (buf);
+ bzero((void *)endbuf, bsize);
+ af_type = lookup_AF_type(chl);
+
+ /* build the return list */
+ for (i = 0; h->h_addr_list[i]; i++) {
+ tptr = endbuf;
+ (void *) inet_ntop(af_type,
+ (af_type == AF_INET6) ?
+ (void *) (h->h_addr_list[i]) :
+ (void *) (h->h_addr_list[i]),
+ endbuf, bsize);
+ endbuf = &endbuf[strlen(endbuf)];
+ bsize = buf + sizeof (buf) - endbuf;
+ (void) snprintf(endbuf, bsize, "\t%s\n", h->h_name);
+ endbuf = &endbuf[strlen(endbuf)];
+ if ((bsize = buf + sizeof (buf) - endbuf) < 300)
+ break;
+ prnt(P_INFO, "my_done: bsize=%d str=%s", bsize, tptr);
+ }
+ valdatp.dptr = buf;
+ valdatp.dsize = strlen(buf);
+ /* remove trailing newline */
+ if (valdatp.dsize) {
+ valdatp.dptr[valdatp.dsize-1] = '\0';
+ valdatp.dsize -= 1;
+ }
+ chl->val.dsize = valdatp.dsize;
+ chl->val.dptr = (char *)malloc(valdatp.dsize);
+ if (chl->val.dptr == NULL) {
+ perror("my_done");
+ free_cache_ent(deq_cache_ent(chl));
+ return;
+ }
+ (void) memcpy(chl->val.dptr, valdatp.dptr, valdatp.dsize);
+ resp.status = (unsigned)YP_TRUE;
+ }
+ /* try to answer here */
+
+ if (valdatp.dptr)
+ prnt(P_INFO, "my_done: return %s.\n", valdatp.dptr);
+ transp = chl->xprt;
+ if (transp && transp->xp_rtaddr.len <= caller_hold.maxlen) {
+ caller_hold.len = transp->xp_rtaddr.len;
+ (void) memcpy(caller_hold.buf, transp->xp_rtaddr.buf,
+ transp->xp_rtaddr.len);
+ xid_hold = svc_setxid(transp, chl->xid);
+ addrp = &(chl->caller);
+ SETCALLER(transp, addrp);
+ resp.valdat = valdatp;
+ if (!svc_sendreply(transp, (xdrproc_t)xdr_ypresp_val,
+ (char *)&resp)) {
+ return;
+ }
+ addrp = &caller_hold;
+ SETCALLER(transp, addrp);
+ (void) svc_setxid(transp, xid_hold);
+ }
+}
+
+/*
+ * this routine returns the DNS query type:
+ * T_A: IPv4
+ * T_AAAA: IPv6
+ */
+int
+lookup_T_type(struct cache_ent *chl)
+{
+
+ if (strcmp(chl->map, "ipnodes.byname") == 0) {
+ prnt(P_INFO, "lookup_T_type: T_AAAA\n");
+ return (T_AAAA);
+ } else if ((strcmp(chl->map, "hosts.byaddr") == 0) ||
+ (strcmp(chl->map, "ipnodes.byaddr") == 0)) {
+ prnt(P_INFO, "lookup_T_type: T_PTR\n");
+ return (T_PTR);
+ }
+ prnt(P_INFO, "lookup_T_type: T_A\n");
+ return (T_A);
+}
+
+
+/*
+ * this routine returns the AF type for the request:
+ * AF_INET: ipv4
+ * AF_INET6: IPv6
+ */
+int
+lookup_AF_type(struct cache_ent *chl)
+{
+ if ((strcmp(chl->map, "ipnodes.byname") == 0) ||
+ (strcmp(chl->map, "ipnodes.byaddr") == 0)) {
+ prnt(P_INFO, "lookup_AF_type: AF_INET6\n");
+ return (AF_INET6);
+ }
+ prnt(P_INFO, "lookup_AF_type: AF_INET\n");
+ return (AF_INET);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/yp_ns_proc.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/yp_ns_proc.c
new file mode 100644
index 0000000000..c3ce75f147
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/yp_ns_proc.c
@@ -0,0 +1,2312 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 1991-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * yp_ns_proc.c
+ *
+ * This module contains the implementation for backward compatibility
+ * with NIS version 2 (aka YP), so that a version 3 server (aka NIS+) can
+ * serve a version 2 client requests [only for Sun standard maps].
+ * It provides the routines that the dispatch function yp_prog_svc in
+ * yp_svc.c calls. That file, yp_svc.c, reflects
+ * the interface definition that is described in the nis.x/yp.x files.
+ *
+ * This module contains the Namespace manipulation procedures and is
+ * not supposed to access the database directly.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <rpc/rpc.h>
+#include <netinet/in.h>
+#include <netconfig.h>
+#include <netdir.h>
+#include <rpcsvc/nis.h>
+#include <rpcsvc/yp_prot.h>
+/* "nis_proc.h" includes "nis_mt.h" */
+#include "nis_proc.h"
+#undef master
+
+#include <netdb.h>
+#include <ctype.h>
+
+#define ORGDIR ".org_dir."
+#define ORGDIR1 org_dir
+#define ORGDIR2 "org_dir."
+#define ORGLEN 10 /* len(ORGDIR + null char) */
+#define DEFAULTKEY "key" /* search column name for the non-standard maps */
+#define BY "by"
+#define CNAME "cname"
+#define CNAMELEN 5
+#define MAILNAME "alias" /* name of the first column in mail_aliases */
+#define MAILADDR "expansion" /* name of the second column in mail_aliases */
+#define NGROUPNAME "name" /* column name of netgroup name in netgroup */
+#define COLVAL(n) (e->en_cols.en_cols_val[n].ENVAL && COLLEN(n) > 0) ? \
+ e->en_cols.en_cols_val[n].ENVAL : ""
+#define COLLEN(n) e->en_cols.en_cols_val[n].ENLEN
+#define COLGETVAL(n) e->en_cols.en_cols_val[n].ENVAL
+#define ENCOLVAL(e, n) e->en_cols.en_cols_val[n].ENVAL ? \
+ e->en_cols.en_cols_val[n].ENVAL : ""
+
+extern nis_object *nis_return_list(nis_object *,
+ obj_list *, int, nis_name, int *, int, int, nis_attr *);
+extern nis_object *nis_censor_object(nis_object *,
+ table_col *, nis_name);
+
+extern int resolv_flag;
+extern int resolv_pid;
+extern CLIENT *resolv_client;
+extern char *resolv_tp;
+
+long err_conv_nistoyp();
+nis_error getzobj_orgdir();
+int ypcheck_nisdir();
+struct ypresp_maplist *ypproc_maplist_svc();
+void map2table();
+bool_t xdr_ypresp_all();
+
+int serv_wart = 0;
+/* to help the two key lookup on port/proto or service/proto in services */
+
+int mail_wart_byaddr = 0;
+/* to pass the column name info from map2table to cook_record */
+
+int netid_wart = 0;
+/* the request on cred table/auth_name is for netid */
+
+int mult_lines_wart = 0;
+/* the client can handle mulitple lines of reply from ypproc_match_svc */
+
+static nis_error
+cook_record_from_entry(entry_obj *, char **, int *, char *, nis_object *);
+static void
+cook_an_ngroup(entry_obj *, char **, int *);
+static nis_error
+cook_host_record(nis_object *, int, char **, int *, struct svc_req *);
+static char *getcaller_inet();
+
+#define record (__nis_get_tsd()->yp_ns_proc_record)
+#define keyval (__nis_get_tsd()->yp_ns_proc_keyval)
+
+
+static
+int
+check_map(map)
+ char *map;
+{
+ int n;
+
+ if (map == 0 || (n = strlen(map)) == 0 || n >= YPMAXMAP)
+ return (0);
+ return (1);
+}
+
+static
+char *
+check_domain(domain)
+ char *domain;
+{
+ int n;
+
+ if (domain == 0 || *domain == 0 || strlen(domain) >= YPMAXDOMAIN)
+ return (0);
+ while (*domain && *domain == '.')
+ domain++;
+ if (*domain == 0)
+ return (0);
+ return (domain);
+}
+
+static
+int
+check_key(keydat)
+ datum *keydat;
+{
+ if (keydat->dsize == 0 || keydat->dptr == 0)
+ return (0);
+ return (1);
+}
+
+/*
+ * This determines whether or not the domain passed is served by this server,
+ * and returns a boolean.
+ */
+int *
+ypproc_domain_svc(dname, rqstp)
+ string_t *dname;
+ struct svc_req *rqstp;
+{
+ char *domain;
+#define isserved (__nis_get_tsd()->ypproc_domain_svc_isserved)
+
+ if ((domain = check_domain(*dname)) == 0)
+ return (&isserved);
+
+ if (verbose)
+ syslog(LOG_INFO, "ypserv: bind request from %s",
+ getcaller_inet(rqstp));
+ isserved = ypcheck_nisdir(domain);
+ if (!isserved)
+ syslog(LOG_ERR, "ypserv: Domain %s not supported", *dname);
+ return (&isserved);
+}
+
+#undef isserved
+
+/*
+ * We keep a list of the domains that have been broadcasted, but that
+ * we don't serve so that we will only syslog a message about it
+ * once.
+ */
+struct domain_list {
+ char *domain;
+ struct domain_list *next;
+};
+struct domain_list *domains;
+DECLMUTEXLOCK(domains);
+
+/*
+ * Check to see if dname is in the list of domains. As a side-effect
+ * we add it to the list.
+ */
+static
+int
+domain_list_check(char *dname)
+{
+ struct domain_list *dl;
+
+ MUTEXLOCK(domains, "domain_list_check(domains)");
+ for (dl = domains; dl; dl = dl->next) {
+ if (strcmp(dname, dl->domain) == 0) {
+ MUTEXUNLOCK(domains, "domain_list_check(domains)");
+ return (1);
+ }
+ }
+
+ if ((dl = malloc(sizeof (*dl))) != 0 &&
+ (dl->domain = strdup(dname)) != 0) {
+ dl->next = domains;
+ domains = dl;
+ } else if (dl != 0) {
+ free(dl);
+ }
+
+ MUTEXUNLOCK(domains, "domain_list_check(domains)");
+ return (0);
+}
+
+int *
+ypproc_domain_nonack_svc(dname, rqstp)
+ string_t *dname;
+ struct svc_req *rqstp;
+{
+ char *domain;
+#define isserved (__nis_get_tsd()->ypproc_domain_nonack_svc_isserved)
+
+ if ((domain = check_domain(*dname)) == 0)
+ return (&isserved);
+
+ if (verbose)
+ syslog(LOG_INFO, "ypserv: (broadcast) request from %s",
+ getcaller_inet(rqstp));
+ isserved = ypcheck_nisdir(domain);
+ if (isserved)
+ return (&isserved);
+ else {
+
+ /*
+ * This case is the one in which the domain is not supported,
+ * and in which we are not to respond in the unsupported
+ * case. We are going to make an error happen to allow the
+ * portmapper to end his wait without the normal udp timeout
+ * period. The assumption here is that the only process in
+ * the world which is using the function in its
+ * no-answer-if-nack form is the portmapper, which is doing
+ * the krock for pseudo-broadcast. If some poor fool calls
+ * this function as a single-cast message, the nack case will
+ * look like an incomprehensible error. Sigh... (The
+ * traditional Unix disclaimer)
+ */
+
+ svcerr_decode(rqstp->rq_xprt);
+ if (!domain_list_check(domain)) {
+ syslog(LOG_ERR,
+ "ypserv: Domain %s not supported (broadcast)",
+ *dname);
+ }
+ return (0);
+ }
+}
+
+#undef isserved
+
+/*
+ * This implements the "get master name" function.
+ */
+struct ypresp_master *
+ypproc_master_svc(req, rqstp)
+ struct ypreq_nokey *req;
+ struct svc_req *rqstp;
+{
+#define resp (__nis_get_tsd()->ypproc_master_svc_resp)
+#define masterbuf (__nis_get_tsd()->ypproc_master_svc_masterbuf)
+ nis_object *org_zobj = NULL;
+ nis_error status;
+ char *domain;
+ char *table, *column, *full_tblnm;
+ nis_db_result *dbres;
+ int i;
+
+ if ((domain = check_domain(req->domain)) == 0 ||
+ !check_map(req->map)) {
+ resp.status = YP_BADARGS;
+ return (&resp);
+ }
+
+ resp.status = YP_TRUE;
+ resp.master = masterbuf;
+ if (verbose)
+ syslog(LOG_INFO, "ypserv: yp_master MAP = %s from %s",
+ req->map, getcaller_inet(rqstp));
+ status = getzobj_orgdir(domain, &org_zobj);
+ if (status != NIS_SUCCESS || org_zobj == NULL) {
+ syslog(LOG_INFO, "ypserv: yp_master failed for %s%s.",
+ ORGDIR2, domain);
+ resp.status = err_conv_nistoyp(NIS_NAMEUNREACHABLE);
+ return (&resp);
+ }
+ strcpy(resp.master,
+ (nis_leaf_of((org_zobj)->DI_data.do_servers.do_servers_val[0].name)));
+ map2table(req->map, &table, &column);
+
+ if ((full_tblnm = (char *)XCALLOC(1, strlen(table) + ORGLEN
+ + strlen(domain) + 1)) == NULL) {
+ resp.status = err_conv_nistoyp(NIS_NOMEMORY);
+ return (&resp);
+ }
+ strcpy(full_tblnm, table);
+ strcat(full_tblnm, ORGDIR);
+ strcat(full_tblnm, domain);
+ if (*(domain + strlen(domain) - 1) != '.')
+ strcat(full_tblnm, ".");
+
+ /* Get the information base (table) object from the database */
+ dbres = db_lookup(full_tblnm);
+ /*
+ * MEMORY POLICY: db_lookup adds dbres to the cleanup list.
+ * We use it and forget it.
+ */
+ if (dbres->status != NIS_SUCCESS || (!dbres->obj)) {
+ if (dbres->status == NIS_NOTFOUND)
+ resp.status = err_conv_nistoyp(NIS_NOSUCHTABLE);
+ else
+ resp.status = err_conv_nistoyp(dbres->status);
+ XFREE(full_tblnm);
+ return (&resp);
+ }
+ if ((__type_of(dbres->obj) != NIS_TABLE_OBJ) ||
+ (column &&
+ ((i = get_keyndx(&(dbres->obj->TA_data), column)) < 0 ||
+ (!((dbres->obj->TA_data.ta_cols.ta_cols_val + i)->tc_flags &
+ TA_SEARCHABLE))))) {
+ resp.status = err_conv_nistoyp(NIS_NOSUCHTABLE);
+ }
+ XFREE(full_tblnm);
+ return (&resp);
+}
+
+#undef resp
+#undef masterbuf
+
+entry_obj *
+get_netid_entry(tobj, e, table_name, princp, err)
+ nis_object *tobj; /* cred table object */
+ entry_obj *e; /* correspoding DES entry in cred table */
+ char *table_name; /* fully qualified cred table name */
+ nis_name princp;
+ long *err;
+{
+ ib_request table_req;
+ nis_attr inkey[2];
+ nis_db_list_result *entry_obj_list;
+ nis_object *ret_en_objs;
+ int num_ntrees;
+ int all_read;
+ char *val;
+
+ val = COLVAL(2); /* netname */
+ if (((int)strlen(val) > 5) && (! isdigit(val[5]))) {
+ /*
+ * this section handles netid for root.
+ * Just return the corresponding DES entry, leave up to
+ * cook_record_from_entry() to format the return value.
+ */
+ return (e);
+ }
+ memset((char *)inkey, 0, 2*sizeof (nis_attr));
+ inkey[0].zattr_ndx = CNAME;
+ inkey[0].ZAVAL = (char *)XMALLOC(COLLEN(0));
+ add_cleanup((void (*)())XFREE,
+ (char *)inkey[0].ZAVAL, "get_netid_entry zaval");
+ memcpy(inkey[0].ZAVAL, COLVAL(0), COLLEN(0));
+ inkey[0].ZALEN = COLLEN(0);
+
+ inkey[1].zattr_ndx = "auth_type";
+ inkey[1].ZAVAL = "LOCAL";
+ inkey[1].ZALEN = 6;
+
+ table_req.ibr_srch.ibr_srch_len = 2;
+ table_req.ibr_srch.ibr_srch_val = inkey;
+ table_req.ibr_name = table_name;
+
+ entry_obj_list = db_list(table_req.ibr_name,
+ table_req.ibr_srch.ibr_srch_len,
+ table_req.ibr_srch.ibr_srch_val);
+ if (entry_obj_list->status != NIS_SUCCESS) {
+ if (entry_obj_list->status == NIS_NOTFOUND)
+ *err = YP_NOKEY;
+ else
+ *err = YP_NOMAP; /* bad column, attr etc. */
+ return (NULL);
+ }
+
+ /*
+ * Now, process the list of objects we've got to return.
+ * We know, we gonna return only one entry. The real reason for
+ * going through this is to enforce nis_return_list's censorship.
+ */
+ all_read = __can_do(NIS_READ_ACC, tobj->zo_access, tobj, princp);
+ ret_en_objs = nis_return_list(tobj, entry_obj_list->objs,
+ entry_obj_list->numo, princp, &num_ntrees, all_read,
+ 2, inkey);
+
+ if ((ret_en_objs == NULL) || !(num_ntrees) ||
+ (__type_of(ret_en_objs) != NIS_ENTRY_OBJ)) {
+ *err = YP_NOKEY;
+ return (NULL);
+ }
+ return (&(ret_en_objs->EN_data));
+}
+
+struct ypresp_val *
+ypproc_match_svc(req, rqstp)
+ struct ypreq_key *req;
+ struct svc_req *rqstp;
+{
+#define resp (__nis_get_tsd()->ypproc_match_svc_resp)
+ char *table, *column;
+ int len;
+ entry_obj *e, *netid_e;
+ char princp[1024];
+ char map[YPMAXMAP];
+ int num_ntrees;
+ nis_error status;
+
+ ib_request table_req;
+ char *t, *proto;
+ char *col_proto = "proto";
+ nis_attr inkey[2];
+ char *domain;
+ nis_db_list_result *entry_obj_list;
+ nis_object *ret_en_objs;
+ nis_db_result *dbres;
+ int i, j, all_read;
+
+ if ((domain = check_domain(req->domain)) == 0 ||
+ !check_map(req->map) ||
+ !check_key(&req->keydat)) {
+ resp.status = YP_BADARGS;
+ return (&resp);
+ }
+
+ resp.status = YP_TRUE;
+
+ if (verbose) {
+ syslog(LOG_INFO, "ypserv: yp_match MAP = %s from %s",
+ req->map, getcaller_inet(rqstp));
+ }
+
+ /*
+ * All YP requests are unauthenticated and are successful
+ * only if the database has nobody-read access.
+ */
+
+ if (resolv_flag) { /* save real map name */
+ strncpy(map, req->map, sizeof (map)-1);
+ map[sizeof (map)-1] = '\0';
+ }
+
+ map2table(req->map, &table, &column);
+
+ if ((strcmp(table, "netgroup") == 0) &&
+ (strcmp(column, NGROUPNAME) != 0)) {
+ /* We only support one netgroup YP map, "netgroup". */
+ cook_err_resp(&resp, YP_NOMAP);
+ return (&resp);
+ }
+
+ /* Cook the ib_request */
+ if ((table_req.ibr_name = (char *)XCALLOC(1, \
+ (strlen(table) + ORGLEN + strlen(domain) + 1))) ==
+ NULL) {
+ cook_err_resp(&resp, err_conv_nistoyp(NIS_NOMEMORY));
+ return (&resp);
+ }
+ strcpy(table_req.ibr_name, table);
+ strcat(table_req.ibr_name, ORGDIR);
+ strcat(table_req.ibr_name, domain);
+ if (*(domain + strlen(domain) - 1) != '.')
+ strcat(table_req.ibr_name, ".");
+
+ /* Get the information base (table) object from the database */
+ dbres = db_lookup(table_req.ibr_name);
+ /*
+ * MEMORY POLICY: db_lookup adds dbres to the cleanup list.
+ * We use it and forget it.
+ */
+ if (dbres->status != NIS_SUCCESS || (!dbres->obj)) {
+ if (dbres->status == NIS_NOTFOUND)
+ cook_err_resp(&resp, err_conv_nistoyp(NIS_NOSUCHTABLE));
+ else
+ cook_err_resp(&resp, err_conv_nistoyp(dbres->status));
+ XFREE(table_req.ibr_name);
+ return (&resp);
+ }
+ if (__type_of(dbres->obj) != NIS_TABLE_OBJ) {
+ /* there goes yp's limited knowledge of the NIS+ universe ! */
+ cook_err_resp(&resp, err_conv_nistoyp(NIS_NOSUCHTABLE));
+ XFREE(table_req.ibr_name);
+ return (&resp);
+ }
+
+ /*
+ * If we are using the default key check that the the map actually
+ * contains this column. If not the log an informational error instead
+ * of proceeding to the lookup where a more serious error would be
+ * produced.
+ */
+ if (strcmp(column, DEFAULTKEY) == 0) {
+ if (get_keyndx(dbres->obj->TA_data, column) < 0) {
+ syslog(LOG_INFO,
+ "%s is not explicitly supported in YP "
+ "compatibility mode. Unsupported NIS+ tables "
+ "must have column named \'%s\'.",
+ table, DEFAULTKEY);
+ cook_err_resp(&resp, err_conv_nistoyp(NIS_NOSUCHTABLE));
+ XFREE(table_req.ibr_name);
+ return (&resp);
+ }
+ }
+
+ /* Cook the nis_attr key */
+ memset((char *)inkey, 0, 2*sizeof (nis_attr));
+ inkey[0].zattr_ndx = column;
+ /*
+ * ASSERT: column is never NULL,
+ * could be client supplied or DEFAULTKEY
+ */
+
+ /*
+ * Do the jugglery to conform to the NIS+ convention of null
+ * terminated data in the database. Cannot rely on YP
+ * that it will always pass a null terminated key.
+ */
+ i = req->keydat.dsize;
+ if (req->keydat.dptr[i-1] != '\0')
+ i++;
+ inkey[0].ZAVAL = (char *)XMALLOC(i);
+ inkey[0].ZALEN = i;
+ if (inkey[0].ZAVAL == NULL) {
+ cook_err_resp(&resp, err_conv_nistoyp(NIS_NOMEMORY));
+ XFREE(table_req.ibr_name);
+ return (&resp);
+ }
+ strncpy(inkey[0].ZAVAL, req->keydat.dptr, req->keydat.dsize);
+ inkey[0].ZAVAL[i-1] = '\0';
+
+ cook_err_resp(&resp, err_conv_nistoyp(NIS_SUCCESS));
+
+ /* Now, do the actual lookup for an entry we want */
+ table_req.ibr_srch.ibr_srch_len = 1;
+ table_req.ibr_srch.ibr_srch_val = inkey;
+
+ if (serv_wart && (strcmp(table, "services") == 0) &&
+ ((proto = strchr(inkey[0].ZAVAL, '/')) != NULL)) {
+ *proto++ = '\0';
+ inkey[1].zattr_ndx = col_proto;
+ inkey[1].ZALEN = (strlen(proto) + 1);
+ inkey[1].ZAVAL = proto;
+ inkey[0].ZALEN = i - inkey[1].ZALEN;
+ table_req.ibr_srch.ibr_srch_len++;
+ }
+
+ if (strcmp(table, "cred") == 0) {
+ /* only make the publickey.byname type of information visible */
+ inkey[1].zattr_ndx = "auth_type";
+ table_req.ibr_srch.ibr_srch_len++;
+ inkey[1].ZAVAL = "DES";
+ inkey[1].ZALEN = 4;
+ }
+
+ entry_obj_list = db_list(table_req.ibr_name,
+ table_req.ibr_srch.ibr_srch_len,
+ table_req.ibr_srch.ibr_srch_val);
+ if (entry_obj_list->status != NIS_SUCCESS) {
+ if (entry_obj_list->status == NIS_NOTFOUND)
+ if (resolv_flag &&
+ resolv_req(&resolv_flag, &resolv_client,
+ &resolv_pid, resolv_tp,
+ rqstp->rq_xprt, req, map)) {
+ XFREE(table_req.ibr_name);
+ XFREE(inkey[0].ZAVAL);
+ return (NULL); /* fwd'ed req: skip reply */
+ } else
+ cook_err_resp(&resp, YP_NOKEY);
+ else
+ /* bad column, attr etc. */
+ cook_err_resp(&resp, YP_NOMAP);
+ XFREE(table_req.ibr_name);
+ XFREE(inkey[0].ZAVAL);
+ return (&resp);
+ }
+
+ /*
+ * Now, process the list of objects we've got to return.
+ * We know, we gonna return only one entry. The real reason for
+ * going through this is to enforce nis_return_list's censorship.
+ */
+ nis_getprincipal(princp, rqstp);
+ all_read = __can_do(NIS_READ_ACC, dbres->obj->zo_access,
+ dbres->obj, princp);
+ ret_en_objs = nis_return_list(dbres->obj, entry_obj_list->objs,
+ entry_obj_list->numo, princp, &num_ntrees, all_read,
+ 2, inkey);
+
+ if ((ret_en_objs == NULL) || !(num_ntrees) ||
+ (__type_of(ret_en_objs) != NIS_ENTRY_OBJ)) {
+ cook_err_resp(&resp, YP_NOKEY);
+ XFREE(table_req.ibr_name);
+ XFREE(inkey[0].ZAVAL);
+ return (&resp);
+ }
+ if (strcmp(table, "netgroup") == 0) {
+ /*
+ * This is as grungy as any code can get. The
+ * netgroup format of the NIS+ table is quite
+ * different from that in YP; for instance, we
+ * do not have "reverse" lookup support. We write
+ * the complete code here.
+ */
+ if (strcmp(column, NGROUPNAME) == 0) {
+ int i = 0, len;
+ char *ngrp;
+
+ ngrp = XMALLOC(YPMAXRECORD);
+ add_cleanup((void (*)())XFREE,
+ (char *)ngrp, "yp (match) mem");
+ memset(record, 0, YPMAXRECORD);
+ resp.valdat.dptr = record;
+ while ((ret_en_objs + i) && (i < num_ntrees)) {
+ e = &((ret_en_objs + i)->EN_data);
+ len = 0;
+ cook_an_ngroup(e, &ngrp, &len);
+ /*
+ * len returned here includes the
+ * NULL character '\0'
+ */
+ if ((strlen(record) + len) > YPMAXRECORD)
+ break;
+ strcat(record, ngrp);
+ strcat(record, " ");
+ i++;
+ }
+ if ((num_ntrees == 1) &&
+ (strcmp(COLVAL(5), "") != 0) &&
+ ((strlen(record) + COLLEN(5) + 3) <=
+ YPMAXRECORD)) {
+ /*
+ * the special case of leaf netgroup,
+ * print comments
+ */
+ strcat(record, "\t#");
+ strcat(record, COLVAL(5));
+ }
+ resp.valdat.dsize = strlen(record);
+ } else
+ cook_err_resp(&resp, YP_NOMAP);
+ XFREE(table_req.ibr_name);
+ XFREE(inkey[0].ZAVAL);
+ return (&resp);
+ }
+
+ e = &(ret_en_objs->EN_data);
+ if (netid_wart) {
+ long yperr = YP_TRUE;
+
+ netid_e = e;
+ e = get_netid_entry(dbres->obj, netid_e,
+ table_req.ibr_name, princp, &yperr);
+ if (! e) {
+ cook_err_resp(&resp, yperr);
+ XFREE(table_req.ibr_name);
+ XFREE(inkey[0].ZAVAL);
+ return (&resp);
+ }
+ }
+
+ /* construct a complete entry from all columns */
+ memset(record, 0, YPMAXRECORD);
+ t = record;
+ if (strcmp(table, "hosts") == 0) {
+ status = cook_host_record(ret_en_objs, num_ntrees,
+ &t, &len, rqstp);
+ } else {
+ status = cook_record_from_entry(e, &t, &len, table, dbres->obj);
+ }
+ if (status != NIS_SUCCESS) {
+ cook_err_resp(&resp, err_conv_nistoyp(status));
+ XFREE(table_req.ibr_name);
+ XFREE(inkey[0].ZAVAL);
+ return (&resp);
+ }
+
+ resp.valdat.dptr = record;
+ resp.valdat.dsize = len;
+
+ XFREE(table_req.ibr_name);
+ XFREE(inkey[0].ZAVAL);
+ return (&resp);
+}
+
+#undef resp
+
+struct ypresp_key_val *
+ypproc_first_svc(req, rqstp)
+ struct ypreq_nokey *req;
+ struct svc_req *rqstp;
+{
+#define resp (__nis_get_tsd()->ypproc_first_svc_resp)
+ char *table, *column, *full_tblnm, *t;
+ int len, key_ndx;
+ entry_obj *e, *netid_e = NULL;
+ nis_error status;
+ char *domain;
+ nis_object *ret_en_objs;
+ nis_db_result *dbres;
+ nis_fn_result *fnr;
+ char princp[1024];
+ netobj cookie;
+ int all_readable = 0;
+
+ if ((domain = check_domain(req->domain)) == 0 ||
+ !check_map(req->map)) {
+ resp.status = YP_BADARGS;
+ return (&resp);
+ }
+
+ resp.status = YP_TRUE;
+ if (verbose)
+ syslog(LOG_INFO, "ypserv: yp_first MAP = %s from %s",
+ req->map, getcaller_inet(rqstp));
+
+ /*
+ * All YP requests are unauthenticated and are successful
+ * only if the database has nobody-read access.
+ */
+
+ map2table(req->map, &table, &column);
+ if (strcmp(table, "netgroup") == 0) {
+ cook_err_keyresp(&resp, YP_NOMORE);
+ return (&resp);
+ }
+
+ if ((full_tblnm = (char *)XCALLOC(1, strlen(table) + ORGLEN
+ + strlen(domain) + 1)) ==
+ NULL) {
+ cook_err_keyresp(&resp, err_conv_nistoyp(NIS_NOMEMORY));
+ return (&resp);
+ }
+ strcpy(full_tblnm, table);
+ strcat(full_tblnm, ORGDIR);
+ strcat(full_tblnm, domain);
+ if (*(domain + strlen(domain) - 1) != '.')
+ strcat(full_tblnm, ".");
+
+ /* Get the information base (table) object from the database */
+ dbres = db_lookup(full_tblnm);
+ /*
+ * MEMORY POLICY: db_lookup adds dbres to the cleanup list.
+ * We use it and forget it.
+ */
+ if (dbres->status != NIS_SUCCESS || (!dbres->obj)) {
+ if (dbres->status == NIS_NOTFOUND)
+ cook_err_keyresp(&resp,
+ err_conv_nistoyp(NIS_NOSUCHTABLE));
+ else
+ cook_err_keyresp(&resp,
+ err_conv_nistoyp(dbres->status));
+ XFREE(full_tblnm);
+ return (&resp);
+ }
+ if (__type_of(dbres->obj) != NIS_TABLE_OBJ) {
+ cook_err_keyresp(&resp, err_conv_nistoyp(NIS_NOSUCHTABLE));
+ XFREE(full_tblnm);
+ return (&resp);
+ }
+
+ nis_getprincipal(princp, rqstp);
+ all_readable = __can_do(NIS_READ_ACC, dbres->obj->zo_access,
+ dbres->obj, princp);
+ /*
+ * MEMORY POLICY on db_firstib/nextib: always call it with
+ * !(flags & 2), the db_*ib routines will add fnr and fnr->obj
+ * to the cleanup list. We never free cookie.n_bytes if the
+ * cookie is to be passwd to db_nextib(). db_nextib() always
+ * frees the cookie it is given.
+ */
+ fnr = db_firstib(full_tblnm, 0, NULL, FN_NOMANGLE, NULL);
+ if (fnr->status != NIS_SUCCESS) {
+ cook_err_keyresp(&resp, YP_NOMORE);
+ XFREE(full_tblnm);
+ return (&resp);
+ }
+ cookie = fnr->cookie;
+doagain:
+ do {
+ if (all_readable) {
+ ret_en_objs = fnr->obj;
+ break;
+ } else {
+ ret_en_objs = nis_censor_object(fnr->obj,
+ dbres->obj->TA_data.ta_cols.ta_cols_val,
+ princp);
+ }
+ if (! ret_en_objs) {
+ fnr = db_nextib(full_tblnm, &cookie, 0, NULL);
+ if (fnr->status != NIS_SUCCESS) {
+ cook_err_keyresp(&resp, YP_NOMORE);
+ XFREE(full_tblnm);
+ return (&resp);
+ }
+ cookie = fnr->cookie;
+ }
+ } while ((ret_en_objs == NULL) && (fnr->status == NIS_SUCCESS));
+
+ if (fnr->status != NIS_SUCCESS) {
+ cook_err_keyresp(&resp, YP_NOMORE);
+ if (ret_en_objs && !all_readable)
+ nis_destroy_object(ret_en_objs);
+ XFREE(full_tblnm);
+ return (&resp);
+ }
+ if (__type_of(ret_en_objs) != NIS_ENTRY_OBJ) {
+ cook_err_keyresp(&resp, YP_NOKEY);
+ if (ret_en_objs && !all_readable)
+ nis_destroy_object(ret_en_objs);
+ XFREE(full_tblnm);
+ return (&resp);
+ }
+ e = &(ret_en_objs->EN_data);
+
+ if ((strcmp(table, "cred") == 0) &&
+ (strcmp(COLVAL(1), "DES") != 0)) {
+ fnr = db_nextib(full_tblnm, &cookie, 0, NULL);
+ if (fnr->status != NIS_SUCCESS) {
+ cook_err_keyresp(&resp, YP_NOMORE);
+ if (ret_en_objs && !all_readable)
+ nis_destroy_object(ret_en_objs);
+ XFREE(full_tblnm);
+ return (&resp);
+ }
+ cookie = fnr->cookie;
+ if (ret_en_objs && !all_readable)
+ nis_destroy_object(ret_en_objs);
+ goto doagain;
+ }
+
+ if ((strcmp(table, "cred") == 0) &&
+ (strcmp(COLVAL(1), "DES") == 0) &&
+ netid_wart) {
+ long yperr = YP_TRUE;
+
+ netid_e = e;
+ e = get_netid_entry(dbres->obj,
+ netid_e, full_tblnm, princp, &yperr);
+ if (! e) {
+ fnr = db_nextib(full_tblnm, &cookie, 0, NULL);
+ if (fnr->status != NIS_SUCCESS) {
+ cook_err_keyresp(&resp, YP_NOMORE);
+ if (ret_en_objs && !all_readable)
+ nis_destroy_object(ret_en_objs);
+ XFREE(full_tblnm);
+ return (&resp);
+ }
+ cookie = fnr->cookie;
+ if (ret_en_objs && !all_readable)
+ nis_destroy_object(ret_en_objs);
+ goto doagain;
+ }
+ }
+ add_cleanup((void (*)())XFREE,
+ (char *)cookie.n_bytes, "yp (first) cookie");
+ XFREE(full_tblnm);
+
+ /* construct a complete entry from all columns */
+ memset(record, 0, YPMAXRECORD);
+ t = &(record[0]);
+ status = cook_record_from_entry(e, &t, &len, table, dbres->obj);
+ if (status != NIS_SUCCESS) {
+ if (ret_en_objs && !all_readable)
+ nis_destroy_object(ret_en_objs);
+ cook_err_keyresp(&resp, err_conv_nistoyp(status));
+ return (&resp);
+ }
+
+ key_ndx = get_keyndx(&(dbres->obj->TA_data), column);
+ if ((key_ndx < 0) || (key_ndx > (e->en_cols.en_cols_len - 1))) {
+ if (ret_en_objs && !all_readable)
+ nis_destroy_object(ret_en_objs);
+ cook_err_keyresp(&resp, YP_NOKEY);
+ return (&resp);
+ } else {
+ /*
+ * We are cheating the yp_first/next client by sending the pattern
+ * "<key>\n\0#<cookie><int>" as the key in resp.keydat. They loose
+ * if they don't send the same key back to us or send it back after
+ * str*cpy()'ing to a new place.
+ */
+ int enlen;
+ char *enval;
+ char *pattern = "\n\0#";
+ int patlen = 3;
+
+ if (netid_wart && netid_e)
+ e = netid_e;
+ enlen = COLLEN(key_ndx);
+ enval = COLVAL(key_ndx);
+ resp.keydat.dptr =
+ XMALLOC(enlen + patlen + cookie.n_len + sizeof (int));
+ add_cleanup((void (*)())XFREE, (char *)resp.keydat.dptr,
+ "yp (first) resp.keydat.dptr");
+ resp.keydat.dsize = enlen + patlen + cookie.n_len +
+ sizeof (int);
+ memcpy(resp.keydat.dptr, enval, enlen);
+ memcpy(resp.keydat.dptr + enlen, pattern, patlen);
+ memcpy((resp.keydat.dptr + enlen + patlen), cookie.n_bytes,
+ cookie.n_len);
+ /* skip these many bytes before getting to the cookie */
+ enlen += patlen;
+ memcpy((resp.keydat.dptr + enlen + cookie.n_len),
+ (char *)&enlen, sizeof (int));
+ }
+
+ if (ret_en_objs && !all_readable)
+ nis_destroy_object(ret_en_objs);
+ resp.valdat.dptr = record;
+ resp.valdat.dsize = len;
+
+ return (&resp);
+}
+
+#undef resp
+
+struct ypresp_key_val *
+ypproc_next_svc(req, rqstp)
+ struct ypreq_key *req;
+ struct svc_req *rqstp;
+{
+#define resp (__nis_get_tsd()->ypproc_next_svc_resp)
+ char *t, *table, *column, *full_tblnm;
+ int len;
+ entry_obj *e, *netid_e = NULL;
+ nis_error status;
+ char *domain;
+ nis_object *ret_en_objs;
+ nis_db_result *dbres;
+ char princp[1024];
+ nis_fn_result *fnr;
+ netobj cookie;
+ int key_ndx;
+ int all_readable = 0;
+
+ if ((domain = check_domain(req->domain)) == 0 ||
+ !check_map(req->map) ||
+ !check_key(&req->keydat)) {
+ resp.status = YP_BADARGS;
+ return (&resp);
+ }
+
+ resp.status = YP_TRUE;
+ if (verbose)
+ syslog(LOG_INFO, "ypserv: yp_next MAP = %s from %s",
+ req->map, getcaller_inet(rqstp));
+
+ /*
+ * All YP requests are unauthenticated and are successful
+ * only if the database has nobody-read access.
+ */
+
+ map2table(req->map, &table, &column);
+ if (strcmp(table, "netgroup") == 0) {
+ cook_err_keyresp(&resp, YP_NOMORE);
+ return (&resp);
+ }
+
+ if ((full_tblnm = (char *)XCALLOC(1, strlen(table) + ORGLEN
+ + strlen(domain) + 1)) == NULL) {
+ cook_err_keyresp(&resp, YP_YPERR);
+ return (&resp);
+ }
+ strcpy(full_tblnm, table);
+ strcat(full_tblnm, ORGDIR);
+ strcat(full_tblnm, domain);
+ if (*(domain + strlen(domain) - 1) != '.')
+ strcat(full_tblnm, ".");
+
+ /* Get the information base (table) object from the database */
+ dbres = db_lookup(full_tblnm);
+ /*
+ * MEMORY POLICY: db_lookup adds dbres to the cleanup list.
+ * We use it and forget it.
+ */
+ if (dbres->status != NIS_SUCCESS || (!dbres->obj)) {
+ if (dbres->status == NIS_NOTFOUND)
+ cook_err_keyresp(&resp,
+ err_conv_nistoyp(NIS_NOSUCHTABLE));
+ else
+ cook_err_keyresp(&resp,
+ err_conv_nistoyp(dbres->status));
+ XFREE(full_tblnm);
+ return (&resp);
+ }
+ if (__type_of(dbres->obj) != NIS_TABLE_OBJ) {
+ /* there goes yp's limited knowledge of the NIS+ universe ! */
+ cook_err_keyresp(&resp, err_conv_nistoyp(NIS_NOSUCHTABLE));
+ XFREE(full_tblnm);
+ return (&resp);
+ }
+
+ /*
+ * We have cheated the yp_first/next client by sending the pattern
+ * "<key>\n\0#<cookie><int>" as the key in resp.keydat. They loose
+ * if they haven't sent the same key back to us or sent it back after
+ * str*cpy()'ing to a new place.
+ */
+ memcpy((char *)&len,
+ (req->keydat.dptr + req->keydat.dsize - sizeof (int)),
+ sizeof (int));
+ if ((len < 0) || (len > req->keydat.dsize)) {
+ /* We got a cookie we can't understand */
+ cook_err_keyresp(&resp, YP_NOMORE);
+ XFREE(full_tblnm);
+ return (&resp);
+ }
+ t = (req->keydat.dptr) + len;
+ if ((*(t - 1) != '#') || (*(t -2) != '\0')) {
+ /* We got a cookie we can't understand */
+ cook_err_keyresp(&resp, YP_NOMORE);
+ XFREE(full_tblnm);
+ return (&resp);
+ }
+ cookie.n_len = req->keydat.dsize - len - sizeof (int);
+ cookie.n_bytes = XMALLOC(cookie.n_len);
+ memcpy(cookie.n_bytes, t, cookie.n_len);
+
+ nis_getprincipal(princp, rqstp);
+ all_readable = __can_do(NIS_READ_ACC, dbres->obj->zo_access,
+ dbres->obj, princp);
+ fnr = db_nextib(full_tblnm, &cookie, 0, NULL);
+ if (fnr->status != NIS_SUCCESS) {
+ cook_err_keyresp(&resp, YP_NOMORE);
+ XFREE(full_tblnm);
+ return (&resp);
+ }
+ cookie = fnr->cookie;
+doagain:
+ do {
+ if (all_readable) {
+ ret_en_objs = fnr->obj;
+ break;
+ } else {
+ ret_en_objs = nis_censor_object(fnr->obj,
+ dbres->obj->TA_data.ta_cols.ta_cols_val,
+ princp);
+ }
+ if (! ret_en_objs) {
+ fnr = db_nextib(full_tblnm, &cookie, 0, NULL);
+ if (fnr->status != NIS_SUCCESS) {
+ cook_err_keyresp(&resp, YP_NOMORE);
+ XFREE(full_tblnm);
+ return (&resp);
+ }
+ cookie = fnr->cookie;
+ }
+ } while ((ret_en_objs == NULL) && (fnr->status == NIS_SUCCESS));
+
+ if (fnr->status != NIS_SUCCESS) {
+ cook_err_keyresp(&resp, YP_NOMORE);
+ if (ret_en_objs && !all_readable)
+ nis_destroy_object(ret_en_objs);
+ XFREE(full_tblnm);
+ return (&resp);
+ }
+ if (__type_of(ret_en_objs) != NIS_ENTRY_OBJ) {
+ cook_err_keyresp(&resp, YP_NOKEY);
+ if (ret_en_objs && !all_readable)
+ nis_destroy_object(ret_en_objs);
+ XFREE(full_tblnm);
+ return (&resp);
+ }
+ e = &(ret_en_objs->EN_data);
+
+ if ((strcmp(table, "cred") == 0) &&
+ (strcmp(COLVAL(1), "DES") != 0)) {
+ fnr = db_nextib(full_tblnm, &cookie, 0, NULL);
+ if (fnr->status != NIS_SUCCESS) {
+ cook_err_keyresp(&resp, YP_NOMORE);
+ if (ret_en_objs && !all_readable)
+ nis_destroy_object(ret_en_objs);
+ XFREE(full_tblnm);
+ return (&resp);
+ }
+ cookie = fnr->cookie;
+ if (ret_en_objs && !all_readable)
+ nis_destroy_object(ret_en_objs);
+ goto doagain;
+ }
+
+ if ((strcmp(table, "cred") == 0) &&
+ (strcmp(COLVAL(1), "DES") == 0) &&
+ netid_wart) {
+ long yperr = YP_TRUE;
+
+ netid_e = e;
+ e = get_netid_entry(dbres->obj,
+ netid_e, full_tblnm, princp, &yperr);
+ if (! e) {
+ fnr = db_nextib(full_tblnm, &cookie, 0, NULL);
+ if (fnr->status != NIS_SUCCESS) {
+ cook_err_keyresp(&resp, YP_NOMORE);
+ if (ret_en_objs && !all_readable)
+ nis_destroy_object(ret_en_objs);
+ XFREE(full_tblnm);
+ return (&resp);
+ }
+ cookie = fnr->cookie;
+ if (ret_en_objs && !all_readable)
+ nis_destroy_object(ret_en_objs);
+ goto doagain;
+ }
+ }
+ add_cleanup((void (*)())XFREE,
+ (char *)cookie.n_bytes, "yp (next) cookie");
+ XFREE(full_tblnm);
+
+ /* construct a complete entry from all columns */
+ memset(record, 0, YPMAXRECORD);
+ t = &(record[0]);
+ status = cook_record_from_entry(e, &t, &len, table, dbres->obj);
+ if (status != NIS_SUCCESS) {
+ cook_err_keyresp(&resp, err_conv_nistoyp(status));
+ if (ret_en_objs && !all_readable)
+ nis_destroy_object(ret_en_objs);
+ return (&resp);
+ }
+
+ key_ndx = get_keyndx(&(dbres->obj->TA_data), column);
+ if ((key_ndx < 0) || (key_ndx > (e->en_cols.en_cols_len - 1))) {
+ if (ret_en_objs && !all_readable)
+ nis_destroy_object(ret_en_objs);
+ cook_err_keyresp(&resp, YP_NOMORE);
+ return (&resp);
+ } else {
+ /*
+ * We are cheating the yp_first/next client by sending the pattern
+ * "<key>\n\0#<cookie><int>" as the key in resp.keydat. They loose
+ * if they don't send the same key back to us or send it back after
+ * str*cpy()'ing to a new place.
+ */
+ int enlen;
+ char *enval;
+ char *pattern = "\n\0#";
+ int patlen = 3;
+
+ if (netid_wart && netid_e)
+ e = netid_e;
+ enlen = COLLEN(key_ndx);
+ enval = COLVAL(key_ndx);
+ resp.keydat.dptr = XMALLOC(enlen + patlen + cookie.n_len +
+ sizeof (int));
+ add_cleanup((void (*)())XFREE, (char *)resp.keydat.dptr,
+ "yp (next) resp.keydat.dptr");
+ resp.keydat.dsize = enlen + patlen + cookie.n_len +
+ sizeof (int);
+ memcpy(resp.keydat.dptr, enval, enlen);
+ memcpy(resp.keydat.dptr + enlen, pattern, patlen);
+ memcpy((resp.keydat.dptr + enlen + patlen), cookie.n_bytes,
+ cookie.n_len);
+ /* skip these many bytes before getting to the cookie */
+ enlen += patlen;
+ memcpy((resp.keydat.dptr + enlen + cookie.n_len),
+ (char *)&enlen, sizeof (int));
+ }
+
+ if (ret_en_objs && !all_readable)
+ nis_destroy_object(ret_en_objs);
+
+ resp.valdat.dptr = record;
+ resp.valdat.dsize = len;
+ return (&resp);
+}
+
+#undef resp
+
+struct ypresp_all *
+ypproc_all_svc(req, rqstp)
+ struct ypreq_nokey *req;
+ struct svc_req *rqstp;
+{
+#define resp (__nis_get_tsd()->ypproc_all_svc_resp)
+ char *table, *column, *full_tblnm;
+ nis_error status;
+ char *domain;
+ pid_t pid;
+ SVCXPRT *transp = rqstp->rq_xprt;
+
+ nis_db_result *dbres;
+ char princp[1024];
+
+ if ((domain = check_domain(req->domain)) == 0 ||
+ !check_map(req->map)) {
+ resp.status = YP_BADARGS;
+ return (&resp);
+ }
+
+ memset((char *)&resp, 0, sizeof (struct ypresp_all));
+ resp.status = YP_TRUE;
+ if (verbose)
+ syslog(LOG_INFO, "ypserv: yp_all MAP = %s from %s",
+ req->map, getcaller_inet(rqstp));
+
+ /*
+ * All YP requests are unauthenticated and are successful
+ * only if the database has nobody-read access.
+ */
+
+ map2table(req->map, &table, &column);
+ if (strcmp(table, "netgroup") == 0) {
+ resp.status = YP_NOMORE;
+ return (&resp);
+ }
+
+ if ((full_tblnm = (char *)XCALLOC(1, strlen(table) + ORGLEN
+ + strlen(domain) + 1)) == NULL) {
+ resp.status = YP_YPERR;
+ return (&resp);
+ }
+ add_cleanup((void (*)())XFREE,
+ (char *)full_tblnm, "yp (all) full_tblnm");
+ strcpy(full_tblnm, table);
+ strcat(full_tblnm, ORGDIR);
+ strcat(full_tblnm, domain);
+ if (*(domain + strlen(domain) - 1) != '.')
+ strcat(full_tblnm, ".");
+
+ /* Get the information base (table) object from the database */
+ dbres = db_lookup(full_tblnm);
+ /*
+ * MEMORY POLICY: db_lookup adds dbres to the cleanup list.
+ * We use it and forget it.
+ */
+ if (dbres->status != NIS_SUCCESS || (!dbres->obj)) {
+ if (dbres->status == NIS_NOTFOUND)
+ resp.status = err_conv_nistoyp(NIS_NOSUCHTABLE);
+ else
+ resp.status = err_conv_nistoyp(dbres->status);
+ XFREE(full_tblnm);
+ return (&resp);
+ }
+ if ((__type_of(dbres->obj) != NIS_TABLE_OBJ) ||
+ (column && (strcmp(column, DEFAULTKEY) != 0) &&
+((resp.key_column_ndx = get_keyndx(&(dbres->obj->TA_data), column)) < 0 ||
+ (!((dbres->obj->TA_data.ta_cols.ta_cols_val +
+ resp.key_column_ndx)->tc_flags & TA_SEARCHABLE))))) {
+ resp.status = err_conv_nistoyp(NIS_NOSUCHTABLE);
+ XFREE(full_tblnm);
+ return (&resp);
+ }
+
+ nis_getprincipal(princp, rqstp);
+ resp.table_name = full_tblnm;
+ resp.princp = strdup(princp);
+ add_cleanup((void (*)()) XFREE, (char *)resp.princp, "principal name");
+ resp.status = YP_TRUE; /* so far, so good. */
+ resp.table_zobj = dbres->obj;
+
+ /*
+ * Send reply from this thread without forking. An alternative
+ * implementation would be to create a new thread to send the
+ * reply; both have their complementary advantages and disadvantages:
+ *
+ * Send reply from this thread:
+ *
+ * Simpler to implement
+ *
+ * No new thread created, so probably slightly quicker
+ *
+ * No runaway resource usage (i.e., lots of threads) in
+ * rpc.nisd
+ *
+ * Don't have to worry about 'transp' (= rqstp->rq_xprt)
+ * possibly becoming invalid while a child thread is running.
+ *
+ * Send reply from a new thread:
+ *
+ * Less risk of denial-of-service if yp_all() calls use up
+ * all of the auto RPC mode service threads
+ *
+ * We opt for simplicity.
+ */
+ if (!svc_sendreply(transp,
+ (xdrproc_t)xdr_ypresp_all, (char *)&resp)) {
+ svcerr_systemerr(transp);
+ }
+
+ return (0);
+}
+
+#undef resp
+
+static void
+free_ypmaplist(maplist)
+ struct ypmaplist *maplist;
+{
+ struct ypmaplist *tmp;
+ while (maplist) {
+ tmp = maplist->ypml_next;
+ (void) XFREE(maplist);
+ maplist = tmp;
+ }
+}
+
+struct ypresp_maplist *
+ypproc_maplist_svc(dname, rqstp)
+ string_t *dname;
+ struct svc_req *rqstp;
+{
+#define maplist (__nis_get_tsd()->ypproc_maplist_svc_maplist)
+ struct ypmaplist *map, *mapnames;
+ nis_object *z_obj = NULL;
+ nis_error status;
+ char *domain;
+ int namesz, i;
+ char orgdir[YPMAXDOMAIN + ORGLEN];
+ nis_fn_result *fnr;
+ netobj cookie;
+
+ if ((domain = check_domain(*dname)) == 0) {
+ maplist.status = YP_BADARGS;
+ maplist.list = 0;
+ return (&maplist);
+ }
+
+ maplist.status = YP_TRUE;
+ maplist.list = NULL;
+ if (verbose)
+ syslog(LOG_INFO, "ypserv: yp_maplist from %s",
+ getcaller_inet(rqstp));
+ status = getzobj_orgdir(domain, &z_obj);
+ if (status != NIS_SUCCESS || z_obj == NULL) {
+ syslog(LOG_INFO, "ypserv: yp_maplist failed for ORGDIR1.%s.",
+ domain);
+ maplist.status = err_conv_nistoyp(NIS_NAMEUNREACHABLE);
+ return (&maplist);
+ }
+ strcpy(orgdir, ORGDIR2);
+ strcat(orgdir, domain);
+ if (*(domain + strlen(domain) - 1) != '.')
+ strcat(orgdir, ".");
+
+ /*
+ * MEMORY POLICY on db_firstib/nextib: always call it with
+ * !(flags & 2), the db_*ib routines will add fnr and fnr->obj
+ * to the cleanup list. We never free cookie.n_bytes if the
+ * cookie is to be passwd to db_nextib(). db_nextib() always
+ * frees the cookie it is given.
+ */
+ fnr = db_firstib(orgdir, 0, NULL, FN_NOMANGLE, NULL);
+ while ((fnr->status == NIS_SUCCESS) &&
+ (__type_of(fnr->obj) == NIS_TABLE_OBJ)) {
+ char *tblname, mname[YPMAXMAP + 1];
+ struct table_obj *t_obj;
+ struct table_col *tcol;
+
+ cookie = fnr->cookie;
+ memset(mname, 0, YPMAXMAP+1);
+ /*
+ * POLICY: For each table name, the corresponding map names
+ * are concocted by expanding <tablename>.by<approriate_column>,
+ * where appropriate_column is a searchable column in the
+ * table that does not have the names CNAME (ignore this) or
+ * DEFAULTKEY (expand only to <tablename>). Idea is that the
+ * client MUST be able to lookup EVERY such mapname returned,
+ * should not return too many mapnames and should not confuse
+ * users by renaming conventional NIS mapnames.
+ *
+ * HACKS: As a consequence of the above policy, we must do
+ * something special to rename all the "auto_*" tables to
+ * "auto.*", suppress printing "services.byproto", print
+ * a new map "services.byservicename" that really looks at the
+ * key "name" in the services table (look at map2table() for
+ * the explanation of this hack), and print "mail.aliases"
+ * and "mail.byaddr" as the only two maps for the mail_aliases
+ * table.
+ */
+ tblname = fnr->obj->zo_name;
+ t_obj = &(fnr->obj->TA_data);
+ for (i = 0; i < t_obj->ta_cols.ta_cols_len; i++) {
+ tcol = t_obj->ta_cols.ta_cols_val + i;
+ if (!(tcol->tc_flags & TA_SEARCHABLE) ||
+ (strcmp(CNAME, tcol->tc_name) == 0) ||
+ (strlen(tblname) + strlen(tcol->tc_name) + 3) >
+ YPMAXMAP) {
+ continue;
+ }
+
+ if (strcmp(tblname, "mail_aliases") == 0) {
+ if (strcmp(MAILNAME, tcol->tc_name) == 0)
+ strcpy(mname, "mail.aliases");
+ else if (strcmp(MAILADDR, tcol->tc_name) == 0)
+ strcpy(mname, "mail.byaddr");
+ else
+ continue;
+ } else if (strncmp(tblname, "auto_", 5) == 0) {
+ char *ptr;
+ strcpy(mname, tblname);
+ ptr = strchr(mname, '_');
+ if (ptr)
+ *ptr = '.';
+ } else if (strcmp(tblname, "services") == 0) {
+ if (strcmp("name", tcol->tc_name) == 0)
+ strcpy(mname, "services.byservicename");
+ else if (strcmp("port", tcol->tc_name) == 0)
+ strcpy(mname, "services.byname");
+ else
+ continue;
+ } else if (strcmp(tblname, "cred") == 0) {
+ if (strcmp("auth_type", tcol->tc_name) == 0)
+ strcpy(mname, "publickey.byname");
+ else if (strcmp("auth_name",
+ tcol->tc_name) == 0)
+ strcpy(mname, "netid.byname");
+ else
+ continue;
+ } else if (strcmp(tblname, "netgroup") == 0) {
+ if (strcmp(NGROUPNAME, tcol->tc_name) == 0)
+ strcpy(mname, "netgroup");
+ else
+ continue;
+ } else {
+ strcpy(mname, tblname);
+ if (strcmp(tcol->tc_name, DEFAULTKEY) != 0) {
+ strcat(mname, ".");
+ strcat(mname, BY);
+ strcat(mname, tcol->tc_name);
+ }
+ }
+ if ((map = (struct ypmaplist *)XMALLOC(
+ (unsigned)sizeof (struct ypmaplist))) == NULL) {
+ maplist.status = YP_YPERR;
+ break;
+ }
+ map->ypml_next = maplist.list;
+ maplist.list = map;
+ namesz = strlen(mname);
+ if (namesz <= YPMAXMAP) {
+ (void) strcpy(map->ypml_name, mname);
+ } else {
+ (void) strncpy(map->ypml_name, mname, YPMAXMAP);
+ map->ypml_name[YPMAXMAP] = '\0';
+ }
+ }
+ fnr = db_nextib(orgdir, &cookie, FN_NOMANGLE, NULL);
+ }
+ add_cleanup(free_ypmaplist, (void *)(maplist.list), "ypmaplist");
+ return (&maplist);
+}
+
+#undef maplist
+
+/*
+ * This one does most of the server side work for yp_all. Of course,
+ * should not be generated by rpcgen. Fetches the whole database
+ * for the map and serializes a stream of struct ypresp_key_val's.
+ */
+bool_t
+xdr_ypresp_all(xdrs, resp)
+ XDR *xdrs;
+ struct ypresp_all *resp;
+{
+ struct ypresp_key_val respdat;
+ nis_object *ret_en_objs;
+ nis_fn_result *fnr;
+ netobj cookie;
+ entry_obj *e, *netid_e = NULL;
+ int len;
+ int nokey = 0; /* can we isolate the key part of the entry ? */
+ bool_t more = TRUE;
+ nis_error status;
+#define short_tblnm (__nis_get_tsd()->xdr_ypresp_all_short_tblnm)
+ char *t, *p = short_tblnm;
+ int all_readable = 0;
+
+ respdat.keydat.dptr = respdat.valdat.dptr = (char *)NULL;
+ respdat.keydat.dsize = respdat.valdat.dsize = 0;
+ respdat.status = resp->status;
+
+ if (!resp->table_name || !resp->princp || (resp->status != YP_TRUE)) {
+ if (!xdr_bool(xdrs, &more))
+ return (FALSE);
+ if (!xdr_ypresp_key_val(xdrs, &respdat))
+ return (FALSE);
+ more = FALSE;
+ if (!xdr_bool(xdrs, &more))
+ return (FALSE);
+ return (TRUE);
+ }
+
+ memset(p, 0, YPMAXMAP);
+ strcpy(p, resp->table_name);
+ t = strchr(p, '.');
+ if (t)
+ *t = '\0';
+
+ all_readable = __can_do(NIS_READ_ACC, resp->table_zobj->zo_access,
+ resp->table_zobj, resp->princp);
+ /*
+ * MEMORY POLICY on db_firstib/nextib: always call it with
+ * !(flags & 2), the db_*ib routines will add fnr and fnr->obj
+ * to the cleanup list. We never free cookie.n_bytes if the
+ * cookie is to be passwd to db_nextib(). db_nextib() always
+ * frees the cookie it is given.
+ */
+ fnr = db_firstib(resp->table_name, 0, NULL, FN_NOMANGLE, NULL);
+ cookie = fnr->cookie;
+ while (fnr->status == NIS_SUCCESS) {
+ if (all_readable)
+ ret_en_objs = fnr->obj;
+ else
+ ret_en_objs = nis_censor_object(fnr->obj,
+ resp->table_zobj->TA_data.ta_cols.ta_cols_val,
+ resp->princp);
+ if (ret_en_objs == NULL) {
+ fnr = db_nextib(resp->table_name,
+ &cookie, FN_NOMANGLE, NULL);
+ cookie = fnr->cookie;
+ continue; /* avoid the song 'n dance below */
+ }
+ if (__type_of(ret_en_objs) != NIS_ENTRY_OBJ) {
+ respdat.status = YP_NOKEY;
+ if (!xdr_bool(xdrs, &more))
+ return (FALSE);
+ if (!xdr_ypresp_key_val(xdrs, &respdat))
+ return (FALSE);
+ if (ret_en_objs && !all_readable)
+ nis_destroy_object(ret_en_objs);
+ break;
+ }
+ e = &(ret_en_objs->EN_data);
+
+ if ((strcmp(p, "cred") == 0) &&
+ (strcmp(COLVAL(1), "DES") != 0)) {
+ fnr = db_nextib(resp->table_name,
+ &cookie, FN_NOMANGLE, NULL);
+ cookie = fnr->cookie;
+ if (ret_en_objs && !all_readable)
+ nis_destroy_object(ret_en_objs);
+ continue;
+ }
+
+ if ((strcmp(p, "cred") == 0) &&
+ (strcmp(COLVAL(1), "DES") == 0) &&
+ netid_wart) {
+ long yperr = YP_TRUE;
+
+ netid_e = e;
+ e = get_netid_entry(resp->table_zobj, netid_e,
+ resp->table_name, resp->princp, &yperr);
+ if (! e) {
+ fnr = db_nextib(resp->table_name, &cookie,
+ FN_NOMANGLE, NULL);
+ cookie = fnr->cookie;
+ if (ret_en_objs && !all_readable)
+ nis_destroy_object(ret_en_objs);
+ continue;
+ }
+ }
+
+ if ((resp->key_column_ndx < 0) ||
+ (resp->key_column_ndx > (e->en_cols.en_cols_len - 1)))
+ nokey = 1;
+
+ /* construct a complete entry from all columns */
+ memset(record, 0, YPMAXRECORD);
+ t = &(record[0]);
+ status = cook_record_from_entry(e, &t, &len, p,
+ resp->table_zobj);
+ if (status != NIS_SUCCESS) {
+ respdat.status = err_conv_nistoyp(status);
+ if (!xdr_bool(xdrs, &more))
+ return (FALSE);
+ if (!xdr_ypresp_key_val(xdrs, &respdat))
+ return (FALSE);
+ break;
+ }
+ memset(keyval, 0, YPMAXRECORD);
+ if (! nokey) {
+ if (netid_wart && netid_e)
+ e = netid_e;
+ strncpy(keyval,
+ COLVAL(resp->key_column_ndx),
+ COLLEN(resp->key_column_ndx));
+ if (serv_wart &&
+ (strcmp(p, "services") == 0)) {
+ strcat(keyval, "/");
+ strcat(keyval, COLVAL(2));
+ }
+ }
+ respdat.keydat.dptr = keyval;
+ respdat.keydat.dsize = strlen(keyval);
+ respdat.valdat.dptr = record;
+ respdat.valdat.dsize = len;
+ if (!xdr_bool(xdrs, &more))
+ return (FALSE);
+ if (!xdr_ypresp_key_val(xdrs, &respdat))
+ return (FALSE);
+ fnr = db_nextib(resp->table_name, &cookie, FN_NOMANGLE, NULL);
+ cookie = fnr->cookie;
+ if (ret_en_objs && !all_readable)
+ nis_destroy_object(ret_en_objs);
+ }
+
+ more = FALSE;
+ if (!xdr_bool(xdrs, &more))
+ return (FALSE);
+ return (TRUE);
+}
+
+#undef short_tblnm
+
+static char *
+upcase(s)
+ char *s;
+{
+ char *t;
+
+ for (t = s; *t; t++)
+ *t = toupper(*t);
+ return (s);
+}
+
+/*
+ * Following cook_* routines are very flimsy, and should be kept static.
+ */
+
+static
+cook_err_resp(resp, stat)
+ struct ypresp_val *resp;
+ long stat;
+{
+ resp->status = stat;
+ resp->valdat.dptr = NULL;
+ resp->valdat.dsize = 0;
+}
+
+static
+cook_err_keyresp(keyresp, stat)
+ struct ypresp_key_val *keyresp;
+ long stat;
+{
+ keyresp->status = stat;
+ keyresp->keydat.dptr = NULL;
+ keyresp->keydat.dsize = 0;
+ keyresp->valdat.dptr = NULL;
+ keyresp->valdat.dsize = 0;
+}
+
+/*
+ * Takes a NIS+ entry and the column separator and returns
+ * a reasonable looking YP record and its length.
+ */
+
+static nis_error
+cook_record_from_entry(e, record_dat, record_len, table, tobj)
+ entry_obj *e;
+ char **record_dat;
+ int *record_len;
+ char *table;
+ nis_object *tobj;
+{
+ int i, len;
+ char *entrydat = *record_dat, *last;
+ char minibuf[80];
+ table_obj *tbl = &tobj->TA_data;
+ table_col *cols = tbl->ta_cols.ta_cols_val;
+ int num_cols = tbl->ta_cols.ta_cols_len;
+
+ for (i = 0, len = 0; i < num_cols; i++, len++)
+ len += e->en_cols.en_cols_val[i].ENLEN;
+
+ /* a rare occasion, to safeguard against garbage in e */
+ if (++len > YPMAXRECORD)
+ return (NIS_NOMEMORY);
+
+ if (strcmp(table, "hosts") == 0)
+ sprintf(entrydat, "%s %s\t#%s", COLVAL(2), COLVAL(1),
+ COLVAL(3));
+ else if (strcmp(table, "passwd") == 0)
+ sprintf(entrydat, "%s:%s:%s:%s:%s:%s:%s", COLVAL(0),
+ COLVAL(1), COLVAL(2), COLVAL(3),
+ COLVAL(4), COLVAL(5), COLVAL(6));
+ else if (strcmp(table, "group") == 0)
+ sprintf(entrydat, "%s:%s:%s:%s", COLVAL(0), COLVAL(1),
+ COLVAL(2), COLVAL(3));
+ else if (strcmp(table, "services") == 0)
+ sprintf(entrydat, "%s %s/%s #%s", COLVAL(1), COLVAL(3),
+ COLVAL(2), COLVAL(4));
+ else if (strcmp(table, "ethers") == 0)
+ sprintf(entrydat, "%s %s #%s", COLVAL(0), COLVAL(1), COLVAL(2));
+ else if (strcmp(table, "networks") == 0)
+ sprintf(entrydat, "%s %s #%s", COLVAL(1), COLVAL(2), COLVAL(3));
+ else if (strcmp(table, "netmasks") == 0)
+ sprintf(entrydat, "%s #%s", COLVAL(1), COLVAL(2));
+ else if (strcmp(table, "rpc") == 0)
+ sprintf(entrydat, "%s %s #%s", COLVAL(1), COLVAL(2), COLVAL(3));
+ else if (strcmp(table, "timezone") == 0)
+ sprintf(entrydat, "%s %s", COLVAL(1), COLVAL(0));
+ else if (strcmp(table, "protocols") == 0) {
+ minibuf[79] = '\0'; /* make sure its terminated */
+ strncpy(minibuf, COLVAL(1), 79);
+ sprintf(entrydat, "%s %s %s #%s", minibuf, COLVAL(2),
+ upcase(COLVAL(1)), COLVAL(3));
+ } else if (strcmp(table, "cred") == 0) {
+ if (netid_wart) {
+ if (strcmp(COLVAL(1), "DES") == 0) {
+ /* netid for root */
+ char *col, *p, *q;
+
+ if ((col = strdup(COLVAL(2))) == NULL)
+ return (NIS_NOMEMORY);
+ p = strchr(col, '.');
+ if ((p != NULL) &&
+ ((q = strchr(++p, '@')) != NULL)) {
+ *q = '\0';
+ sprintf(entrydat, "0:%s", p);
+ } else {
+ free(col);
+ return (NIS_BADOBJECT);
+ }
+ free(col);
+ } else {
+ /* netid for user */
+ sprintf(entrydat, "%s:%s", COLVAL(2),
+ COLVAL(3));
+ }
+ } else {
+ char *p;
+ p = strchr(COLVAL(3), ':');
+ if (p)
+ *p = 0;
+ /* weed out the uid/gid junk. why is it there ? */
+ sprintf(entrydat, "%s:%s", COLVAL(3), COLVAL(4));
+ }
+ } else if (strcmp(table, "mail_aliases") == 0)
+ if (mail_wart_byaddr)
+ sprintf(entrydat, "%s", COLVAL(0));
+ else
+ sprintf(entrydat, " %s", COLVAL(1));
+ else if (strncmp(table, "auto_", 5) == 0)
+ sprintf(entrydat, "%s", COLVAL(1));
+ else {
+ /*
+ * For 2-column key/value tables we build a value
+ * with whatever is in column 1. For other tables
+ * we combine all of the columns with the table
+ * separator.
+ */
+ if (num_cols == 2 &&
+ strcasecmp(cols[0].tc_name, "key") == 0 &&
+ strcasecmp(cols[1].tc_name, "value") == 0) {
+ sprintf(entrydat, "%s", COLVAL(1));
+ } else {
+ entrydat[0] = 0;
+ for (i = 0; i < num_cols; i++) {
+ if (i) {
+ strncat(entrydat,
+ (char *)&tbl->ta_sep, 1);
+ }
+ strcat(entrydat, COLVAL(i));
+ }
+ }
+ }
+ if (verbose)
+ syslog(LOG_INFO,
+ "cook_record_from_entry: returns %s", entrydat);
+ *record_len = strlen(entrydat);
+
+ /* XXX Just a hack to workaround 1083096 ??? */
+ if (strcmp(table, "mail_aliases") == 0)
+ (*record_len)++;
+
+ return (NIS_SUCCESS);
+}
+
+#define best_address (__nis_get_tsd()->best_host_address_best_address)
+
+static
+char *
+best_host_address(nis_object *obj, int nobj, struct svc_req *rqstp)
+{
+ int i;
+ SVCXPRT *xp;
+ struct netconfig *nc;
+ ulong_t addr;
+ char *beststring;
+ ulong_t bestaddr;
+ char *tstring;
+ ulong_t taddr;
+
+ /* determine the address of the client and store in 'addr' */
+ if (rqstp == NULL)
+ return (NULL);
+ xp = rqstp->rq_xprt;
+ nc = getnetconfigent(xp->xp_netid);
+ if (nc == NULL)
+ return (NULL);
+ if (strcmp(nc->nc_protofmly, "inet") != 0) {
+ freenetconfigent(nc);
+ return (NULL);
+ }
+ addr = ((struct sockaddr_in *)xp->xp_rtaddr.buf)->sin_addr.s_addr;
+
+ /* start with first object as best address */
+ beststring = ENTRY_VAL(&obj[0], 2);
+ bestaddr = inet_addr(beststring);
+
+ /* loop through other objects and see if there is a better address */
+ for (i = 1; i < nobj; i++) {
+ tstring = ENTRY_VAL(&obj[i], 2);
+ taddr = inet_addr(tstring, 2);
+ if (ntohl(addr ^ taddr) < ntohl(addr ^ bestaddr)) {
+ bestaddr = taddr;
+ beststring = tstring;
+ }
+ }
+
+ /* set global variable to best address found */
+ return (strdup(beststring));
+}
+
+/*
+ * This is a qsort comparison function for host objects.
+ * The objects are sorted by address. For hosts with the
+ * same address, the canonical object (cname == name) is
+ * sorted before the other objects with the same address.
+ */
+static int
+host_object_compar(p1, p2)
+ const void *p1;
+ const void *p2;
+{
+ int st;
+ int iscanon1;
+ int iscanon2;
+ entry_obj *e1 = &((nis_object *)p1)->EN_data;
+ entry_obj *e2 = &((nis_object *)p2)->EN_data;
+
+ /* if addresses differ, return comparison */
+ st = strcmp(ENCOLVAL(e1, 2), ENCOLVAL(e2, 2));
+ if (st != 0) {
+ /*
+ * If either address matches the best host address for
+ * the client, sort that before all others.
+ */
+ if (best_address) {
+ if (strcmp(ENCOLVAL(e1, 2), best_address) == 0)
+ return (-1);
+ else if (strcmp(ENCOLVAL(e2, 2), best_address) == 0)
+ return (1);
+ }
+ return (st);
+ }
+
+ /* determine if any objects are canonical (cname == name) */
+ iscanon1 = strcmp(ENCOLVAL(e1, 0), ENCOLVAL(e1, 1));
+ iscanon2 = strcmp(ENCOLVAL(e2, 0), ENCOLVAL(e2, 1));
+
+ if (iscanon1 == iscanon2)
+ return (0);
+
+ if (iscanon1)
+ return (-1);
+ else
+ return (1);
+}
+
+
+#define STRCAT_CHECK(ptr, s, space) { \
+ int len; \
+ \
+ len = strlen(s); \
+ if (len + 1 > space) { \
+ nis_err = NIS_NOMEMORY; \
+ goto out_of_space; \
+ } else { \
+ strcat(ptr, s); \
+ ptr += len; \
+ space -= len; \
+ } \
+}
+
+/*
+ * Takes an NIS+ entry and returns a reasonable looking YP host record
+ * and its length. We sort the host objects by host address. For
+ * hosts with the same address, the canonical host object (cname == name)
+ * is placed before the other objects with the same address. For each
+ * address, we print the entries for that address. If a comment appears in
+ * an entry, then we save it and print it after all of the host names for
+ * the address have been printed.
+ *
+ * The fields in a host object are:
+ * COLVAL(0) - official host name (canonical name)
+ * COLVAL(1) - host name (alias name)
+ * COLVAL(2) - host address
+ * COLVAL(3) - comment
+ *
+ * We use COLGETVAL instead COLVAL for the comment field so that if it
+ * is null we don't put an obnoxious "\t#" at the end.
+ */
+static nis_error
+cook_host_record(objs, num_objs, record_dat, record_len, rqstp)
+ nis_object *objs;
+ int num_objs;
+ char **record_dat;
+ int *record_len;
+ struct svc_req *rqstp;
+{
+ int i;
+ entry_obj *e;
+ char *comment;
+ char *ptr;
+ char *s;
+ int space;
+ int did_line;
+ char *current_address;
+ char *completed_entry;
+ char *cname;
+ char *name;
+ char *addr;
+ nis_error nis_err = NIS_SUCCESS; /* assume success */
+
+ *record_len = 0;
+
+ if (num_objs == 0) {
+ return (NIS_SUCCESS);
+ } else if (num_objs > 1) {
+ best_address = best_host_address(objs, num_objs, rqstp);
+ qsort((void *)objs, num_objs, sizeof (nis_object),
+ host_object_compar);
+ if (best_address) {
+ free(best_address);
+ best_address = NULL;
+ }
+ }
+
+ ptr = *record_dat; /* this is where we write our data */
+ space = YPMAXRECORD; /* this is how much space we have */
+ comment = 0; /* we save the comment field here */
+ did_line = 0; /* if true, we have started an entry */
+ completed_entry = ptr; /* keeps track of last full entry */
+ current_address = "";
+
+ for (i = 0; i < num_objs; i++) {
+ e = &objs[i].EN_data;
+ cname = COLVAL(0);
+ name = COLVAL(1);
+ addr = COLVAL(2);
+
+ if (strcmp(addr, current_address) != 0) {
+ /* new address, finish off previous entry */
+ current_address = addr;
+ if (did_line) {
+ if (comment) {
+ STRCAT_CHECK(ptr, "\t# ", space);
+ STRCAT_CHECK(ptr, comment, space);
+ comment = 0;
+ }
+ STRCAT_CHECK(ptr, "\n", space);
+ }
+ STRCAT_CHECK(ptr, addr, space);
+ STRCAT_CHECK(ptr, " ", space);
+ STRCAT_CHECK(ptr, cname, space);
+ did_line = 1;
+ }
+ if (strcmp(name, cname) != 0) {
+ STRCAT_CHECK(ptr, " ", space);
+ STRCAT_CHECK(ptr, name, space);
+ }
+ if (!comment) {
+ s = COLGETVAL(3);
+ if (s && *s)
+ comment = s;
+ }
+
+ completed_entry = ptr;
+ }
+ if (did_line && comment) {
+ STRCAT_CHECK(ptr, "\t# ", space);
+ STRCAT_CHECK(ptr, comment, space);
+ }
+ completed_entry = ptr;
+
+out_of_space:
+
+ *completed_entry = 0; /* in case failed on partial entry */
+ *record_len = completed_entry - *record_dat;
+
+ if (verbose) {
+ syslog(LOG_INFO,
+ "cook_host_record: returning \"%s\"", *record_dat);
+ }
+
+ return (nis_err);
+}
+
+/*
+ * Given an entry in the netgroup table, constructs a string
+ * consisting of either a member netgroup name, or a leaf
+ * netgroup like "(host,name,domain)".
+ */
+static void
+cook_an_ngroup(e, val, vallen)
+ entry_obj *e;
+ char **val;
+ int *vallen;
+{
+ if (strcmp(COLVAL(1), "") == 0) {
+ *vallen = COLLEN(2) + COLLEN(3) + COLLEN(4) + 5;
+ if (*vallen > YPMAXRECORD) {
+ sprintf(*val, "");
+ *vallen = 1;
+ } else
+ sprintf(*val, "(%s,%s,%s)",
+ COLVAL(2), COLVAL(3), COLVAL(4));
+ } else {
+ *vallen = COLLEN(1) + 1;
+ if (*vallen > YPMAXRECORD) {
+ sprintf(*val, "");
+ *vallen = 1;
+ } else
+ sprintf(*val, COLVAL(1));
+ }
+}
+
+/*
+ * Goes through column names in table object and returns the
+ * index for the one that matches with the col_val passed.
+ * indexing is 0 thro n and returns -1 on failure.
+ */
+static int
+get_keyndx(t_obj, col_val)
+ table_obj *t_obj;
+ char *col_val;
+{
+ int i;
+
+ for (i = 0; i < t_obj->ta_cols.ta_cols_len; i++)
+ if (strcmp(col_val,
+ (t_obj->ta_cols.ta_cols_val + i)->tc_name) == 0)
+ return (i);
+ return (-1);
+}
+
+static char *
+getcaller_inet(rqstp)
+ struct svc_req *rqstp;
+{
+#define buf (__nis_get_tsd()->getcaller_inet_buf)
+ SVCXPRT *xp;
+ char *uaddr;
+ struct netconfig *nc;
+
+ if (rqstp == NULL)
+ return ("");
+ xp = rqstp->rq_xprt;
+ nc = (struct netconfig *)getnetconfigent(xp->xp_netid);
+
+ if (nc)
+ uaddr = taddr2uaddr(nc, &(xp->xp_rtaddr));
+ else
+ uaddr = "<no netconfig ent>";
+
+ sprintf(buf, "[tp=%s, netid=%d, uaddr=%s]",
+ xp->xp_tp, xp->xp_netid, uaddr);
+
+ if (nc) {
+ freenetconfigent(nc);
+ XFREE(uaddr);
+ }
+ return (buf);
+}
+
+#undef buf
+
+/*
+ * Hacks here onwards may go into a library, potentially
+ * to be inflicted on the users.
+ */
+
+long
+err_conv_nistoyp(inerr)
+ nis_error inerr;
+{
+ switch (inerr) {
+ case NIS_SUCCESS:
+ case NIS_S_SUCCESS: return (YP_TRUE);
+ case NIS_NOTFOUND:
+ case NIS_S_NOTFOUND: return (YP_FALSE);
+ case NIS_NAMEUNREACHABLE:
+ case NIS_NOTSEARCHABLE:
+ case NIS_UNKNOWNOBJ: return (YP_NODOM);
+ case NIS_NOSUCHTABLE: return (YP_NOMAP);
+ case NIS_BADOBJECT:
+ case NIS_NOMEMORY: return (YP_YPERR);
+ default: return (YP_YPERR);
+ }
+}
+
+/*
+ * Takes the name of a domain/directory, fully qualifies it
+ * the most naive way and finds out if the corresponding
+ * NIS+ directory is served by this server.
+ */
+
+int
+ypcheck_nisdir(dname)
+ char *dname;
+{
+ char name[YPMAXDOMAIN + 2];
+ struct ticks ticks;
+ nis_object *z_obj = NULL;
+ nis_error status;
+ int len = strlen(dname);
+
+ (void) strncpy(name, dname, len);
+ if (name[len - 1] != '.')
+ name[len++] = '.';
+ name[len] = '\0';
+ status = __directory_object(name, &ticks, FALSE, &z_obj);
+ /*
+ * MEMORY POLICY: __directory_object maintains a cache and
+ * returns a pointer to an entry into it. We do not free
+ * anything here.
+ */
+ return (status == NIS_SUCCESS);
+}
+
+/*
+ * A little different from ypcheck_nisdir. This one attempts to
+ * get the nis_object for the "ORGDIR.<domainname>[.]" directory.
+ */
+nis_error
+getzobj_orgdir(dname, z_obj)
+ char *dname;
+ nis_object **z_obj;
+{
+ char name[YPMAXDOMAIN + ORGLEN];
+ struct ticks ticks;
+ nis_error status;
+ int len;
+
+ (void) strcpy(name, ORGDIR2);
+ (void) strncat(name, dname, strlen(dname));
+ len = strlen(name);
+ if (name[len - 1] != '.')
+ name[len++] = '.';
+ name[len] = '\0';
+ /* don't bother who's master */
+ status = __directory_object(name, &ticks, FALSE, z_obj);
+ /*
+ * MEMORY POLICY: __directory_object maintains a cache and
+ * returns a pointer to an entry into it. We do not free
+ * anything here.
+ */
+ return (status);
+}
+
+
+/*
+ * Supply an NIS style mapname and get back pointers to the
+ * names of corresponding table in NIS+ and the column to
+ * search. Does not make any guarantees that the table
+ * and/or column indeed exist in the NIS+ namespace.
+ */
+void
+map2table(xx_mapname, xx_table, xx_column)
+ char *xx_mapname;
+ char **xx_table;
+ char **xx_column;
+{
+#define tbl (__nis_get_tsd()->map2table_tbl)
+#define col (__nis_get_tsd()->map2table_col)
+ char *p, *t = tbl, *c = col;
+
+ memset(t, 0, YPMAXMAP);
+ memset(c, 0, NIS_MAXATTRNAME);
+ *xx_table = t;
+ *xx_column = c;
+
+ serv_wart = 0;
+ netid_wart = 0;
+ mult_lines_wart = 0;
+
+ if (verbose)
+ syslog(LOG_INFO, "map2table: maps from %s", xx_mapname);
+ if (strcmp(xx_mapname, "services.byname") == 0) {
+ /*
+ * This is a special and weird case.
+ * We have to be compatible with past mistakes
+ * when some ??????? built the services.byname map
+ * with a key of the form "port/proto".
+ */
+ strcpy(t, "services");
+ strcpy(c, "port");
+ serv_wart = 1;
+ if (verbose)
+ syslog(LOG_INFO, "map2table: maps to %s and %s", t, c);
+ return;
+ }
+ if (strcmp(xx_mapname, "services.byservicename") == 0) {
+ /*
+ * This is even more special and weird case.
+ * We are trying to be nice to the YP clients by
+ * giving a map they can match on using the key
+ * service/proto. It turns out, they can do
+ * the match only on "service" or "port" as
+ * the key too.
+ */
+ strcpy(t, "services");
+ strcpy(c, "name");
+ serv_wart = 1;
+ if (verbose)
+ syslog(LOG_INFO, "map2table: maps to %s and %s", t, c);
+ return;
+ }
+
+ if (strcmp(xx_mapname, "publickey.byname") == 0) {
+ strcpy(t, "cred");
+ strcpy(c, "auth_name");
+ if (verbose)
+ syslog(LOG_INFO, "map2table: maps to %s and %s", t, c);
+ return;
+ }
+
+ if (strcmp(xx_mapname, "netid.byname") == 0) {
+ strcpy(t, "cred");
+ strcpy(c, "auth_name");
+ netid_wart = 1;
+ if (verbose)
+ syslog(LOG_INFO, "map2table: maps to %s and %s", t, c);
+ return;
+ }
+
+ if (strncmp(xx_mapname, "mail.", 5) == 0) {
+ strcpy(t, "mail_aliases");
+ if (strcmp(xx_mapname, "mail.byaddr") == 0) {
+ mail_wart_byaddr = 1;
+ strcpy(c, MAILADDR);
+ } else /* if (strcmp(xx_mapname, "mail.aliases) == 0) */ {
+ mail_wart_byaddr = 0;
+ strcpy(c, MAILNAME);
+ }
+ if (verbose)
+ syslog(LOG_INFO, "map2table: maps to %s and %s", t, c);
+ return;
+ }
+
+ if ((p = strrchr(xx_mapname, '.')) != NULL) {
+ /*
+ * First check for maps of type X.byY and then replace
+ * all dots by _
+ */
+ char *tmp;
+
+ tmp = p;
+ if (strncmp(++p, BY, 2) == 0) {
+ p += 2;
+ if (*p == NULL) {
+ /* BY was really a name */
+ *tmp = '_'; /* replace _ back */
+ p = NULL;
+ } else {
+ *tmp = '\0';
+ }
+ } else {
+ p = NULL;
+ }
+ /* replace the other dots by _ */
+ while ((tmp = strchr(xx_mapname, '.')) != NULL)
+ *tmp = '_';
+ }
+ strcpy(t, xx_mapname);
+
+ /* only the gethostbyYY client backends can handle multiple lines */
+ if (strcmp(t, "hosts") == 0)
+ mult_lines_wart = 1;
+
+ if (p)
+ strcpy(c, p);
+ else if (strcmp(xx_mapname, "netgroup") == 0)
+ strcpy(c, NGROUPNAME);
+ else
+ /*
+ * POLICY: The default column for all maps that want to
+ * use the backward compatibility and are not among the
+ * privileged 12 must be DEFAULTKEY. The 12 are: passwd,
+ * group, hosts, networks, services, rpc, mail_aliases,
+ * ethers, protocols, netmasks, publickey and bootparams.
+ */
+ strcpy(c, DEFAULTKEY);
+
+ if (verbose)
+ syslog(LOG_INFO, "map2table: maps to %s and %s", t, c);
+}
+
+#undef tbl
+#undef col
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nisd/ypserv1.c b/usr/src/cmd/rpcsvc/nis/rpc.nisd/ypserv1.c
new file mode 100644
index 0000000000..a502e68e48
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nisd/ypserv1.c
@@ -0,0 +1,153 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifndef lint
+static char sccsid[] = "%Z%%M% %I% %E% SMI";
+#endif
+/*
+ * YP Version 1 compatibility code added to rpc.nisd.
+ *
+ * ypserv1.c
+ *
+ * Copyright (c) 1993 Sun Microsystems Inc
+ * All rights reserved.
+ *
+ * This file provides incomplete support for YP Version 1.
+ *
+ * This addresses bug #1136940.
+ *
+ * If a NIS+ server running in NIS compat mode does not respond
+ * immediately to a NIS version2 call, the 4.X clients fail over to NIS
+ * version 1 and then never switches back to version 2. As the rpc.nisd
+ * only supports NIS V2 protocol the client process will hang even when
+ * rpc.nisd comes up later.
+ *
+ * The fix (implemented here) is to provide support for NULL, DOMAIN, and
+ * DOMAIN_NOACK calls for version 1 YP (in the NIS compatibility case).
+ *
+ * From the 4.X source: /src/413/lib/libc/yp/yp_match.c,
+ * if we provide support for above three procedures, _yp_dobind() returns
+ * with binding for version 1, but later (*dofunc) fails because
+ * that RPC procedure for version 1 is not supported. As a result, yp_unbind()
+ * is called and then __yp_dobind() is again called. This time around,
+ * version 2 is tried first and that succeeds (because now hopefully, the NIS+
+ * server is up and responding) and then voila! the (*dofunc) succeeds.
+ *
+ * Please note that there is no need to provide support for other YP version 1
+ * procedures such as yp_match or yp_all.
+ */
+
+#include <stdio.h>
+#include <stdlib.h> /* getenv, exit */
+#include <sys/types.h>
+#include <memory.h>
+#include <stropts.h>
+#include <syslog.h>
+#include <rpcsvc/yp_prot.h>
+
+typedef char *domainname;
+
+extern int verbose; /* set by the -v switch */
+
+/*
+ * NIS Version 1 (YP) Dispatch table
+ */
+static void *ypproc_null_1();
+extern int *ypproc_domain_svc();
+extern int *ypproc_domain_nonack_svc();
+
+static bool_t
+xdr_domainname(xdrs, objp)
+ register XDR *xdrs;
+ domainname *objp;
+{
+ register long *buf;
+
+ if (!xdr_string(xdrs, objp, YPMAXDOMAIN))
+ return (FALSE);
+ return (TRUE);
+}
+
+void
+ypprog_1(rqstp, transp)
+ struct svc_req *rqstp;
+ register SVCXPRT *transp;
+{
+ union {
+ domainname ypproc_domain_1_arg;
+ domainname ypproc_domain_nonack_1_arg;
+ } argument;
+ char *result;
+ bool_t (*xdr_argument)(), (*xdr_result)();
+ char *(*local)();
+
+ /*
+ * In the code that follows, we dispatch to the YP Version 2
+ * calls for the DOMAIN and NONACK cases, since there's no
+ * reason to implement those here.
+ */
+ switch (rqstp->rq_proc) {
+ case YPPROC_NULL:
+ xdr_argument = xdr_void;
+ xdr_result = xdr_void;
+ local = (char *(*)()) ypproc_null_1;
+ break;
+
+ case YPPROC_DOMAIN:
+ xdr_argument = xdr_domainname;
+ xdr_result = xdr_bool;
+ local = (char *(*)()) ypproc_domain_svc;
+ break;
+
+ case YPPROC_DOMAIN_NONACK:
+ xdr_argument = xdr_domainname;
+ xdr_result = xdr_bool;
+ local = (char *(*)()) ypproc_domain_nonack_svc;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+ (void) memset((char *)&argument, 0, sizeof (argument));
+ if (!svc_getargs(transp, xdr_argument, (caddr_t) &argument)) {
+ svcerr_decode(transp);
+ return;
+ }
+ result = (*local)(&argument, rqstp);
+ if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, xdr_argument, (caddr_t) &argument)) {
+ syslog(LOG_ERR, "ypserv_v1: unable to free arguments");
+ exit(1);
+ }
+}
+
+static void *
+ypproc_null_1()
+{
+ static char dummy;
+
+ return ((void *) &dummy);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/Makefile b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/Makefile
new file mode 100644
index 0000000000..67fe4198b3
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/Makefile
@@ -0,0 +1,80 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Makefile for nispasswd daemon
+#
+# cmd/rpcsvc/nis/rpc.nispasswdd/Makefile
+#
+
+PROG = rpc.nispasswdd
+
+DERIVED_FILES = nispasswd_xdr.c
+
+SRCS = rpc.nispasswdd.c npd_svc.c npd_cache.c npd_svcsubr.c npd_ypfwd.c $(DERIVED_FILES)
+
+OBJS = $(SRCS:%.c=%.o)
+
+include $(SRC)/cmd/Makefile.cmd
+
+PROTOCOL_DIR = $(ROOT)/usr/include/rpcsvc
+LDLIBS += -lnsl
+
+# rpcgen flags
+RPCGENFLAGS= -c -C -M
+
+CPPFLAGS += -I$(SRC)/lib/libnsl/include -I$(SRC)/lib/nsswitch/nisplus/common \
+ -DDEBUG -D_REENTRANT
+
+#
+LINTFLAGS= -L$(SRC)/lib/libnsl -c
+LINTOUT= lint.out
+
+.KEEP_STATE:
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+lint := TARGET= lint
+
+all: $(DERIVED_FILES) $(PROG)
+install: all $(ROOTUSRSBINPROG)
+clean:
+ $(RM) core $(DERIVED_FILES) $(OBJS) $(PROG)
+lint:
+ $(LINT.c) $(SRCS) $(LDLIBS) > $(LINTOUT) 2>&1
+
+#
+# build rules
+#
+$(PROG): $(OBJS)
+ $(LINK.c) -o $(PROG) $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+$(DERIVED_FILES) : $(PROTOCOL_DIR)/nispasswd.x
+ $(RPCGEN) $(RPCGENFLAGS) $(PROTOCOL_DIR)/nispasswd.x > $(DERIVED_FILES)
+
+include $(SRC)/cmd/Makefile.targ
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_cache.c b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_cache.c
new file mode 100644
index 0000000000..5eabb9451e
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_cache.c
@@ -0,0 +1,215 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * npd_cache.c
+ * NPD cache routines
+ *
+ * Copyright (c) 1994-2001 Sun Microsystems, Inc.
+ * All Rights Reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <syslog.h>
+#include <string.h>
+#include <stdlib.h>
+#include <shadow.h>
+#include <synch.h>
+#include <rpcsvc/nis.h>
+#include <rpcsvc/nispasswd.h>
+#include <ctype.h>
+#include "npd_cache.h"
+
+extern NIS_HASH_TABLE upd_list;
+extern long cache_time;
+static int __nis_hash_key(nis_name name, NIS_HASH_TABLE *table);
+
+void
+free_upd_item(upd)
+struct update_item *upd;
+{
+ if (upd == NULL)
+ return;
+
+ upd = (struct update_item *)nis_remove_item(upd->ul_item.name,
+ &upd_list);
+ if (upd == NULL)
+ return;
+
+ free(upd->ul_item.name);
+ free(upd->ul_user);
+ free(upd->ul_domain);
+ (void) memset(upd->ul_oldpass, 0, sizeof (upd->ul_oldpass));
+ free(upd->ul_oldpass);
+ free(upd);
+ upd = NULL;
+}
+
+/*
+ * return 0 if an entry already exists
+ * return -1 if out of memory
+ * return 1 on successful addition
+ */
+int
+add_upd_item(principal, user, sameuser, domain, ident, rval, key, pass)
+nis_name principal;
+char *user;
+bool_t sameuser;
+char *domain;
+ulong_t ident;
+ulong_t rval;
+des_block *key;
+char *pass;
+{
+ struct update_item *old = NULL, *tmp = NULL;
+
+ if ((principal == NULL || *principal == '\0') ||
+ (user == NULL || *user == '\0') ||
+ (domain == NULL || *domain == '\0') ||
+ (pass == NULL || *pass == '\0'))
+ return (0);
+
+ old = (struct update_item *)nis_find_item(principal, &upd_list);
+ if (old != NULL) {
+ return (0);
+ }
+
+ tmp = (struct update_item *)calloc(1, sizeof (struct update_item));
+ if (tmp == NULL)
+ return (-1);
+
+ tmp->ul_item.name = strdup(principal);
+ if (tmp->ul_item.name == NULL) {
+ free(tmp);
+ return (-1);
+ }
+ tmp->ul_user = strdup(user);
+ if (tmp->ul_user == NULL) {
+ free(tmp->ul_item.name);
+ free(tmp);
+ return (-1);
+ }
+ tmp->ul_domain = strdup(domain);
+ if (tmp->ul_domain == NULL) {
+ free(tmp->ul_user);
+ free(tmp->ul_item.name);
+ free(tmp);
+ return (-1);
+ }
+ tmp->ul_oldpass = strdup(pass);
+ if (tmp->ul_oldpass == NULL) {
+ free(tmp->ul_domain);
+ free(tmp->ul_user);
+ free(tmp->ul_item.name);
+ free(tmp);
+ return (-1);
+ }
+ tmp->ul_sameuser = sameuser;
+ tmp->ul_ident = ident;
+ tmp->ul_rval = rval;
+ tmp->ul_key = *key;
+ tmp->ul_attempt = 1;
+ tmp->ul_expire = time(NULL) + cache_time;
+
+ return (nis_insert_item((NIS_HASH_ITEM *) tmp, &upd_list));
+}
+
+bool_t
+find_upd_item(principal, upd)
+nis_name principal;
+struct update_item **upd;
+{
+ struct update_item *found = NULL;
+
+ if (principal == NULL || *principal == '\0')
+ return (FALSE);
+
+ found = (struct update_item *)nis_find_item(principal, &upd_list);
+ if (found == NULL)
+ return (FALSE);
+ *upd = found;
+ return (TRUE);
+}
+
+int
+__npd_hash_key(name)
+nis_name name;
+{
+ int key = __nis_hash_key(name, &upd_list);
+
+ return (key >= 0 ? key+1 : key);
+}
+
+struct update_item *
+__npd_item_by_key(key)
+int key;
+{
+ int size;
+
+ key -= 1;
+ size = sizeof (upd_list.keys) / sizeof (upd_list.keys[0]);
+
+ if (key < 0 || key >= size)
+ return (NULL);
+ return ((struct update_item *)upd_list.keys[key]);
+}
+
+/* the following macro improves performance */
+#define LOWER(c) (isupper((c)) ? _tolower((c)) : (c))
+
+static int
+__nis_hash_key(name, table)
+nis_name name;
+NIS_HASH_TABLE *table;
+{
+ int key = 0;
+ unsigned char *s;
+
+ if ((name == NULL || *name == '\0') || table == NULL)
+ return (-1);
+
+ for (s = (unsigned char *) name; *s != 0; s++)
+ key += LOWER(*s);
+ key %= (sizeof (table->keys) / sizeof (table->keys[0]));
+
+ return (key);
+}
+
+void
+__npd_print_entry(prin)
+char *prin;
+{
+ struct update_item *pi;
+
+ if (find_upd_item(prin, &pi) == TRUE) {
+ syslog(LOG_ERR, "user=%s, sameuser=%d",
+ pi->ul_user, pi->ul_sameuser);
+ syslog(LOG_ERR, "ident=%ld, rval=%ld",
+ pi->ul_ident, pi->ul_rval);
+ syslog(LOG_ERR, "attempt=%d, domain=%s",
+ pi->ul_attempt, pi->ul_domain);
+ return;
+ } else {
+ syslog(LOG_ERR, "no entry found for %s", prin);
+ return;
+ }
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_cache.h b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_cache.h
new file mode 100644
index 0000000000..c308c51540
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_cache.h
@@ -0,0 +1,69 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * npd_cache.h
+ *
+ * Copyright (c) 1994, 2001 Sun Microsystems Inc
+ * All Rights Reserved.
+ */
+
+#ifndef _NPD_CACHE_H
+#define _NPD_CACHE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <rpcsvc/nis.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * cache for useful information
+ */
+struct update_item {
+ NIS_HASH_ITEM ul_item; /* generic tag */
+ bool_t ul_sameuser; /* changing their own passwd */
+ char *ul_user; /* username */
+ char *ul_domain; /* domainname */
+ ulong_t ul_rval; /* random value */
+ ulong_t ul_ident; /* identifier */
+ des_block ul_key; /* session key */
+ char *ul_oldpass; /* old clear passwd */
+ int ul_attempt; /* failed attempts per session */
+ ulong_t ul_expire; /* expiration time */
+};
+
+
+bool_t find_upd_item(nis_name principal, struct update_item **upd);
+void free_upd_item(struct update_item *upd);
+struct update_item *__npd_item_by_key(int key);
+int add_upd_item(nis_name principal, char *user, bool_t sameuser, char *domain,
+ ulong_t ident, ulong_t rval, des_block *key, char *pass);
+void __npd_print_entry(char *prin);
+int __npd_hash_key(nis_name name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _NPD_CACHE_H */
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_svc.c b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_svc.c
new file mode 100644
index 0000000000..8650eb0570
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_svc.c
@@ -0,0 +1,1231 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 1994-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * npd_svc.c
+ * NPD service routines
+ *
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <syslog.h>
+#include <string.h>
+#include <ctype.h>
+#include <shadow.h>
+#include <crypt.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <rpcsvc/yppasswd.h>
+#include <rpcsvc/nis.h>
+#include <rpc/key_prot.h>
+#include <rpc/des_crypt.h>
+#include <rpcsvc/nispasswd.h>
+#include "npd_cache.h"
+#include "npd_svcsubr.h"
+#include <sys/byteorder.h>
+#include <rpcsvc/nis_dhext.h>
+
+extern int max_attempts;
+extern int cache_time;
+extern int verbose;
+extern int debug;
+
+#define _NPD_PASSMAXLEN 16
+#define MAX_RETRY 3
+
+extern char *ypfwd; /* YP domain to fwd NIS+ req */
+
+/*
+ * service routine for first part of the nispasswd update
+ * protocol.
+ */
+bool_t
+nispasswd_authenticate_1_svc(argp, result, rqstp)
+npd_request *argp;
+nispasswd_authresult *result;
+struct svc_req *rqstp;
+{
+ bool_t check_aging = TRUE; /* default == check */
+ bool_t same_user = TRUE; /* default == same user */
+ bool_t is_admin = FALSE; /* default == not an admin */
+ bool_t entry_exp = FALSE; /* default == not expired */
+ bool_t upd_entry = FALSE; /* default == do not update */
+ bool_t refresh = FALSE; /* default == do not refresh */
+ int ans = 0;
+ char prin[NIS_MAXNAMELEN];
+ unsigned char xpass[_NPD_PASSMAXLEN];
+ struct update_item *entry = NULL;
+ nis_result *pass_res;
+ nis_object *pobj;
+ des_block deskeys[3], ivec, cryptbuf;
+ int status;
+ char *oldpass;
+ unsigned long randval;
+ uint32_t rval, ident;
+ keylen_t pkeylen; /* public key length (bits) */
+ algtype_t algtype; /* public key algorithm type */
+
+ if (verbose)
+ syslog(LOG_ERR, "received NIS+ auth request for %s",
+ argp->username);
+
+ /* check if I'm running on the host == master(domain) */
+ if (__nis_ismaster(nis_local_host(), argp->domain) == FALSE) {
+ syslog(LOG_ERR, "not master for %s", argp->domain);
+ result->status = NPD_FAILED;
+ result->nispasswd_authresult_u.npd_err = NPD_NOTMASTER;
+ return (TRUE);
+ }
+
+again:
+ /* get caller info. from auth_handle */
+ if (rqstp)
+ (void) __nis_auth2princ_rpcgss(prin, rqstp, refresh, 0);
+ else
+ prin[0] = '\0';
+
+ if (verbose)
+ syslog(LOG_INFO, "_authenticate_: principal of req is %s",
+ prin);
+
+ /* caller == admin ? ; Y -> skip checks, N -> do aging checks */
+ if ((*prin != '\0') && strcmp(prin, "nobody") != 0)
+ /* authenticated user, check if they are privileged */
+ if (__nis_isadmin(prin, "passwd", argp->domain) == TRUE) {
+ check_aging = FALSE;
+ is_admin = TRUE;
+ }
+
+ if ((*prin == '\0') || (strcmp(prin, "nobody") == 0)) {
+ /* "." + null + "." = 3 */
+ if ((strlen(argp->username) + strlen(argp->domain) + 3) >
+ (size_t)NIS_MAXNAMELEN) {
+ syslog(LOG_ERR, "buffer too small");
+ result->status = NPD_FAILED;
+ result->nispasswd_authresult_u.npd_err =
+ NPD_BUFTOOSMALL;
+ return (TRUE);
+ }
+ (void) sprintf(prin, "%s.%s", argp->username, argp->domain);
+ if (prin[strlen(prin) - 1] != '.')
+ (void) strcat(prin, ".");
+ if (debug)
+ syslog(LOG_DEBUG,
+ "_authenticate_: 'nobody' or NUL prinicipal set to '%s'", prin);
+ }
+
+ /*
+ * The credential information may have changed for the user,
+ * we should refresh the cache to make sure or the user is
+ * a different user and the necessary checks need to be made.
+ */
+ if (strncmp(prin, argp->username, strlen(argp->username)) != 0)
+ if (refresh)
+ same_user = FALSE;
+ else {
+ refresh = TRUE;
+ goto again;
+ }
+
+ /* check if there is a cached entry */
+ if ((find_upd_item(prin, &entry)) &&
+ (strcmp(entry->ul_user, argp->username) == 0)) {
+
+ /* found an entry - check if it has expired */
+ if (entry->ul_expire > time(NULL)) {
+ entry->ul_attempt = entry->ul_attempt + 1;
+ /*
+ * check if this attempt > max_attempts.
+ */
+ if (entry->ul_attempt > max_attempts) {
+ syslog(LOG_ERR,
+ "too many failed attempts for %s",
+ argp->username);
+ result->status = NPD_FAILED;
+ result->nispasswd_authresult_u.npd_err =
+ NPD_PASSINVALID;
+ return (TRUE);
+ }
+ if (argp->ident == 0) {
+ /*
+ * a new session and we have an entry cached
+ * but we have not reached max_attempts, so
+ * just update entry with the new pass
+ */
+ upd_entry = TRUE;
+ }
+ } else { /* entry has expired */
+ (void) free_upd_item(entry);
+ entry_exp = TRUE;
+ entry = NULL;
+ }
+ } else {
+ entry = (struct update_item *)__npd_item_by_key(argp->ident);
+ if (entry == NULL) /* no cached entry */
+ if (argp->ident != 0) {
+ syslog(LOG_ERR,
+ "no cache entry found for %s but the identifier is %d",
+ argp->username, argp->ident);
+ result->status = NPD_FAILED;
+ result->nispasswd_authresult_u.npd_err =
+ NPD_IDENTINVALID;
+ return (TRUE);
+ }
+ }
+
+ /* get passwd info for username */
+ pass_res = nis_getpwdent(argp->username, argp->domain);
+
+ if (pass_res == NULL) {
+ syslog(LOG_ERR, "invalid args %s and %s",
+ argp->username, argp->domain);
+ result->status = NPD_FAILED;
+ result->nispasswd_authresult_u.npd_err = NPD_NOSUCHENTRY;
+ return (TRUE);
+ }
+ switch (pass_res->status) {
+ case NIS_SUCCESS:
+ pobj = NIS_RES_OBJECT(pass_res);
+ break;
+ case NIS_NOTFOUND:
+ syslog(LOG_ERR, "no passwd entry found for %s",
+ argp->username);
+ (void) nis_freeresult(pass_res);
+ result->status = NPD_FAILED;
+ result->nispasswd_authresult_u.npd_err = NPD_NOSUCHENTRY;
+ return (TRUE);
+ default:
+ syslog(LOG_ERR,
+ "NIS+ error (%d) getting passwd entry for %s",
+ pass_res->status, argp->username);
+ (void) nis_freeresult(pass_res);
+ result->status = NPD_FAILED;
+ result->nispasswd_authresult_u.npd_err = NPD_NISERROR;
+ return (TRUE);
+ }
+
+ /* if user check if 'min' days have passed since 'lastchg' */
+ if (check_aging) {
+ if ((__npd_has_aged(pobj, &ans) == FALSE) &&
+ ans == NPD_NOTAGED) {
+ syslog(LOG_ERR,
+ "password has not aged enough for %s",
+ argp->username);
+ (void) nis_freeresult(pass_res);
+ result->status = NPD_FAILED;
+ result->nispasswd_authresult_u.npd_err = ans;
+ return (TRUE);
+ }
+ /* if ans == NPD_NOSHDWINFO then aging cannot be enforced */
+ }
+
+ /*
+ * Find out what key length and algorithm type we're dealing with.
+ */
+ if (strncmp("DES", argp->key_type, 4) == 0) {
+ pkeylen = AUTH_DES_KEYLEN;
+ algtype = AUTH_DES_ALGTYPE;
+ } else {
+ /*
+ * This will check if the key_type exists in the
+ * NIS+ security cf.
+ */
+ if (__nis_translate_mechanism(argp->key_type, &pkeylen,
+ &algtype) < 0) {
+ syslog(LOG_ERR,
+ "cannot get keylen/algtype for key type '%s'",
+ argp->key_type);
+ (void) nis_freeresult(pass_res);
+ result->status = NPD_FAILED;
+ result->nispasswd_authresult_u.npd_err =
+ NPD_INVALIDARGS;
+ return (TRUE);
+ }
+ }
+
+ /* generate CK (from P.c and S.d) */
+ if (key_get_conv_g((const char *)argp->user_pub_key.user_pub_key_val,
+ pkeylen, algtype, deskeys,
+ AUTH_DES_KEY(pkeylen, algtype) ? 1 : 3) != 0) {
+ syslog(LOG_ERR,
+ "cannot generate common DES key for %s",
+ argp->username);
+ syslog(LOG_ERR, "is keyserv still running ?");
+ syslog(LOG_ERR, "has %s keylogged in ?", nis_local_host());
+ (void) nis_freeresult(pass_res);
+ result->status = NPD_FAILED;
+ result->nispasswd_authresult_u.npd_err = NPD_CKGENFAILED;
+ return (TRUE);
+ }
+ /* decrypt the passwd sent */
+ if (argp->npd_authpass.npd_authpass_len != _NPD_PASSMAXLEN) {
+ syslog(LOG_ERR, "password length wrong");
+ (void) nis_freeresult(pass_res);
+ result->status = NPD_TRYAGAIN;
+ result->nispasswd_authresult_u.npd_err = NPD_PASSINVALID;
+ return (TRUE);
+ }
+ (void) memcpy(xpass, argp->npd_authpass.npd_authpass_val,
+ _NPD_PASSMAXLEN);
+
+ ivec.key.high = ivec.key.low = 0;
+ if (AUTH_DES_KEY(pkeylen, algtype))
+ status = cbc_crypt(deskeys[0].c, (char *)xpass,
+ _NPD_PASSMAXLEN, DES_DECRYPT | DES_HW,
+ (char *)&ivec);
+ else
+ status = __cbc_triple_crypt(deskeys, (char *)xpass,
+ _NPD_PASSMAXLEN, DES_DECRYPT | DES_HW,
+ (char *)&ivec);
+
+ if (DES_FAILED(status)) {
+ syslog(LOG_ERR, "failed to decrypt password");
+ (void) nis_freeresult(pass_res);
+ result->status = NPD_FAILED;
+ result->nispasswd_authresult_u.npd_err = NPD_DECRYPTFAIL;
+ return (TRUE);
+ }
+
+ /* assign an ID and generate R on the first call of a session */
+ if (argp->ident == 0) {
+ ident = (uint32_t)__npd_hash_key(prin);
+ if ((int)ident == -1) {
+ syslog(LOG_ERR, "invalid ident value calculated");
+ (void) nis_freeresult(pass_res);
+ result->status = NPD_FAILED;
+ result->nispasswd_authresult_u.npd_err = NPD_SYSTEMERR;
+ return (TRUE);
+ }
+ (void) __npd_gen_rval(&randval);
+ } else {
+ /* second or third attempt */
+ ident = argp->ident;
+ if (entry == NULL) {
+ if (entry_exp) /* gen a new random val */
+ (void) __npd_gen_rval(&randval);
+ else {
+ syslog(LOG_ERR, "cache corrupted");
+ (void) nis_freeresult(pass_res);
+ result->status = NPD_FAILED;
+ result->nispasswd_authresult_u.npd_err =
+ NPD_SYSTEMERR;
+ return (TRUE);
+ }
+ } else {
+ if (strcmp(entry->ul_user, argp->username) != 0) {
+ /* gen a new random val */
+ (void) __npd_gen_rval(&randval);
+ } else
+ randval = entry->ul_rval;
+ }
+ }
+
+ rval = (uint32_t)randval;
+ if (! __npd_ecb_crypt(&ident, &rval, &cryptbuf,
+ sizeof (des_block), DES_ENCRYPT, &deskeys[0])) {
+ syslog(LOG_ERR, "failed to encrypt verifier");
+ (void) nis_freeresult(pass_res);
+ result->status = NPD_FAILED;
+ result->nispasswd_authresult_u.npd_err = NPD_ENCRYPTFAIL;
+ return (TRUE);
+ }
+ /* encrypt the passwd and compare with that stored in NIS+ */
+ if (same_user) {
+ if ((oldpass = ENTRY_VAL(pobj, 1)) == NULL) {
+ (void) nis_freeresult(pass_res);
+ result->status = NPD_FAILED;
+ result->nispasswd_authresult_u.npd_err = NPD_NOPASSWD;
+ return (TRUE);
+ }
+ if (strcmp(crypt((const char *)xpass,
+ (const char *)oldpass), oldpass) != 0) {
+ if (debug)
+ syslog(LOG_DEBUG,
+ "_authenticate_: pw decrypt failed");
+ (void) nis_freeresult(pass_res);
+ result->nispasswd_authresult_u.npd_verf.npd_xid =
+ htonl(cryptbuf.key.high);
+ result->nispasswd_authresult_u.npd_verf.npd_xrandval =
+ htonl(cryptbuf.key.low);
+ /* cache relevant info */
+ if (entry == NULL) {
+ ans = add_upd_item(prin, argp->username,
+ same_user, argp->domain, ident, rval,
+ &deskeys[0], (char *)xpass);
+ if (ans <= 0) {
+ result->status = NPD_FAILED;
+ result->nispasswd_authresult_u.npd_err =
+ NPD_SYSTEMERR;
+ } else
+ result->status = NPD_TRYAGAIN;
+ } else {
+ /* found an entry, attempt == max_attempts */
+ if (entry->ul_attempt == max_attempts) {
+ result->status = NPD_FAILED;
+ result->nispasswd_authresult_u.npd_err =
+ NPD_SYSTEMERR;
+ /*
+ * not really a system error but we
+ * want the caller to think that 'cos
+ * they are obviously trying to break-in.
+ * Perhaps, we should not respond at all,
+ * the client side would timeout.
+ */
+ }
+ }
+ if (verbose)
+ (void) __npd_print_entry(prin);
+ return (TRUE);
+ }
+ } else {
+ if (is_admin == FALSE) { /* not privileged */
+ (void) nis_freeresult(pass_res);
+ result->status = NPD_FAILED;
+ result->nispasswd_authresult_u.npd_err =
+ NPD_PERMDENIED;
+ return (TRUE);
+ }
+ /* admin changing another users password */
+ if (__authenticate_admin(prin, (char *)xpass) == FALSE) {
+ /*
+ * we have no idea where this admin's
+ * passwd record is stored BUT we do know
+ * where their PK cred(s) is stored from
+ * the netname, so lets try to decrypt
+ * the secret key(s) with the passwd that
+ * was sent across.
+ */
+ (void) nis_freeresult(pass_res);
+ result->status = NPD_TRYAGAIN;
+ result->nispasswd_authresult_u.npd_verf.npd_xid =
+ htonl(cryptbuf.key.high);
+ result->nispasswd_authresult_u.npd_verf.npd_xrandval =
+ htonl(cryptbuf.key.low);
+ /* cache relevant info */
+ if (entry == NULL) {
+ ans = add_upd_item(prin, argp->username,
+ same_user, argp->domain, ident, rval,
+ &deskeys[0], (char *)xpass);
+ if (ans <= 0) {
+ result->status = NPD_FAILED;
+ result->nispasswd_authresult_u.npd_err =
+ NPD_SYSTEMERR;
+ }
+ }
+ return (TRUE);
+ }
+ }
+ /* done with pass_res */
+ (void) nis_freeresult(pass_res);
+ /* cache relevant info */
+ if (entry == NULL) {
+ ans = add_upd_item(prin, argp->username, same_user,
+ argp->domain, ident, rval, &deskeys[0],
+ (char *)xpass);
+ if (ans <= 0) {
+ result->status = NPD_FAILED;
+ result->nispasswd_authresult_u.npd_err =
+ NPD_SYSTEMERR;
+ return (TRUE);
+ }
+ } else {
+ if (entry->ul_oldpass != NULL)
+ free(entry->ul_oldpass);
+ entry->ul_oldpass = strdup((char *)&xpass[0]);
+ if (entry->ul_oldpass == NULL) {
+ result->status = NPD_FAILED;
+ result->nispasswd_authresult_u.npd_err =
+ NPD_SYSTEMERR;
+ return (TRUE);
+ }
+ if (upd_entry == TRUE) {
+ entry->ul_ident = ident;
+ entry->ul_rval = rval;
+ entry->ul_key = deskeys[0];
+ }
+ }
+ result->status = NPD_SUCCESS;
+ result->nispasswd_authresult_u.npd_verf.npd_xid =
+ htonl(cryptbuf.key.high);
+ result->nispasswd_authresult_u.npd_verf.npd_xrandval =
+ htonl(cryptbuf.key.low);
+ if (verbose)
+ (void) __npd_print_entry(prin);
+ return (TRUE);
+}
+
+/*
+ * service routine for second part of the nispasswd update
+ * protocol.
+ */
+/* ARGSUSED2 */
+bool_t
+nispasswd_update_1_svc(updreq, res, rqstp)
+npd_update *updreq;
+nispasswd_updresult *res;
+struct svc_req *rqstp;
+{
+ struct update_item *entry;
+ npd_newpass cryptbuf;
+ passbuf pass;
+ char *newpass, buf[NIS_MAXNAMELEN];
+ uint32_t rand;
+ struct nis_result *pass_res = NULL, *mod_res = NULL;
+ struct nis_object *pobj = NULL, *eobj = NULL;
+ entry_col ecol[8];
+ char *old_gecos, *old_shell, *sp;
+ char shadow[80];
+ static nispasswd_error errlist[3];
+ int error = NPD_SUCCESS;
+ register int i;
+ char *old_pass;
+ entry_col * eobj_col;
+ uint_t eobj_col_len;
+
+ /* set to success, and reset to error when warranted */
+ res->status = NPD_SUCCESS;
+
+ entry = (struct update_item *)__npd_item_by_key(updreq->ident);
+ if (entry == NULL) {
+ syslog(LOG_ERR, "invalid identifier: %ld", updreq->ident);
+ res->status = NPD_FAILED;
+ res->nispasswd_updresult_u.npd_err = NPD_IDENTINVALID;
+ return (TRUE);
+ }
+
+ if (verbose) {
+ syslog(LOG_DEBUG, "received NIS+ passwd update request for %s",
+ entry->ul_user);
+ }
+
+ /*
+ * iterate thru entry list and decrypt R sent and new passwd until
+ * we have a rand hit (or reach end of list)
+ */
+ for (; entry != NULL;
+ entry = (struct update_item *)entry->ul_item.next) {
+
+ /* decrypt R and new passwd */
+ cryptbuf.npd_xrandval = ntohl(updreq->xnewpass.npd_xrandval);
+ for (i = 0; i < __NPD_MAXPASSBYTES; i++)
+ cryptbuf.pass[i] = updreq->xnewpass.pass[i];
+
+ if (! __npd_cbc_crypt(&rand, pass, __NPD_MAXPASSBYTES,
+ &cryptbuf, _NPD_PASSMAXLEN, DES_DECRYPT,
+ &entry->ul_key)) {
+ syslog(LOG_ERR, "failed to decrypt verifier");
+ res->status = NPD_FAILED;
+ res->nispasswd_updresult_u.npd_err = NPD_DECRYPTFAIL;
+ return (TRUE);
+ }
+
+ /* check if R sent/decrypted matches cached R */
+ if (rand == entry->ul_rval) {
+ if (verbose)
+ syslog(LOG_DEBUG, "rand hit (%ld) for user '%s'",
+ rand, entry->ul_user);
+ break;
+ } else
+ if (verbose)
+ syslog(LOG_ERR,
+ "rand miss: entry rval=%ld; continue...",
+ entry->ul_rval);
+ }
+
+ if (entry == NULL) {
+ if (verbose)
+ syslog(LOG_ERR, "update failed; no rand matches");
+ res->status = NPD_FAILED;
+ res->nispasswd_updresult_u.npd_err = NPD_VERFINVALID;
+ return (TRUE);
+ }
+
+ /* create passwd struct with this pass & gecos/shell */
+ pass_res = nis_getpwdent(entry->ul_user, entry->ul_domain);
+
+ /* encrypt new passwd */
+ if (!(newpass = __npd_encryptpass(pass, NIS_RES_OBJECT(pass_res)))) {
+ syslog(LOG_ERR, "password encryption failed");
+ res->status = NPD_FAILED;
+ res->nispasswd_updresult_u.npd_err = NPD_ENCRYPTFAIL;
+ return (TRUE);
+ }
+
+ if (pass_res == NULL) {
+ syslog(LOG_ERR, "invalid args %s and %s",
+ entry->ul_user, entry->ul_domain);
+ res->status = NPD_FAILED;
+ res->nispasswd_updresult_u.npd_err = NPD_NOSUCHENTRY;
+ return (TRUE);
+ }
+
+ switch (pass_res->status) {
+ case NIS_SUCCESS:
+ pobj = NIS_RES_OBJECT(pass_res);
+ break;
+ case NIS_NOTFOUND:
+ syslog(LOG_ERR, "no passwd entry found for %s",
+ entry->ul_user);
+ (void) nis_freeresult(pass_res);
+ res->status = NPD_FAILED;
+ res->nispasswd_updresult_u.npd_err = NPD_NOSUCHENTRY;
+ return (TRUE);
+ default:
+ syslog(LOG_ERR,
+ "NIS+ error (%d) getting passwd entry for %s",
+ pass_res->status, entry->ul_user);
+ (void) nis_freeresult(pass_res);
+ res->status = NPD_FAILED;
+ res->nispasswd_updresult_u.npd_err = NPD_NISERROR;
+ return (TRUE);
+ }
+
+ old_pass = ENTRY_VAL(pobj, 1);
+ old_gecos = ENTRY_VAL(pobj, 4);
+ old_shell = ENTRY_VAL(pobj, 6);
+
+ /* can change passwd, shell or gecos */
+ (void) memset(ecol, 0, sizeof (ecol));
+ ecol[1].ec_value.ec_value_val = newpass;
+ ecol[1].ec_value.ec_value_len = strlen(newpass) + 1;
+ ecol[1].ec_flags = EN_CRYPT|EN_MODIFIED;
+
+ /* clear out the error list */
+ (void) memset(errlist, 0, sizeof (errlist));
+
+ /* if a gecos field is provided... */
+ if (*updreq->pass_info.pw_gecos != '\0') {
+ if (__npd_can_do(NIS_MODIFY_ACC, pobj,
+ entry->ul_item.name, 4) == FALSE) {
+ syslog(LOG_NOTICE,
+ "insufficient permission for %s to change the gecos",
+ entry->ul_user);
+ res->status = NPD_PARTIALSUCCESS;
+ errlist[0].npd_field = NPD_GECOS;
+ errlist[0].npd_code = NPD_PERMDENIED;
+ errlist[0].next = NULL;
+ } else if (old_gecos == NULL ||
+ strcmp(updreq->pass_info.pw_gecos, old_gecos) != 0) {
+
+ ecol[4].ec_value.ec_value_val =
+ updreq->pass_info.pw_gecos;
+ ecol[4].ec_value.ec_value_len =
+ strlen(updreq->pass_info.pw_gecos) + 1;
+ ecol[4].ec_flags = EN_MODIFIED;
+ }
+ }
+
+ /* if a shell field is provided... */
+ if (*updreq->pass_info.pw_shell != '\0') {
+ if (__npd_can_do(NIS_MODIFY_ACC, pobj,
+ entry->ul_item.name, 6) == FALSE) {
+ syslog(LOG_NOTICE,
+ "insufficient permission for %s to change the shell",
+ entry->ul_user);
+
+ /*
+ * If already set to partial success, that means
+ * that gecos field and error was provided, so
+ * add the next item to the error list.
+ */
+ if (res->status == NPD_PARTIALSUCCESS) {
+ errlist[0].next = &errlist[1];
+ errlist[1].npd_field = NPD_SHELL;
+ errlist[1].npd_code = NPD_PERMDENIED;
+ errlist[1].next = NULL;
+ } else {
+ res->status = NPD_PARTIALSUCCESS;
+ errlist[0].npd_field = NPD_SHELL;
+ errlist[0].npd_code = NPD_PERMDENIED;
+ errlist[0].next = NULL;
+ }
+ } else if (old_shell == NULL ||
+ strcmp(updreq->pass_info.pw_shell, old_shell) != 0) {
+ ecol[6].ec_value.ec_value_val =
+ updreq->pass_info.pw_shell;
+ ecol[6].ec_value.ec_value_len =
+ strlen(updreq->pass_info.pw_shell) + 1;
+ ecol[6].ec_flags = EN_MODIFIED;
+ }
+ }
+
+ /* update lstchg field in the shadow area */
+ sp = ENTRY_VAL(pobj, 7);
+ if (sp != NULL) {
+ if ((sp = strchr(ENTRY_VAL(pobj, 7), ':')) == NULL) {
+ syslog(LOG_ERR, "shadow column corrupted: user %s",
+ entry->ul_user);
+ (void) nis_freeresult(pass_res);
+ res->status = NPD_FAILED;
+ res->nispasswd_updresult_u.npd_err = NPD_SHDWCORRUPT;
+ goto end;
+ }
+ (void) sprintf(shadow, "%d%s", (int)DAY_NOW, sp);
+ ecol[7].ec_value.ec_value_val = shadow;
+ ecol[7].ec_value.ec_value_len = strlen(shadow) + 1;
+ ecol[7].ec_flags = EN_CRYPT|EN_MODIFIED;
+ }
+
+ /* clone an entry object to update passwd entry */
+ eobj = nis_clone_object(pobj, NULL);
+ if (eobj == NULL) {
+ syslog(LOG_CRIT, "out of memory");
+ res->status = NPD_FAILED;
+ res->nispasswd_updresult_u.npd_err = NPD_SYSTEMERR;
+ goto end;
+ }
+
+ /* save the old values for restoring before freeing the object */
+ eobj_col = eobj->EN_data.en_cols.en_cols_val;
+ eobj_col_len = eobj->EN_data.en_cols.en_cols_len;
+
+ /* set column value to entry column */
+ eobj->EN_data.en_cols.en_cols_val = ecol;
+ eobj->EN_data.en_cols.en_cols_len = 8;
+
+ /* strlen("[name=],passwd.") + null + "." = 17 */
+ if ((strlen(entry->ul_user) + strlen(pobj->zo_domain) + 17) >
+ (size_t)NIS_MAXNAMELEN) {
+ syslog(LOG_ERR, "not enough buffer space");
+ res->status = NPD_FAILED;
+ res->nispasswd_updresult_u.npd_err = NPD_BUFTOOSMALL;
+ goto end;
+ }
+
+ /* put together table index info and object modification */
+ (void) sprintf(buf, "[name=%s],passwd.%s", entry->ul_user,
+ pobj->zo_domain);
+
+ /* add dot "." if necessary */
+ if (buf[strlen(buf) - 1] != '.')
+ (void) strcat(buf, ".");
+
+ /* update NIS+ passwd table */
+ mod_res = nis_modify_entry(buf, eobj, 0);
+
+ /* if NIS+ update fails, bail now */
+ if (mod_res->status != NIS_SUCCESS) {
+ syslog(LOG_DEBUG, "could not update NIS+ passwd table");
+ res->status = NPD_FAILED;
+ res->nispasswd_updresult_u.npd_err = NPD_NISERROR;
+ goto end;
+ }
+
+ /* NIS+ master updated; if YP-forwarding turned on, do YP */
+ if (ypfwd) {
+
+ int try = 0; /* retry counter for YP & NIS+ updates */
+
+ /*
+ * Attempt the YP passwd map update;
+ * on failures use exponential backoff
+ */
+ while ((update_authtok_nis_fwd(entry->ul_user, newpass,
+ entry->ul_oldpass, ypfwd, old_gecos, old_shell)
+ == -1) && (try < MAX_RETRY)) {
+ (void) sleep((unsigned)(1<<try));
+ try++;
+ }
+
+ /*
+ * On repeated failures (MAX_RETRY),
+ * give up & undo NIS+ table update
+ */
+ if (try == MAX_RETRY) {
+ try = 0; /* clear for undo attempts */
+
+ syslog(LOG_ERR,
+ "unable to update NIS(YP) passwd \
+entry on %s for %s", ypfwd, entry->ul_user);
+
+ /*
+ * The passwd tbl update failed, so set 'error'
+ * to an NPD failure. Here is where it gets
+ * tricky. The permissions on the passwd table
+ * must be turned off because the failover mech-
+ * anism will try to get NISD to make the update.
+ */
+ error = res->status = NPD_FAILED;
+ res->nispasswd_updresult_u.npd_err = NPD_SYSTEMERR;
+
+ /* set "new" passwd back to old password */
+ ecol[1].ec_value.ec_value_val = old_pass;
+ ecol[1].ec_value.ec_value_len = strlen(old_pass) + 1;
+
+ /* undo the NIS+ passwd table update */
+ do {
+
+ /* exponential backoff each try */
+ if (try) (void) sleep((unsigned)(1<<try));
+
+ /*
+ * Update table with old passwd information,
+ * freeing the old 'mod_res' struct first.
+ * 'eobj' has already been set to 'ecol' above.
+ */
+ if (mod_res) (void) nis_freeresult(mod_res);
+ mod_res = nis_modify_entry(buf, eobj, 0);
+
+ /* On success, exit; otherwise increment ctr */
+ if (mod_res->status == NIS_SUCCESS) break;
+ else try++;
+
+ } while (try < MAX_RETRY);
+
+ /* NIS+ update repeated failures, bail now and log */
+ if (try == MAX_RETRY) {
+ syslog(LOG_ERR, "WARNING: unable to undo NIS+ \
+passwd update; maybe out-of-sync with YP map -- verify by hand");
+ }
+ }
+ }
+
+ /*
+ * The 'error' flag can only be changed from NPD_SUCCESS if
+ * YP updating was on and then, only in a failure scenario.
+ * In all other situations, update the credential!
+ */
+ if (error == NPD_SUCCESS) {
+
+ /* attempt to update PK cred(s) */
+ (void) __npd_upd_all_pk_creds(entry->ul_user, entry->ul_domain,
+ entry->ul_oldpass, pass, &error);
+ if (error != NIS_SUCCESS) {
+ if (res->status == NPD_PARTIALSUCCESS) {
+ if (errlist[0].next == NULL) {
+ errlist[0].next = &errlist[1];
+ errlist[1].npd_field = NPD_SECRETKEY;
+ errlist[1].npd_code = error;
+ errlist[1].next = NULL;
+ } else if (errlist[1].next == NULL) {
+ errlist[1].next = &errlist[2];
+ errlist[2].npd_field = NPD_SECRETKEY;
+ errlist[2].npd_code = error;
+ errlist[2].next = NULL;
+ }
+ } else {
+ res->status = NPD_PARTIALSUCCESS;
+ errlist[0].npd_field = NPD_SECRETKEY;
+ errlist[0].npd_code = error;
+ errlist[0].next = NULL;
+ }
+
+ /*
+ * Only set the reason union member if partial success;
+ * otherwise may wipe out npd_err union member.
+ */
+ if (res->status == NPD_PARTIALSUCCESS)
+ res->nispasswd_updresult_u.reason = errlist[0];
+ }
+ }
+
+ /* Epilogue just consists of freeing up data */
+end:
+ /* Restore column stuff so that we can free eobj */
+ if (eobj) {
+ eobj->EN_data.en_cols.en_cols_val = eobj_col;
+ eobj->EN_data.en_cols.en_cols_len = eobj_col_len;
+ (void) nis_destroy_object(eobj);
+ }
+
+ /*
+ * The code to free pobj is not necessary as in
+ * yppasswdproc_update_1_svc() because pobj is
+ * freed when pass_res is just below. Otherwise
+ * if it's in here, it will dump core.
+ */
+ if (mod_res) (void) nis_freeresult(mod_res);
+ if (pass_res) (void) nis_freeresult(pass_res);
+ if (entry) (void) free_upd_item(entry);
+ return (TRUE);
+}
+
+/*
+ * yppasswd update service routine.
+ * The error codes returned are from the 4.x rpc.yppasswdd.c,
+ * it seems that the client side only checks if the result is
+ * non-zero in which case it prints a generic message !
+ */
+/* ARGSUSED2 */
+bool_t
+yppasswdproc_update_1_svc(yppass, result, rqstp)
+struct yppasswd *yppass;
+int *result;
+struct svc_req *rqstp;
+{
+ char buf[NIS_MAXNAMELEN], *dom;
+ struct passwd *newpass;
+ struct nis_result *mod_res = NULL;
+ struct nis_object *pobj = NULL, *eobj = NULL;
+ entry_col ecol[8];
+ char shadow[80], *sp, *p;
+ char *old_gecos, *old_shell, *old_pass;
+ nis_server *srv;
+ nis_tag tags[2], *tagres;
+ int status, ans;
+ entry_col * eobj_col;
+ uint_t eobj_col_len;
+
+ /* set new password from YP passwd struct */
+ newpass = &yppass->newpw;
+
+ if (verbose)
+ syslog(LOG_ERR,
+ "received yp password update request from %s",
+ newpass->pw_name);
+
+ /* fill-in NIS+ server information */
+ srv = (nis_server *)__nis_host2nis_server(NULL, 0, &ans);
+ if (srv == NULL) {
+ syslog(LOG_ERR, "no host/addr information: %d", ans);
+ *result = -1;
+ return (TRUE);
+ }
+
+ /*
+ * make the nis_stats call to check if the server is running
+ * in compat mode and get the list of directories this server
+ * is serving
+ */
+ tags[0].tag_type = TAG_NISCOMPAT;
+ tags[0].tag_val = "";
+ tags[1].tag_type = TAG_DIRLIST;
+ tags[1].tag_val = "";
+
+ status = nis_stats(srv, tags, 2, &tagres);
+ __free_nis_server(srv);
+ if (status != NIS_SUCCESS) {
+ syslog(LOG_ERR, "nis_error: %d", status);
+ *result = -1;
+ return (1);
+ }
+
+ if ((strcmp(tagres[0].tag_val, "<Unknown Statistics>") == 0) ||
+ (strcmp(tagres[1].tag_val, "<Unknown Statistics>") == 0)) {
+ /* old server */
+ syslog(LOG_ERR,
+ "NIS+ server does not support the new statistics tags");
+ nis_freetags(tagres, 2);
+ *result = -1;
+ return (1);
+ }
+
+ /* check if server is running in NIS compat mode */
+ if (strcasecmp(tagres[0].tag_val, "OFF") == 0) {
+ syslog(LOG_ERR,
+ "Local NIS+ server is not running in NIS compat mode");
+ *result = -1;
+ nis_freetags(tagres, 2);
+ return (1);
+ }
+ /*
+ * find the dir that has a passwd entry for this user
+ * POLICY: if user has a passwd stored in more then one
+ * dir then do not make an update. if user has an entry
+ * in only one dir, then make an update in that dir.
+ */
+
+ if (! __npd_find_obj(newpass->pw_name, tagres[1].tag_val, &pobj)) {
+ *result = -1;
+ nis_freetags(tagres, 2);
+ return (1);
+ }
+ nis_freetags(tagres, 2);
+
+ old_pass = ENTRY_VAL(pobj, 1);
+ old_gecos = ENTRY_VAL(pobj, 4);
+ old_shell = ENTRY_VAL(pobj, 6);
+
+ if (!__npd_has_aged(pobj, &ans) && ans == NPD_NOTAGED) {
+ syslog(LOG_ERR, "password has not aged enough for %s",
+ newpass->pw_name);
+ *result = -1;
+ goto ypend;
+ }
+ /* if ans == NPD_NOSHDWINFO then aging cannot be enforced */
+
+ /* validate the old passwd */
+ if (old_pass == NULL) {
+ syslog(LOG_ERR, "no passwd found for %s", newpass->pw_name);
+ *result = 7; /* password incorrect */
+ goto ypend;
+ }
+
+ /* mismatch on old passwd */
+ if (strcmp(crypt((const char *)yppass->oldpass,
+ (const char *)old_pass), old_pass) != 0) {
+ syslog(LOG_ERR, "incorrect passwd for %s", newpass->pw_name);
+ *result = 7;
+ goto ypend;
+ }
+
+ /* can change passwd, gecos or shell */
+ (void) memset(ecol, 0, sizeof (ecol));
+
+ if (strcmp(old_pass, newpass->pw_passwd) != 0) {
+ ecol[1].ec_value.ec_value_val = newpass->pw_passwd;
+ ecol[1].ec_value.ec_value_len =
+ strlen(newpass->pw_passwd) + 1;
+ ecol[1].ec_flags = EN_CRYPT|EN_MODIFIED;
+ }
+
+ if (strcmp(nis_leaf_of(pobj->zo_domain), "org_dir") == 0) {
+ /* need to strip org_dir part of the domain */
+ dom = strchr(pobj->zo_domain, '.');
+ if (dom != NULL)
+ dom++;
+ } else
+ dom = pobj->zo_domain;
+
+ /* "." + null + "." = 3 */
+ if ((strlen(newpass->pw_name) + strlen(dom) + 3) >
+ (size_t)NIS_MAXNAMELEN) {
+ syslog(LOG_ERR, "not enough buffer space");
+ *result = -1;
+ goto ypend;
+ }
+ (void) sprintf(buf, "%s.%s", newpass->pw_name, dom);
+ if (buf[strlen(buf) - 1] != '.')
+ (void) strcat(buf, ".");
+
+ if (newpass->pw_gecos != NULL &&
+ (old_gecos == NULL ||
+ strcmp(old_gecos, newpass->pw_gecos) != 0)) {
+
+ if (__npd_can_do(NIS_MODIFY_ACC, pobj, buf, 4) == FALSE) {
+ syslog(LOG_NOTICE,
+ "insufficient permission for %s to change the gecos",
+ newpass->pw_name);
+ *result = 2;
+ goto ypend;
+ }
+ ecol[4].ec_value.ec_value_val = newpass->pw_gecos;
+ ecol[4].ec_value.ec_value_len =
+ strlen(newpass->pw_gecos) + 1;
+ ecol[4].ec_flags = EN_MODIFIED;
+ }
+
+ if (newpass->pw_shell != NULL &&
+ (old_shell == NULL ||
+ strcmp(old_shell, newpass->pw_shell) != 0)) {
+
+ if (! __npd_can_do(NIS_MODIFY_ACC, pobj, buf, 6)) {
+ syslog(LOG_NOTICE,
+ "insufficient permission for %s to change the shell",
+ newpass->pw_name);
+ *result = 2;
+ goto ypend;
+ }
+ ecol[6].ec_value.ec_value_val = newpass->pw_shell;
+ ecol[6].ec_value.ec_value_len =
+ strlen(newpass->pw_shell) + 1;
+ ecol[6].ec_flags = EN_MODIFIED;
+ }
+ /*
+ * from 4.x:
+ * This fixes a really bogus security hole, basically anyone can
+ * call the rpc passwd daemon, give them their own passwd and a
+ * new one that consists of ':0:0:Im root now:/:/bin/csh^J' and
+ * give themselves root access. With this code it will simply make
+ * it impossible for them to login again, and as a bonus leave
+ * a cookie for the always vigilant system administrator to ferret
+ * them out.
+ */
+
+ for (p = newpass->pw_name; (*p != '\0'); p++)
+ if ((*p == ':') || !(isprint(*p)))
+ *p = '$'; /* you lose ! */
+ for (p = newpass->pw_passwd; (*p != '\0'); p++)
+ if ((*p == ':') || !(isprint(*p)))
+ *p = '$'; /* you lose ! */
+
+ /* update lstchg field */
+ sp = ENTRY_VAL(pobj, 7);
+ if (sp != NULL) {
+ if ((sp = strchr(sp, ':')) == NULL) {
+ syslog(LOG_ERR, "shadow column corrupted: user %s",
+ newpass->pw_name);
+ *result = -1;
+ goto ypend;
+ }
+ (void) sprintf(shadow, "%d%s", (int)DAY_NOW, sp);
+ ecol[7].ec_value.ec_value_val = shadow;
+ ecol[7].ec_value.ec_value_len = strlen(shadow) + 1;
+ ecol[7].ec_flags = EN_CRYPT|EN_MODIFIED;
+ }
+
+ /* clone an entry object to update passwd entry */
+ eobj = nis_clone_object(pobj, NULL);
+ if (eobj == NULL) {
+ syslog(LOG_CRIT, "out of memory");
+ *result = -1;
+ goto ypend;
+ }
+
+ /* save the old values to restore while freeing the object */
+ eobj_col = eobj->EN_data.en_cols.en_cols_val;
+ eobj_col_len = eobj->EN_data.en_cols.en_cols_len;
+
+ /* set column value to entry column */
+ eobj->EN_data.en_cols.en_cols_val = ecol;
+ eobj->EN_data.en_cols.en_cols_len = 8;
+
+ /* strlen("[name=],passwd.") + null + "." = 17 */
+ if ((strlen(newpass->pw_name) + strlen(pobj->zo_domain) + 17) >
+ (size_t)NIS_MAXNAMELEN) {
+ syslog(LOG_ERR, "not enough buffer space");
+ *result = -1;
+ goto ypend;
+ }
+
+ /* put together table index info and object modification */
+ (void) sprintf(buf, "[name=%s],passwd.%s", newpass->pw_name,
+ pobj->zo_domain);
+
+ /* add dot "." if necessary */
+ if (buf[strlen(buf) - 1] != '.')
+ (void) strcat(buf, ".");
+
+ /* update NIS+ passwd table */
+ mod_res = nis_modify_entry(buf, eobj, 0);
+
+ /* nisd in temp read-only mode (nisbackup(1M)) */
+ if (mod_res->status == NIS_TRYAGAIN) {
+ syslog(LOG_INFO,
+ "could not update NIS+ passwd: %s",
+ nis_sperrno(mod_res->status));
+ /*
+ * 8 is the magic number from way back in 4.1.x.
+ * User should see
+ * "Password file/table busy. Try again later."
+ */
+ *result = 8;
+ goto ypend;
+ }
+
+
+ /* if NIS+ update fails, bail now */
+ if (mod_res->status != NIS_SUCCESS) {
+ syslog(LOG_ERR,
+ "could not update NIS+ passwd: %s",
+ nis_sperrno(mod_res->status));
+ *result = 13;
+ goto ypend;
+ }
+
+ /*
+ * nothing happens here because we cannot re-encrypt the credential
+ * because we do not have the unencrypted new password .... :^(
+ */
+ *result = 0;
+
+ /* NIS+ master updated; if YP-forwarding turned on, do YP */
+ if (ypfwd) {
+
+ int try = 0; /* retry counter for YP & NIS+ updates */
+
+ /*
+ * Attempt the YP passwd map update;
+ * on failures use exponential backoff
+ */
+ while ((update_authtok_nis_fwd(newpass->pw_name,
+ newpass->pw_passwd, yppass->oldpass, ypfwd,
+ old_gecos, old_shell) == -1) &&
+ (try < MAX_RETRY)) {
+ (void) sleep((unsigned)(1<<try));
+ try++;
+ }
+
+ /*
+ * On repeated failures (MAX_RETRY),
+ * give up & undo NIS+ table update
+ */
+ if (try == MAX_RETRY) {
+ try = 0; /* clear for undo attempts */
+ syslog(LOG_ERR,
+ "unable to update NIS(YP) passwd \
+entry on %s for %s", ypfwd, newpass->pw_name);
+ *result = -1;
+
+ /* set "new" passwd back to old password */
+ ecol[1].ec_value.ec_value_val = old_pass;
+ ecol[1].ec_value.ec_value_len = strlen(old_pass) + 1;
+
+ /* undo the NIS+ passwd table update */
+ do {
+ /* exponential backoff each try */
+ if (try) (void) sleep((unsigned)(1<<try));
+
+ /*
+ * Update table with old passwd information,
+ * freeing the old 'mod_res' struct first.
+ * 'eobj' has already been set to 'ecol' above.
+ */
+ if (mod_res) (void) nis_freeresult(mod_res);
+ mod_res = nis_modify_entry(buf, eobj, 0);
+
+ /* On success, exit; otherwise increment ctr */
+ if (mod_res->status == NIS_SUCCESS) break;
+ else try++;
+
+ } while (try < MAX_RETRY);
+
+ /* NIS+ update repeated failures, bail now and log */
+ if (try == MAX_RETRY) {
+ syslog(LOG_ERR, "WARNING: unable to undo NIS+ \
+passwd update; maybe out-of-sync with YP map -- verify by hand");
+ }
+ }
+ }
+
+ /* the epilogue just consists of freeing up data */
+ypend:
+ /* Restore column stuff so that we can free eobj/pobj */
+ if (eobj) {
+ eobj->EN_data.en_cols.en_cols_val = eobj_col;
+ eobj->EN_data.en_cols.en_cols_len = eobj_col_len;
+ (void) nis_destroy_object(eobj);
+ }
+ if (pobj)
+ (void) nis_destroy_object(pobj);
+ if (mod_res) (void) nis_freeresult(mod_res);
+ return (1);
+}
+
+/* ARGSUSED */
+int
+nispasswd_prog_1_freeresult(transp, xdr_result, result)
+SVCXPRT *transp;
+xdrproc_t xdr_result;
+caddr_t result;
+{
+
+ /*
+ * (void) xdr_free(xdr_result, result);
+ * Insert additional freeing code here, if needed
+ */
+
+ return (1);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_svcsubr.c b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_svcsubr.c
new file mode 100644
index 0000000000..b8296528a2
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_svcsubr.c
@@ -0,0 +1,1377 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 1994-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * npd_svcsubr.c
+ * Contains the sub-routines required by the server.
+ *
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <syslog.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <memory.h>
+#include <shadow.h>
+#include <crypt.h>
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpc/key_prot.h>
+#include <rpc/des_crypt.h>
+#include <mp.h>
+#include <rpcsvc/nis.h>
+#include <rpcsvc/nispasswd.h>
+#include <rpcsvc/nis_dhext.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <limits.h>
+#include "npd_svcsubr.h"
+#include "nisplus_tables.h"
+
+extern int verbose;
+extern bool_t debug;
+extern bool_t generatekeys;
+
+bool_t __npd_prin2netname(char *, char []);
+static struct passwd *nis_object2passwd(nis_object *obj);
+
+/*
+ * get the password information for this user
+ * the result should be freed using nis_freeresult()
+ */
+struct nis_result *
+nis_getpwdent(user, domain)
+char *user;
+char *domain;
+{
+ char buf[NIS_MAXNAMELEN];
+
+ if ((user == NULL || *user == '\0') ||
+ (domain == NULL || *domain == '\0'))
+ return (NULL);
+
+ /* strlen("[name=],passwd.org_dir.") + null + "." = 25 */
+ if ((25 + strlen(user) + strlen(domain)) >
+ (size_t)NIS_MAXNAMELEN)
+ return (NULL);
+
+ (void) sprintf(buf, "[name=%s],passwd.org_dir.%s", user, domain);
+ if (buf[strlen(buf) - 1] != '.')
+ (void) strcat(buf, ".");
+
+ return (nis_list(buf, USE_DGRAM+MASTER_ONLY, NULL, NULL));
+}
+
+/*
+ * get the credential information for this user
+ * the result should be freed using nis_freeresult()
+ */
+static struct nis_result *
+nis_getcredent(principal, domain, auth_type)
+char *principal;
+char *domain;
+char *auth_type;
+{
+ char buf[NIS_MAXNAMELEN];
+
+ if ((principal == NULL || *principal == '\0') ||
+ (domain == NULL || *domain == '\0') || auth_type == NULL)
+ return (NULL);
+
+ /* strlen("[cname=,auth_type=],cred.org_dir.") + null + "." = 35 */
+ if ((35 + strlen(principal) + strlen(domain)) >
+ (size_t)NIS_MAXNAMELEN)
+ return (NULL);
+
+ (void) sprintf(buf, "[cname=%s,auth_type=%s],cred.org_dir.%s",
+ principal, auth_type, domain);
+
+ if (buf[strlen(buf) - 1] != '.')
+ (void) strcat(buf, ".");
+
+ return (nis_list(buf, USE_DGRAM+MASTER_ONLY, NULL, NULL));
+}
+
+static bool_t
+__npd_fill_spwd(obj, sp)
+struct nis_object *obj;
+struct spwd *sp;
+{
+ long val;
+ char *p = NULL, *ageinfo = NULL;
+ char *ep, *end;
+
+ /* defaults */
+ sp->sp_lstchg = -1;
+ sp->sp_min = -1;
+ sp->sp_max = -1;
+ sp->sp_warn = -1;
+ sp->sp_inact = -1;
+ sp->sp_expire = -1;
+ sp->sp_flag = 0;
+
+ /* shadow information is in column 7 */
+ if ((p = ENTRY_VAL(obj, 7)) == NULL)
+ return (FALSE);
+ if ((ageinfo = strdup(p)) == NULL)
+ return (FALSE);
+
+ p = ageinfo;
+ end = ageinfo + ENTRY_LEN(obj, 7);
+
+ /* format is: lstchg:min:max:warn:inact:expire:flag */
+
+ val = strtol(p, &ep, 10); /* base = 10 */
+ if (*ep != ':' || ep >= end) {
+ (void) free(ageinfo);
+ return (FALSE);
+ }
+ if (ep != p)
+ sp->sp_lstchg = val;
+ p = ep + 1;
+
+ val = strtol(p, &ep, 10);
+ if (*ep != ':' || ep >= end) {
+ (void) free(ageinfo);
+ return (FALSE);
+ }
+ if (ep != p)
+ sp->sp_min = val;
+ p = ep + 1;
+
+ val = strtol(p, &ep, 10);
+ if (*ep != ':' || ep >= end) {
+ (void) free(ageinfo);
+ return (FALSE);
+ }
+ if (ep != p)
+ sp->sp_max = val;
+ p = ep + 1;
+
+ val = strtol(p, &ep, 10);
+ if (*ep != ':' || ep >= end) {
+ (void) free(ageinfo);
+ return (FALSE);
+ }
+ if (ep != p)
+ sp->sp_warn = val;
+ p = ep + 1;
+
+ val = strtol(p, &ep, 10);
+ if (*ep != ':' || ep >= end) {
+ (void) free(ageinfo);
+ return (FALSE);
+ }
+ if (ep != p)
+ sp->sp_inact = val;
+ p = ep + 1;
+
+ val = strtol(p, &ep, 10);
+ if (*ep != ':' || ep >= end) {
+ (void) free(ageinfo);
+ return (FALSE);
+ }
+ if (ep != p)
+ sp->sp_expire = val;
+ p = ep + 1;
+
+ val = strtol(p, &ep, 10);
+ if (*ep != ':' || ep >= end) {
+ (void) free(ageinfo);
+ return (FALSE);
+ }
+ if (ep != p)
+ sp->sp_flag = val;
+
+ (void) free(ageinfo);
+ return (TRUE);
+}
+/*
+ * checks if the password has aged sufficiently
+ */
+bool_t
+__npd_has_aged(obj, res)
+struct nis_object *obj;
+int *res;
+{
+ struct spwd sp;
+ long now;
+
+ if (__npd_fill_spwd(obj, &sp) == TRUE) {
+
+ now = DAY_NOW;
+ if (sp.sp_lstchg != 0 && sp.sp_lstchg <= now) {
+ /* password changed before or just now */
+ if (now < (sp.sp_lstchg + sp.sp_min)) {
+ *res = NPD_NOTAGED;
+ return (FALSE);
+ } else
+ return (TRUE);
+ }
+ }
+ *res = NPD_NOSHDWINFO;
+ return (FALSE);
+}
+
+/*
+ * Authenticate the admin by decrypting any of their secret keys with
+ * the passwd they sent across. If any of the secret keys, of mech types
+ * listed in the NIS+ security cf, are decrypted, then return TRUE.
+ */
+bool_t
+__authenticate_admin(char *prin, char *pass)
+{
+ char *d;
+ struct nis_result *cres;
+ char netname[MAXNETNAMELEN+1];
+ mechanism_t **mechs;
+
+ if ((prin == NULL || *prin == '\0') ||
+ (pass == NULL || *pass == '\0'))
+ return (FALSE);
+
+ if (!__npd_prin2netname(prin, netname)) {
+ syslog(LOG_ERR,
+ "__authenticate_admin: cannot convert principal '%s' to netname", prin);
+ return (FALSE);
+ }
+
+ d = strchr(prin, '.');
+ if (d == NULL)
+ d = nis_local_directory();
+ else
+ d++;
+
+ if (mechs = __nis_get_mechanisms(FALSE)) {
+ mechanism_t **mpp;
+ char auth_type[MECH_MAXATNAME+1];
+
+ for (mpp = mechs; *mpp; mpp++) {
+ mechanism_t *mp = *mpp;
+
+ if (verbose)
+ syslog(LOG_INFO,
+ "__authenticate_admin: trying mech '%s' for user '%s'",
+ mp->alias ? mp->alias : "NULL", prin);
+
+ if (AUTH_DES_COMPAT_CHK(mp)) {
+ __nis_release_mechanisms(mechs);
+ goto try_auth_des;
+ }
+ if (! VALID_MECH_ENTRY(mp))
+ continue;
+
+ if (!__nis_mechalias2authtype(mp->alias, auth_type,
+ sizeof (auth_type)))
+ continue;
+
+ cres = nis_getcredent(prin, d, auth_type);
+ if (cres != NULL && cres->status == NIS_SUCCESS) {
+ char *sp;
+
+ if (debug)
+ syslog(LOG_DEBUG,
+ "__authenticate_admin: got cred entry");
+
+ sp = ENTRY_VAL(NIS_RES_OBJECT(cres), 4);
+
+ if (sp != NULL) {
+ if (xdecrypt_g(sp, mp->keylen,
+ mp->algtype, pass,
+ netname, TRUE)) {
+ __nis_release_mechanisms(
+ mechs);
+
+ if (debug)
+ syslog(LOG_DEBUG,
+ "__authenticate_admin: xdecrypt success");
+
+ (void) nis_freeresult(cres);
+ return (TRUE);
+ }
+ }
+ }
+ if (cres != NULL)
+ (void) nis_freeresult(cres);
+
+ }
+ __nis_release_mechanisms(mechs);
+ } else {
+ /*
+ * No valid mechs in the NIS+ security cf file,
+ * so let's try AUTH_DES.
+ */
+ try_auth_des:
+ cres = nis_getcredent(prin, d, AUTH_DES_AUTH_TYPE);
+ if (cres != NULL && cres->status == NIS_SUCCESS) {
+ char *sp;
+ sp = ENTRY_VAL(NIS_RES_OBJECT(cres), 4);
+ if (sp != NULL) {
+ if (xdecrypt_g(sp, AUTH_DES_KEYLEN,
+ AUTH_DES_ALGTYPE, pass,
+ NULL, TRUE)) {
+ (void) nis_freeresult(cres);
+ return (TRUE);
+ }
+ }
+ }
+ if (cres != NULL)
+ (void) nis_freeresult(cres);
+ }
+
+ return (FALSE);
+}
+
+/*
+ * build a netname given a nis+ principal name
+ */
+bool_t
+__npd_prin2netname(prin, netname)
+char *prin;
+char netname[MAXNETNAMELEN];
+{
+ nis_result *pass_res;
+ nis_object *pobj;
+ char name[NIS_MAXNAMELEN];
+ char *domain;
+ uid_t uid;
+
+
+ if (prin == NULL || *prin == '\0')
+ return (FALSE);
+ if (strlen(prin) > (size_t)NIS_MAXNAMELEN)
+ return (FALSE);
+ (void) sprintf(name, "%s", prin);
+
+ /* get the domain name */
+ if (name[strlen(name) - 1] == '.')
+ name[strlen(name) - 1] = '\0';
+ else
+ return (FALSE);
+ domain = strchr(name, '.');
+ if (domain == NULL)
+ return (FALSE);
+ else
+ *domain++ = '\0';
+
+ /* nis_getpwdent() will fully qualify the domain */
+ pass_res = nis_getpwdent(name, domain);
+
+ if (pass_res == NULL)
+ return (FALSE);
+
+ switch (pass_res->status) {
+ case NIS_SUCCESS:
+ pobj = NIS_RES_OBJECT(pass_res);
+ uid = atol(ENTRY_VAL(pobj, 2));
+ (void) nis_freeresult(pass_res);
+ /* we cannot simply call user2netname() ! */
+ if ((strlen(domain) + 7 + 11) > (size_t)MAXNETNAMELEN)
+ return (FALSE);
+ (void) sprintf(netname, "unix.%d@%s", uid, domain);
+ return (TRUE);
+
+ case NIS_NOTFOUND:
+ /*
+ * assumption: hosts DO NOT have their passwords
+ * stored in NIS+ ==> not a user but a host
+ */
+ if ((strlen(domain) + 7 + strlen(name)) >
+ (size_t)MAXNETNAMELEN)
+ return (FALSE);
+ (void) sprintf(netname, "unix.%s@%s", name, domain);
+ (void) nis_freeresult(pass_res);
+ return (TRUE);
+
+ default:
+ syslog(LOG_ERR, "no passwd entry found for %s", prin);
+ (void) nis_freeresult(pass_res);
+ return (FALSE);
+ }
+}
+
+/*
+ * make a new DH (classic "DES" or new "DH640-0") cred entry and add it
+ */
+bool_t
+__npd_addcredent(char *prin, /* principal name */
+ char *domain, /* domain name */
+ char *auth_type, /* cred table auth type name */
+ keylen_t keylen, /* DH key length */
+ algtype_t algtype, /* DH algorithm type */
+ char *newpass) /* new pw used to encrypt secret key */
+{
+ char *public = NULL;
+ char *secret = NULL;
+ char *encrypted_secret = NULL;
+ nis_object obj;
+ entry_col ecol[5];
+ struct nis_result *mod_res;
+ char buf[NIS_MAXNAMELEN];
+ char nisdomain[NIS_MAXNAMELEN];
+ char netname[MAXNETNAMELEN];
+ int status;
+ bool_t ret;
+
+ /* check args */
+ if ((prin == NULL || *prin == '\0') ||
+ (domain == NULL || *domain == '\0') || auth_type == NULL) {
+ ret = FALSE;
+ goto out;
+ }
+
+ if (strlen(domain) > (size_t)NIS_MAXNAMELEN) {
+ ret = FALSE;
+ goto out;
+ }
+ (void) sprintf(nisdomain, "%s", domain);
+ if (nisdomain[strlen(buf) - 1] != '.')
+ (void) strcat(buf, ".");
+
+ /* build netname from principal name */
+ if (__npd_prin2netname(prin, netname) == FALSE) {
+ ret = FALSE;
+ goto out;
+ }
+
+ /* initialize object */
+ memset((char *)&obj, 0, sizeof (obj));
+ memset((char *)ecol, 0, sizeof (ecol));
+
+ obj.zo_name = "cred";
+ obj.zo_ttl = 43200;
+ obj.zo_data.zo_type = NIS_ENTRY_OBJ;
+ obj.zo_owner = prin;
+ obj.zo_group = nis_local_group();
+ obj.zo_domain = nisdomain;
+ /* owner: r; group: rmcd */
+ obj.zo_access = (NIS_READ_ACC<<16)|
+ ((NIS_READ_ACC|NIS_MODIFY_ACC|
+ NIS_CREATE_ACC|NIS_DESTROY_ACC)<<8);
+
+ if (strcasecmp(auth_type, "LOCAL") != 0) {
+
+ if ((public = malloc(BITS2NIBBLES(keylen) + 1)) == NULL) {
+ syslog(LOG_ALERT, "malloc failed");
+ ret = FALSE;
+ goto out;
+ }
+ if ((secret = malloc(BITS2NIBBLES(keylen) + 1)) == NULL) {
+ syslog(LOG_ALERT, "malloc failed");
+ ret = FALSE;
+ goto out;
+ }
+
+ /* generate key-pair */
+ if (! __gen_dhkeys_g(public, secret, keylen, algtype,
+ newpass)) {
+ syslog(LOG_ERR,
+ "could not generate DH key pair for %d-%d",
+ keylen, algtype);
+ ret = FALSE;
+ goto out;
+ }
+ if (! xencrypt_g(secret, keylen, algtype, newpass, netname,
+ &encrypted_secret, TRUE)) {
+ syslog(LOG_ERR,
+ "could not encrypt secret key for %d-%d",
+ keylen, algtype);
+ ret = FALSE;
+ goto out;
+ }
+
+ /* build cred entry */
+ ecol[0].ec_value.ec_value_val = prin;
+ ecol[0].ec_value.ec_value_len = strlen(prin) + 1;
+
+ ecol[1].ec_value.ec_value_val = auth_type;
+ ecol[1].ec_value.ec_value_len = strlen(auth_type) + 1;
+
+ ecol[2].ec_value.ec_value_val = netname;
+ ecol[2].ec_value.ec_value_len = strlen(netname) + 1;
+
+ ecol[3].ec_value.ec_value_val = public;
+ ecol[3].ec_value.ec_value_len = strlen(public) + 1;
+
+ ecol[4].ec_value.ec_value_val = encrypted_secret;
+ ecol[4].ec_value.ec_value_len = strlen(encrypted_secret) + 1;
+ ecol[4].ec_flags |= EN_CRYPT;
+ }
+ obj.EN_data.en_type = "cred_tbl";
+ obj.EN_data.en_cols.en_cols_val = ecol;
+ obj.EN_data.en_cols.en_cols_len = 5;
+
+ /* strlen("cred.org_dir.") + null = 14 */
+ if ((strlen(nisdomain) + 14) > (size_t)NIS_MAXNAMELEN)
+ return (FALSE);
+ (void) sprintf(buf, "cred.org_dir.%s", (char *)&nisdomain[0]);
+
+ if (debug == TRUE)
+ (void) nis_print_object(&obj);
+
+ mod_res = nis_add_entry(buf, &obj, 0);
+ status = mod_res->status;
+ (void) nis_freeresult(mod_res);
+ switch (status) {
+ case NIS_SUCCESS:
+ if (verbose)
+ syslog(LOG_ERR,
+ "generated Diffie-Hellman key pair (type %d-%d) for '%s'",
+ keylen, algtype, prin);
+ ret = TRUE;
+ goto out;
+
+ case NIS_PERMISSION:
+ if (verbose == TRUE)
+ syslog(LOG_ERR,
+ "permission denied to add a %s cred entry for %s",
+ auth_type, prin);
+ ret = FALSE;
+ goto out;
+
+ default:
+ if (verbose == TRUE)
+ syslog(LOG_ERR,
+ "error creating %s cred for %s, NIS+ error: %s",
+ auth_type, prin, nis_sperrno(status));
+ ret = FALSE;
+ goto out;
+ }
+
+out:
+ free(secret);
+ free(public);
+ free(encrypted_secret);
+
+ return (ret);
+}
+
+/*
+ * Reencrypt a DH cred tbl secret key and return a ptr to it's newly
+ * allocated memory on successful return. Also, if the global generatekeys
+ * is set, return a ptr of newly allocated memory containing a new public
+ * key too.
+ *
+ * Return NULL on any type of failure.
+ *
+ * Caller must free secret and public key memory on successful return.
+ */
+static char *
+reencrypt_secret(char *oldcryptsecret, /* in */
+ char *oldpass, /* in */
+ char *newpass, /* in */
+ keylen_t keylen, /* in */
+ algtype_t algtype, /* in */
+ char **public, /* out */
+ bool_t *keyset, /* out */
+ char *netname) /* in */
+{
+ char *secret = NULL;
+ char *reencrypted_secret = NULL;
+ char *tmppk = NULL;
+ uint_t slen;
+
+ if (!oldcryptsecret || !oldpass || !newpass ||
+ (generatekeys && !public) || !keyset || !netname)
+ return (NULL);
+
+ slen = strlen(oldcryptsecret);
+
+ if ((secret = malloc(slen + 1)) == NULL) {
+ syslog(LOG_ALERT,
+ "reencrypte_secret: no memory!");
+ return (NULL);
+ }
+
+ (void) strcpy(secret, oldcryptsecret);
+
+ if (xdecrypt_g(secret, keylen, algtype, oldpass, netname, TRUE) == 0) {
+ if (verbose)
+ syslog(LOG_ERR,
+ "could not decrypt keytype %d-%d for '%s'",
+ keylen, algtype, netname);
+
+ if (generatekeys == TRUE) {
+ if ((tmppk = malloc(slen + 1))
+ == NULL) {
+ syslog(LOG_ALERT, "no memory!");
+ free(secret);
+ return (NULL);
+ }
+ if (!__gen_dhkeys_g(tmppk, secret, keylen,
+ algtype, newpass)) {
+ syslog(LOG_ERR,
+ "could not generate new Diffie-Hellman key pair for type %d-%d",
+ keylen, algtype);
+ free(secret);
+ free(tmppk);
+ return (NULL);
+ }
+ *keyset = TRUE;
+ if (verbose)
+ syslog(LOG_ERR,
+ "generated Diffie-Hellman key pair (type %d-%d) for '%s'",
+ keylen, algtype, netname);
+ } else {
+ free(secret);
+ return (NULL);
+ }
+ }
+ if (xencrypt_g(secret, keylen, algtype, newpass, netname,
+ &reencrypted_secret, TRUE) == 0) {
+ syslog(LOG_ERR, "could not encrypt keytype %d-%d",
+ keylen, algtype);
+ free(secret);
+ free(tmppk);
+ return (NULL);
+ }
+
+ free(secret);
+
+ if (generatekeys == TRUE && *keyset == TRUE)
+ *public = tmppk;
+ return (reencrypted_secret);
+}
+
+/*
+ * reencrypt the secret key (or generate a new key-pair)
+ * and update the cred table (for 1 key of type 'authtype').
+ */
+int
+__npd_upd_cred(char *user, /* user name */
+ char *domain, /* domainname */
+ char *authtype,
+ keylen_t keylen,
+ algtype_t algtype,
+ char *oldpass,
+ char *newpass,
+ int *err)
+{
+ struct nis_result *cred_res, *mod_res;
+ nis_object *cobj, *eobj;
+ char prin[NIS_MAXNAMELEN];
+ char netname[MAXNETNAMELEN];
+ char *oldcryptsecret = NULL;
+ char *newcryptsecret = NULL;
+ char *public = NULL;
+ entry_col ecol[5];
+ char buf[NIS_MAXNAMELEN];
+ int status;
+ bool_t keyset = FALSE;
+ entry_col * eobj_col;
+ uint_t eobj_col_len;
+ char short_opass[sizeof (des_block)+1], *short_opassp = NULL;
+ char short_npass[sizeof (des_block)+1], *short_npassp = NULL;
+
+ if ((user == NULL || *user == '\0') ||
+ (domain == NULL || *domain == '\0') || authtype == NULL) {
+ *err = NPD_KEYNOTREENC;
+ return (NIS_SYSTEMERROR);
+ }
+ /* "." + "." + null = 3 */
+ if ((strlen(user) + strlen(domain) + 3) > (size_t)NIS_MAXNAMELEN) {
+ *err = NPD_KEYNOTREENC;
+ return (NIS_SYSTEMERROR);
+ }
+ (void) sprintf(prin, "%s.%s", user, domain);
+ if (prin[strlen(prin) - 1] != '.')
+ (void) strcat(prin, ".");
+
+ if (__npd_prin2netname(prin, netname) == FALSE) {
+ *err = NPD_KEYNOTREENC;
+ return (NIS_SYSTEMERROR);
+ }
+
+ cred_res = nis_getcredent(prin, domain, authtype);
+ if (cred_res == NULL) {
+ *err = NPD_KEYNOTREENC;
+ return (NIS_SYSTEMERROR);
+ }
+ status = cred_res->status;
+ switch (status) {
+ case NIS_SUCCESS:
+ if (verbose == TRUE)
+ syslog(LOG_ERR, "found a %s cred entry for %s",
+ authtype, prin);
+ cobj = NIS_RES_OBJECT(cred_res);
+ if (cobj == NULL) {
+ *err = NPD_KEYNOTREENC;
+ break;
+ }
+
+ oldcryptsecret = ENTRY_VAL(cobj, 4);
+
+ if (oldpass) {
+ strlcpy(short_opass, oldpass, sizeof (short_opass));
+ short_opassp = short_opass;
+ }
+
+ if (newpass) {
+ strlcpy(short_npass, newpass, sizeof (short_npass));
+ short_npassp = short_npass;
+ }
+
+ if (!(newcryptsecret = reencrypt_secret(oldcryptsecret,
+ short_opassp, short_npassp,
+ keylen, algtype,
+ &public, &keyset, netname))) {
+ *err = NPD_KEYNOTREENC;
+ (void) nis_freeresult(cred_res);
+ return (NIS_SYSTEMERROR);
+ }
+
+ memset((char *)ecol, 0, sizeof (ecol));
+
+ if (keyset == TRUE && generatekeys == TRUE) {
+ /* public key changed too */
+ ecol[3].ec_value.ec_value_val = public;
+ ecol[3].ec_value.ec_value_len = strlen(public) + 1;
+ ecol[3].ec_flags = EN_MODIFIED;
+ }
+ ecol[4].ec_value.ec_value_val = newcryptsecret;
+ ecol[4].ec_value.ec_value_len = strlen(newcryptsecret) + 1;
+ ecol[4].ec_flags = EN_MODIFIED|EN_CRYPT;
+
+ /* strlen("[cname=,auth_type=],cred.") + null + "." = 27 */
+ if ((strlen(prin) + strlen(cobj->zo_domain) + strlen(authtype)
+ + 27) > (size_t)NIS_MAXNAMELEN) {
+ *err = NPD_KEYNOTREENC;
+ break;
+ }
+ (void) sprintf(buf, "[cname=%s,auth_type=%s],cred.%s",
+ prin, authtype, cobj->zo_domain);
+ if (buf[strlen(buf) - 1] != '.')
+ (void) strcat(buf, ".");
+
+ eobj = nis_clone_object(cobj, NULL);
+ if (eobj == NULL) {
+ *err = NPD_KEYNOTREENC;
+ break;
+ }
+
+ eobj_col = eobj->EN_data.en_cols.en_cols_val;
+ eobj_col_len = eobj->EN_data.en_cols.en_cols_len;
+
+ eobj->EN_data.en_cols.en_cols_val = ecol;
+ eobj->EN_data.en_cols.en_cols_len = 5;
+
+ mod_res = nis_modify_entry(buf, eobj, 0);
+ status = mod_res->status;
+ if (status != NIS_SUCCESS)
+ *err = NPD_KEYNOTREENC;
+ else
+ if (verbose)
+ syslog(LOG_ERR,
+ "Diffie-Hellman key (type %d-%d) reencrypted for '%s'",
+ keylen, algtype, prin);
+
+ /* restore ecol so that we can free eobj */
+ eobj->EN_data.en_cols.en_cols_val = eobj_col;
+ eobj->EN_data.en_cols.en_cols_len = eobj_col_len;
+ (void) nis_destroy_object(eobj);
+ (void) nis_freeresult(mod_res);
+ break;
+
+ case NIS_NOTFOUND:
+ if (verbose == TRUE)
+ syslog(LOG_ERR, "no %s cred for %s", authtype, prin);
+ if ((generatekeys == TRUE) &&
+ (__npd_addcredent(prin, domain, authtype,
+ keylen, algtype, short_npassp) == TRUE)) {
+ *err = NPD_KEYSUPDATED;
+ status = NIS_SUCCESS;
+ } else {
+ *err = NPD_KEYNOTREENC;
+ status = NIS_SYSTEMERROR;
+ }
+ break;
+ default:
+ *err = NPD_KEYNOTREENC;
+ syslog(LOG_ERR,
+ "NIS+ error (%d) getting cred entry for %s",
+ status, prin);
+ break;
+ }
+
+ free(newcryptsecret);
+ free(public);
+
+ (void) nis_freeresult(cred_res);
+ return (status);
+}
+
+/*
+ * Loop thru configed DH mechs and if the cred entry exists
+ * and can be decrypted with old pw, then reencrypt it. If
+ * it cannot be decrypted or does not exist, and the generatekeys
+ * option is set, then create a new DH key pair.
+ *
+ * If any of the key possibilities are reencrypted or generated successfully,
+ * then return NIS_SUCCESS (and set 'err' to NIS_SUCCESS).
+ *
+ * On failure, return the NIS error code and set 'err' to the NPD error code.
+ */
+int
+__npd_upd_all_pk_creds(char *user, /* in */
+ char *domain, /* in */
+ char *oldpass, /* in */
+ char *newpass, /* in */
+ int *err) /* out */
+{
+ int ret;
+ int localerr;
+ int key_success = 0;
+ mechanism_t **mechs;
+ mechanism_t **mpp;
+
+ if ((user == NULL || *user == '\0') ||
+ (domain == NULL || *domain == '\0')) {
+ *err = NPD_KEYNOTREENC;
+ return (NIS_SYSTEMERROR);
+ }
+ /* "." + "." + null = 3 */
+ if ((strlen(user) + strlen(domain) + 3) > (size_t)NIS_MAXNAMELEN) {
+ *err = NPD_KEYNOTREENC;
+ return (NIS_SYSTEMERROR);
+ }
+
+ if (debug)
+ syslog(LOG_DEBUG, "__npd_upd_all_pk_creds: user = %s",
+ user);
+ if (mechs = __nis_get_mechanisms(FALSE)) {
+ /*
+ * Try all the mechs listed in the NIS+ security cf (or until
+ * AUTH_DES (192bit DH keys) entry is reached).
+ */
+ for (mpp = mechs; *mpp; mpp++) {
+ mechanism_t *mp = *mpp;
+ char auth_type[MECH_MAXATNAME+1];
+
+ if (AUTH_DES_COMPAT_CHK(mp)) {
+ __nis_release_mechanisms(mechs);
+ goto try_auth_des;
+ }
+
+ if (!VALID_MECH_ENTRY(mp))
+ continue;
+
+ if (!__nis_mechalias2authtype(mp->alias, auth_type,
+ sizeof (auth_type))) {
+ syslog(LOG_ERR,
+ "cannot convert mech alias '%s' to auth type",
+ mp->alias);
+ continue;
+ }
+
+ ret = __npd_upd_cred(user, domain, auth_type,
+ mp->keylen, mp->algtype,
+ oldpass, newpass, &localerr);
+ if (ret == NIS_SUCCESS) {
+ key_success++;
+ } else {
+ syslog(LOG_ERR,
+ "cannot reencrypt ('%s') creds for '%s'",
+ auth_type, user);
+ if (debug)
+ syslog(LOG_DEBUG,
+ "__npd_upd_all_pk_creds: upd_cred fail; return = %d, localerr = %d",
+ ret, localerr);
+ }
+ }
+ __nis_release_mechanisms(mechs);
+
+ } else {
+ if (debug)
+ syslog(LOG_DEBUG,
+"__npd_upd_all_pk_creds: no valid GSS mechs found...trying AUTH_DES...");
+
+try_auth_des:
+ ret = __npd_upd_cred(user, domain, AUTH_DES_AUTH_TYPE,
+ AUTH_DES_KEYLEN, AUTH_DES_ALGTYPE,
+ oldpass, newpass, &localerr);
+ if (ret == NIS_SUCCESS) {
+ key_success++;
+ } else {
+ syslog(LOG_ERR,
+ "cannot reencrypt ('%s') creds for '%s'",
+ AUTH_DES_AUTH_TYPE, user);
+ if (debug)
+ syslog(LOG_DEBUG,
+ "__npd_upd_all_pk_creds: upd_cred des fail; ret = %d, localerr = %d",
+ ret, localerr);
+ }
+ }
+
+ /*
+ * Return success if any of the key possibilities were
+ * reencrypted or generated.
+ */
+ if (key_success > 0) {
+ *err = NIS_SUCCESS;
+ return (NIS_SUCCESS);
+ }
+
+ *err = NPD_KEYNOTREENC;
+ return (NIS_SYSTEMERROR);
+}
+
+/*
+ * encrypt new passwd
+ */
+char *
+__npd_encryptpass(char *pass, nis_object *user)
+{
+ char *newpass;
+ char *salt;
+ struct passwd *pw;
+ if (pass == NULL || *pass == '\0')
+ return (NULL);
+
+ /*
+ * We can't call getpwnam_r in here because the search paths
+ * for the machine running rpc.nispasswdd may not actually find
+ * the user. It is for this reason we got the nis_result passed
+ * in - the output of nis_list(). We need to turn that into a
+ * struct passwd before passing it to crypt_gensalt(3c).
+ */
+ pw = nis_object2passwd(user);
+ salt = crypt_gensalt(pass, pw);
+ newpass = crypt(pass, salt);
+ if (salt != NULL) {
+ free(salt);
+ salt = NULL;
+ }
+ if (pw != NULL) {
+ free(pw);
+ pw = NULL;
+ }
+ return (newpass);
+}
+
+
+/*
+ * find the passwd object for this user from the list
+ * of dirs given. If object is found in more than one
+ * place return an error, otherwise clone the object.
+ * Note, the object should be freed using nis_destroy_object().
+ */
+bool_t
+__npd_find_obj(user, dirlist, obj)
+char *user;
+char *dirlist;
+nis_object **obj; /* returned */
+{
+ char *tmplist;
+ char *curdir, *end = NULL;
+ char buf[NIS_MAXNAMELEN];
+ nis_result *tmpres;
+ nis_object *tmpobj = NULL;
+
+ if ((user == NULL || *user == '\0') ||
+ (dirlist == NULL || *dirlist == '\0'))
+ return (FALSE);
+
+ *obj = NULL;
+ if ((tmplist = strdup(dirlist)) == NULL) {
+ syslog(LOG_CRIT, "out of memory");
+ return (FALSE);
+ }
+ for (curdir = tmplist; curdir != NULL; curdir = end) {
+ end = strchr(curdir, ' ');
+ if (end != NULL)
+ *end++ = NULL;
+ if (strncasecmp(curdir, "org_dir", 7) != 0)
+ continue; /* not an org_dir */
+
+ /* strlen("[name=],passwd." + null + "." = 17 */
+ if ((strlen(user) + 17 + strlen(curdir)) >
+ (size_t)NIS_MAXNAMELEN) {
+ (void) free(tmplist);
+ return (FALSE);
+ }
+
+ (void) sprintf(buf, "[name=%s],passwd.%s", user, curdir);
+ if (buf[strlen(buf) - 1] != '.')
+ (void) strcat(buf, ".");
+ tmpres = nis_list(buf, USE_DGRAM+MASTER_ONLY, NULL, NULL);
+ switch (tmpres->status) {
+ case NIS_NOTFOUND: /* skip */
+ (void) nis_freeresult(tmpres);
+ continue;
+
+ case NIS_SUCCESS:
+ if (NIS_RES_NUMOBJ(tmpres) != 1) {
+ /* should only have one entry */
+ (void) nis_freeresult(tmpres);
+ (void) free(tmplist);
+ if (tmpobj != NULL)
+ (void) nis_destroy_object(tmpobj);
+ return (FALSE);
+ }
+ if (tmpobj != NULL) {
+ /* found in more than one dir */
+ (void) nis_destroy_object(tmpobj);
+ (void) nis_freeresult(tmpres);
+ (void) free(tmplist);
+ *obj = NULL;
+ return (FALSE);
+ }
+ tmpobj = nis_clone_object(NIS_RES_OBJECT(tmpres), NULL);
+ if (tmpobj == NULL) {
+ syslog(LOG_CRIT, "out of memory");
+ (void) nis_freeresult(tmpres);
+ (void) free(tmplist);
+ return (FALSE);
+ }
+ (void) nis_freeresult(tmpres);
+ continue;
+
+ default:
+ /* some NIS+ error - quit processing */
+ curdir = NULL;
+ break;
+ }
+ }
+ (void) nis_freeresult(tmpres);
+ (void) free(tmplist);
+ if (tmpobj == NULL) /* no object found */
+ return (FALSE);
+ *obj = tmpobj;
+ return (TRUE);
+}
+
+/*
+ * go thru' the list of dirs and see if the host is a
+ * master server for any 'org_dir'.
+ */
+bool_t
+__npd_am_master(host, dirlist)
+char *host;
+char *dirlist;
+{
+ char *tmplist;
+ char *curdir, *end = NULL;
+ nis_server **srvs;
+ unsigned sleep_time;
+ unsigned total_sleep_time;
+ unsigned next_log_time;
+
+ if ((host == NULL || *host == '\0') ||
+ (dirlist == NULL || *dirlist == '\0'))
+ return (FALSE);
+
+ if ((tmplist = strdup(dirlist)) == NULL) {
+ syslog(LOG_CRIT, "out of memory");
+ return (FALSE);
+ }
+ for (curdir = tmplist; curdir != NULL; curdir = end) {
+ end = strchr(curdir, ' ');
+ if (end != NULL)
+ *end++ = NULL;
+ if (strncasecmp(curdir, "org_dir", 7) != 0)
+ continue; /* not an org_dir */
+
+ /*
+ * Attempt to get the nis serv list
+ * If no success after an hour, exit
+ */
+ sleep_time = 1;
+ total_sleep_time = 0;
+ next_log_time = 60;
+ while ((srvs = nis_getservlist(curdir)) == NULL) {
+ if (total_sleep_time >= 3600)
+ break; /* give up after an hour */
+ /*
+ * Log errors at 1 minute, 10 minutes, 20 minutes,
+ * 30 minutes, 40 minutes, and 50 minutes
+ */
+ if (total_sleep_time >= next_log_time) {
+ syslog(LOG_ERR,
+ "cannot get a list of servers that serve '%s'. Retrying...",
+ curdir);
+ if (next_log_time == 60)
+ next_log_time = 600;
+ else
+ next_log_time += 600;
+ }
+ (void) sleep(sleep_time);
+ __nis_CacheRestart();
+ total_sleep_time += sleep_time;
+ /*
+ * sleep times of 1,2,4,8,16,29 seconds (total 60)
+ * and once a minute thereafter
+ */
+ if (sleep_time <= 8)
+ sleep_time += sleep_time;
+ else if (sleep_time <= 16)
+ sleep_time = 29;
+ else
+ sleep_time = 60;
+ }
+
+ if (srvs == NULL) {
+ syslog(LOG_ERR,
+ "cannot get a list of servers that serve '%s'", curdir);
+ (void) free(tmplist);
+ return (FALSE);
+ }
+ if (strcasecmp(host, srvs[0]->name) == 0) {
+ (void) free(tmplist);
+ (void) nis_freeservlist(srvs);
+ return (TRUE);
+ }
+ (void) nis_freeservlist(srvs);
+ }
+ (void) free(tmplist);
+ return (FALSE);
+}
+
+
+/*
+ * check whether this principal has permission to
+ * add/delete/modify an entry object
+ */
+bool_t
+__npd_can_do(right, obj, prin, column)
+unsigned long right; /* access right seeked */
+nis_object *obj; /* entry object */
+char *prin; /* principal seeking access */
+int column; /* column being modified */
+{
+ nis_result *res;
+ nis_object *tobj;
+ table_col *tc;
+ char buf[NIS_MAXNAMELEN];
+ int mod_ok;
+
+ /* strlen("passwd." + null + "." = 9 */
+ if ((9 + strlen(obj->zo_domain)) > (size_t)NIS_MAXNAMELEN) {
+ return (FALSE);
+ }
+ (void) sprintf(buf, "passwd.%s", obj->zo_domain);
+ if (buf[strlen(buf) - 1] != '.')
+ (void) strcat(buf, ".");
+
+ res = nis_lookup(buf, MASTER_ONLY);
+
+ if (res->status == NIS_SUCCESS) {
+ tobj = NIS_RES_OBJECT(res);
+ if (__type_of(tobj) != NIS_TABLE_OBJ) {
+ (void) nis_freeresult(res);
+ return (FALSE);
+ }
+ } else {
+ (void) nis_freeresult(res);
+ return (FALSE);
+ }
+
+ /* check the permission on the table */
+ mod_ok = __nis_ck_perms(right, tobj->zo_access, tobj, prin, 1);
+ if (mod_ok == TRUE) {
+ (void) nis_freeresult(res);
+ return (TRUE);
+ }
+
+ /* check the permission on the obj */
+ mod_ok = __nis_ck_perms(right, obj->zo_access, obj, prin, 1);
+ if (mod_ok == TRUE) {
+ (void) nis_freeresult(res);
+ return (TRUE);
+ }
+
+ if (column > tobj->TA_data.ta_maxcol) {
+ /* invalid column */
+ (void) nis_freeresult(res);
+ return (FALSE);
+ }
+ tc = tobj->TA_data.ta_cols.ta_cols_val; /* table columns */
+
+ /* check the permission on column being modified */
+ mod_ok = __nis_ck_perms(right, tc[column].tc_rights, obj, prin, 1);
+
+ (void) nis_freeresult(res);
+ return (mod_ok);
+}
+
+void
+__npd_gen_rval(randval)
+unsigned long *randval;
+{
+ int i, shift;
+ struct timeval tv;
+ unsigned int seed = 0;
+
+ for (i = 0; i < 1024; i++) {
+ (void) gettimeofday(&tv, NULL);
+ shift = i % 8 * sizeof (int);
+ seed ^= (tv.tv_usec << shift) | (tv.tv_usec >> (32 - shift));
+ }
+ (void) srandom(seed);
+ *randval = (ulong_t)random();
+}
+
+/*
+ * The following code is a modified version of nis_object2ent which was
+ * taken from usr/src/lib/nsswitch/nisplus/common/getpwnam.c.
+ * If you make fixes here you should check if they are also valid for the
+ * nss_nisplus.so version.
+ *
+ * The code had to be changed since the original was written for use inside
+ * the nsswitch code which wasn't suitable for direct inclusion here.
+ */
+static struct passwd *
+nis_object2passwd(nis_object *obj)
+{
+ char *val, *endnum, *nullstring;
+ struct passwd *pw;
+ struct entry_col *ecol;
+ int len;
+
+ nullstring = strdup("");
+ /*
+ * If we got more than one nis_object, we just ignore object(s)
+ * except the first. Although it should never have happened.
+ *
+ * ASSUMPTION: All the columns in the NIS+ tables are
+ * null terminated.
+ */
+
+ if (obj->zo_data.zo_type != NIS_ENTRY_OBJ ||
+ obj->EN_data.en_cols.en_cols_len < PW_COL) {
+ /* namespace/table/object is curdled */
+ return (NULL);
+ }
+ ecol = obj->EN_data.en_cols.en_cols_val;
+
+ pw = malloc(sizeof (struct passwd));
+
+ /*
+ * pw_name: user name
+ */
+ EC_SET(ecol, PW_NDX_NAME, len, val);
+ if (len < 2 || (*val == '\0'))
+ return (NULL);
+ pw->pw_name = strdup(val);
+
+ /*
+ * pw_uid: user id
+ */
+ EC_SET(ecol, PW_NDX_UID, len, val);
+ if (len < 2) {
+ return (NULL);
+ } else {
+ pw->pw_uid = strtol(val, &endnum, 10);
+ if (*endnum != 0 || val == endnum) {
+ return (NULL);
+ }
+ }
+
+ /*
+ * pw_passwd: user passwd. Do not HAVE to get this here
+ * because the caller would do a getspnam() anyway.
+ */
+ EC_SET(ecol, PW_NDX_PASSWD, len, val);
+ if (len < 2) {
+ /*
+ * don't return NULL pointer, lot of stupid programs
+ * out there.
+ */
+ pw->pw_passwd = nullstring;
+ } else {
+ pw->pw_passwd = strdup(val);
+ }
+
+ /*
+ * pw_gid: user's primary group id.
+ */
+ EC_SET(ecol, PW_NDX_GID, len, val);
+ if (len < 2) {
+ return (NULL);
+ } else {
+ pw->pw_gid = strtol(val, &endnum, 10);
+ if (*endnum != 0 || val == endnum) {
+ return (NULL);
+ }
+ }
+
+ /*
+ * pw_gecos: user's real name.
+ */
+ EC_SET(ecol, PW_NDX_GCOS, len, val);
+ if (len < 2) {
+ /*
+ * don't return NULL pointer, lot of stupid programs
+ * out there.
+ */
+ pw->pw_gecos = nullstring;
+ } else {
+ pw->pw_gecos = strdup(val);
+ }
+
+ /*
+ * pw_dir: user's home directory
+ */
+ EC_SET(ecol, PW_NDX_HOME, len, val);
+ if (len < 2) {
+ /*
+ * don't return NULL pointer, lot of stupid programs
+ * out there.
+ */
+ pw->pw_dir = nullstring;
+ } else {
+ pw->pw_dir = strdup(val);
+ }
+
+ /*
+ * pw_shell: user's login shell
+ */
+ EC_SET(ecol, PW_NDX_SHELL, len, val);
+ if (len < 2) {
+ /*
+ * don't return NULL pointer, lot of stupid programs
+ * out there.
+ */
+ pw->pw_shell = nullstring;
+ } else {
+ pw->pw_shell = strdup(val);
+ }
+
+ /*
+ * pw_age and pw_comment shouldn't be used anymore, but various things
+ * (allegedly in.ftpd) merrily do strlen() on them anyway, so we
+ * keep the peace by returning a zero-length string instead of a
+ * null pointer.
+ */
+ pw->pw_age = pw->pw_comment = nullstring;
+ if (debug)
+ syslog(LOG_DEBUG, "nis_object2passwd:"
+ "pw->pw_name = %s pw->pw_uid = %d pw->pw_gid = %d "
+ "pw->pw_gecos = %s pw->pw_dir = %s pw->pw_shell = %s ",
+ pw->pw_name, pw->pw_uid, pw->pw_gid, pw->pw_gecos, pw->pw_dir,
+ pw->pw_shell);
+
+ return (pw);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_svcsubr.h b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_svcsubr.h
new file mode 100644
index 0000000000..a3f7158e38
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_svcsubr.h
@@ -0,0 +1,79 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 1994-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ */
+
+#ifndef _NPD_SVCSUBR_H
+#define _NPD_SVCSUBR_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <rpcsvc/yppasswd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct nis_result *nis_getpwdent(char *user, char *domain);
+int __npd_upd_all_pk_creds(char *user, char *domain, char *oldpass,
+ char *newpass, int *err);
+void __npd_gen_rval(unsigned long *randval);
+bool_t __npd_has_aged(struct nis_object *obj, int *res);
+bool_t __authenticate_admin(char *prin, char *pass);
+char *__npd_encryptpass(char *pass, nis_object *user);
+bool_t __npd_can_do(unsigned long right, nis_object *obj, char *prin,
+ int column);
+bool_t __npd_find_obj(char *user, char *dirlist, nis_object **obj);
+int update_authtok_nis_fwd(char *usrname, char *newpwe,
+ char *oldpwu, char *master, char *gecos, char *shell);
+bool_t __npd_prin2netname(char *, char []);
+bool_t __npd_am_master(char *host, char *dirlist);
+
+bool_t nispasswd_authenticate_1_svc(npd_request *argp,
+ nispasswd_authresult *result, struct svc_req *rqstp);
+bool_t nispasswd_update_1_svc(npd_update *updreq, nispasswd_updresult *res,
+ struct svc_req *rqstp);
+bool_t yppasswdproc_update_1_svc(struct yppasswd *yppass, int *result,
+ struct svc_req *rqstp);
+
+/* from libnsl */
+extern bool_t __nis_ismaster(char *host, char *domain);
+extern bool_t __nis_isadmin(char *princ, char *table, char *domain);
+extern bool_t __nis_ck_perms(unsigned int right, unsigned int mask,
+ nis_object *obj, nis_name pr, int level);
+extern nis_server *__nis_host2nis_server(char *host, bool_t addpubkey,
+ int *errcode);
+extern bool_t __npd_ecb_crypt(uint32_t *val1, uint32_t *val2, des_block *buf,
+ unsigned int bufsize, unsigned int mode, des_block *deskey);
+extern bool_t __npd_cbc_crypt(uint32_t *val, char *str, unsigned int strsize,
+ npd_newpass *buf, unsigned int bufsize, unsigned int mode,
+ des_block *deskey);
+extern void __free_nis_server(nis_server *server);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _NPD_SVCSUBR_H */
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_ypfwd.c b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_ypfwd.c
new file mode 100644
index 0000000000..04c63e0837
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/npd_ypfwd.c
@@ -0,0 +1,117 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+#include <syslog.h>
+#include <rpc/rpc.h>
+#include <rpc/clnt.h>
+#include <rpcsvc/yppasswd.h>
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * npd_ypfwd.c
+ * NPD routine to forward password update request to YP
+ *
+ * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * This function is pretty much lifted from
+ * lib/scheme/pm_scheme/pam_update_authtok_nis.c:update_authtok_nis().
+ *
+ * The only difference is that we are taking requests from NPD to
+ * forward a password change to a YP database after the NIS+ database
+ * has already been updated. If this change fails, then we must undo
+ * the NIS+ change as well. Only at Sun can something like this exist...
+ *
+ * This function is called only from npd_svc.c:nispasswd_update_1_svc()
+ * or npd_svc.c:yppasswd_update_1_svc(). The set of variables are similar
+ * so I've provided a translation table below:
+ *
+ * variable nispasswd yppasswd what is it?
+ * -------- --------- -------- -----------
+ * usrname entry->ul_user newpass->pw_name user name
+ * newpwe newpass newpass->pw_passwd encr new passwd
+ * XX oldpwe old_pass old_pass encr old passwd
+ * oldpwu entry->ol_oldpass yppass->oldpass clear old passwd
+ * XX newpwu pass <N/A> clear new passwd
+ * master ypfwd ypfwd YP master to fwd
+ * gecos old_gecos old_gecos original gecos
+ * shell old_shell old_shell original shell
+ */
+int
+update_authtok_nis_fwd(
+ char *usrname, /* user name */
+ char *newpwe, /* encrypted new passwd */
+ char *oldpwu, /* clear old passwd */
+ char *master, /* passwd master YP machine */
+ char *gecos, /* (unchanged) general comments */
+ char *shell) /* (unchanged) login shell */
+{
+ int retval = 0; /* value to return */
+ int ok; /* update return status */
+ enum clnt_stat ans; /* RPC return status */
+ CLIENT *client; /* RPC client handle */
+ const char *fnam = "update_authok_nis_fwd";
+ /* function name */
+ const struct timeval timeout = { 55, 0 };
+ /* NPD uses 55 seconds */
+ static struct yppasswd yppwstruct; /* YP passwd struct */
+ static struct passwd pwstruct; /* passwd struct */
+
+ /*
+ * ck_passwd() already checked the old passwd. It won't get here
+ * if the old passwd is not matched. We are just preparing the
+ * yppasswd update packet here.
+ */
+ pwstruct.pw_name = usrname; /* username is changing passwd */
+ pwstruct.pw_passwd = newpwe; /* encrypted new passwd */
+ pwstruct.pw_gecos = gecos; /* unchanged general comments */
+ pwstruct.pw_shell = shell; /* unchanged login shell */
+ yppwstruct.oldpass = oldpwu; /* unencrypted old passwd */
+ yppwstruct.newpw = pwstruct; /* copy in yppasswd struct */
+
+ if (!(client = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS,
+ "udp"))) {
+ syslog(LOG_INFO, "%s: can't create YP client handle\n", fnam);
+ return (-1);
+ }
+
+ ans = CLNT_CALL(client, YPPASSWDPROC_UPDATE, xdr_yppasswd,
+ (char *)&yppwstruct, xdr_int, (char *)&ok, timeout);
+
+ if ((ok != 0) || (ans != RPC_SUCCESS)) {
+ syslog(LOG_ERR,
+ "%s: can't change NIS(YP) passwd for %s on %s (err: %d)\n",
+ fnam, usrname, master, ok);
+ if (ans != RPC_SUCCESS) {
+ clnt_perror(client,
+ "RPC call failed -- client may have timed-out.");
+ syslog(LOG_INFO,
+ "%s: client could not make RPC call.\n", fnam);
+ retval = -1;
+ }
+ } else {
+ syslog(LOG_DEBUG, "%s: NIS(YP) passwd changed for %s on %s\n",
+ fnam, usrname, master);
+ }
+
+ (void) clnt_destroy(client);
+ return (retval);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/req.flg b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/req.flg
new file mode 100644
index 0000000000..b3b75227c4
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/req.flg
@@ -0,0 +1,28 @@
+#!/bin/sh
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+
+
+echo_file usr/src/lib/libnsl/include/rpcsvc/nis_dhext.h
diff --git a/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/rpc.nispasswdd.c b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/rpc.nispasswdd.c
new file mode 100644
index 0000000000..92f950d0ab
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/rpc.nispasswdd.c
@@ -0,0 +1,553 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 1994-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h> /* getenv, exit */
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <memory.h>
+#include <syslog.h>
+#include <stropts.h>
+#include <netdir.h>
+#include <synch.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/nis.h>
+#include <rpcsvc/yppasswd.h>
+#include <rpcsvc/nispasswd.h>
+#include <rpcsvc/nis_dhext.h>
+#include "npd_svcsubr.h"
+
+int verbose = 0;
+bool_t debug = FALSE;
+/*
+ * generate keys if none exist or if admin. is changing another
+ * users password.
+ */
+bool_t generatekeys = FALSE;
+ulong_t cache_time = 1800; /* cache failed attempts for 30 mins */
+int max_attempts = 3; /* max # of failed attempts allowed */
+NIS_HASH_TABLE upd_list; /* cache of updates */
+char *ypfwd = (char *)NULL; /* passwd map YP master machine */
+
+static void __msgout(const char *msg);
+static int __svc_priv_port_create(void);
+static void nispasswd_prog(struct svc_req *rqstp, register SVCXPRT *transp);
+static void yppasswd_prog(struct svc_req *rqstp, register SVCXPRT *transp);
+static void set_rpc_gss_svc_names(void);
+
+
+void
+main(argc, argv)
+int argc;
+char **argv;
+{
+ pid_t pid;
+ int i;
+ int c;
+ int mins;
+ int status;
+ nis_tag tags[2], *tagres;
+ nis_server *srv;
+ int connmaxrec = RPC_MAXDATASIZE;
+
+ (void) memset((char *)&upd_list, 0, sizeof (upd_list));
+ (void) chdir("/var/nis"); /* drop core here */
+ while ((c = getopt(argc, argv, "a:c:DgvY:")) != -1) {
+ switch (c) {
+ case 'a':
+ max_attempts = atoi(optarg);
+ if (max_attempts < 0) {
+ fprintf(stderr,
+ "%s: invalid number of maximum attempts\n",
+ argv[0]);
+ exit(1);
+ }
+ break;
+ case 'c':
+ mins = atoi(optarg);
+ if (mins <= 0) {
+ fprintf(stderr,
+ "%s: invalid cache time\n", argv[0]);
+ exit(1);
+ }
+ cache_time = (ulong_t)mins * 60;
+ break;
+ case 'D': /* debug mode */
+ debug = TRUE;
+ break;
+ case 'g': /* generate new keys if none exist */
+ generatekeys = TRUE;
+ break;
+ case 'v': /* verbose mode */
+ verbose++;
+ break;
+ case 'Y': /* YP forward mode */
+ if (!(ypfwd = strdup(optarg))) {
+ fprintf(stderr, "%s: out of memory\n", argv[0]);
+ exit(1);
+ }
+ break;
+ case '?':
+ fprintf(stderr,
+ "Usage: %s [-a attempts] [-c minutes] [-D] [-g] [-v]\n",
+ argv[0]);
+ exit(1);
+ }
+ }
+
+ /*
+ * this check should be removed if 'root' is not the
+ * owner of the table or if 'root' is not a member of
+ * the nis+ admins. group.
+ * of course: you also don't want someone to spoof the
+ * the daemon.
+ */
+ if (geteuid() != (uid_t)0) {
+ char err_string[256];
+ snprintf(err_string, sizeof (err_string),
+ "must be superuser to run %s", argv[0]);
+ __msgout(err_string);
+ exit(1);
+ }
+
+ if (debug == FALSE) {
+ pid = fork();
+ if (pid < 0) {
+ perror("cannot fork");
+ exit(1);
+ }
+ if (pid)
+ exit(0);
+ closefrom(0);
+ i = open("/dev/console", 2);
+ (void) dup2(i, 1);
+ (void) dup2(i, 2);
+ (void) setsid();
+ }
+ openlog("rpc.nispasswdd", LOG_PID, LOG_DAEMON);
+
+ /*
+ * should I be running ?
+ * Get the list of directories the local NIS+ server serves and
+ * check if it is the master server of any 'org_dir' listed.
+ */
+ srv = __nis_host2nis_server(NULL, 0, &status);
+ if (srv == NULL) {
+ if (debug == TRUE)
+ (void) fprintf(stderr,
+ "no host/address information for local host, %d\n",
+ status);
+ else
+ syslog(LOG_ERR,
+ "no host/address information for local host, %d",
+ status);
+ __msgout("exiting ...");
+ exit(1);
+ }
+ tags[0].tag_type = TAG_DIRLIST;
+ tags[0].tag_val = "";
+ tags[1].tag_type = TAG_NISCOMPAT;
+ tags[1].tag_val = "";
+
+ status = nis_stats(srv, tags, 2, &tagres);
+ __free_nis_server(srv);
+ if (status != NIS_SUCCESS) {
+ if (verbose)
+ nis_perror(status, "rpc.nispasswdd");
+ else
+ nis_lerror(status, "rpc.nispasswdd");
+ __msgout(" ... exiting ...");
+ exit(1);
+ }
+ if ((strcmp(tagres[0].tag_val, "<Unknown Statistics>") == 0) ||
+ (strcmp(tagres[1].tag_val, "<Unknown Statistics>") == 0)) {
+
+ /* old server */
+ __msgout("NIS+ server does not support the new statistics tags");
+ exit(1);
+ }
+
+ if (__npd_am_master(nis_local_host(), tagres[0].tag_val) == FALSE) {
+ __msgout("Local NIS+ server is not a master server");
+ __msgout(" ... exiting ...");
+ exit(1);
+ }
+
+ /* Specify maximum connection oriented RPC record size */
+ if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &connmaxrec)) {
+ __msgout("unable to set maximum RPC record size");
+ }
+
+ /*
+ * Check if NIS+ server is running in NIS compat mode.
+ * Register YPD only if this is the case.
+ */
+ if (strcasecmp(tagres[1].tag_val, "ON") == 0) {
+ if (rpcb_unset(YPPASSWDPROG, YPPASSWDVERS, 0) == FALSE) {
+ __msgout("unable to de-register (YPPASSWDPROG, YPPASSWDVERS)");
+ __msgout(" ... exiting ...");
+ exit(1);
+ }
+ if (__svc_priv_port_create() == FALSE) {
+ __msgout("unable to create (YPPASSWDPROG, YPPASSWDVERS)");
+ /* continue */
+ }
+ }
+ nis_freetags(tagres, 2);
+
+ set_rpc_gss_svc_names();
+
+ if (rpcb_unset(NISPASSWD_PROG, NISPASSWD_VERS, 0) == FALSE) {
+ __msgout("unable to de-register (NISPASSWD_PROG, NISPASSWD_VERS)");
+ __msgout(" ... exiting ...");
+ exit(1);
+ }
+ if (svc_create(nispasswd_prog, NISPASSWD_PROG, NISPASSWD_VERS,
+ "circuit_v") == 0) {
+ __msgout("unable to create (NISPASSWD_PROG, NISPASSWD_VERS)");
+ __msgout(" ... exiting ...");
+ exit(1);
+ }
+
+ if (verbose == TRUE)
+ __msgout("starting rpc.nispasswdd ...");
+ svc_run();
+ __msgout("svc_run returned");
+ exit(1);
+
+ /* NOTREACHED */
+}
+
+static void
+__msgout(const char *msg)
+{
+ if (debug == TRUE)
+ (void) fprintf(stderr, "%s\n", msg);
+ else
+ syslog(LOG_ERR, msg);
+}
+
+static bool_t
+__svc_priv_port_create()
+{
+ struct netconfig *nconf, *nconf4 = 0, *nconf6 = 0;
+ void *handlep;
+ int fd4 = -1, fd6 = -1;
+ struct t_info tinfo4, tinfo6;
+ SVCXPRT *transp4 = 0, *transp6 = 0;
+ int svc4 = 0, svc6 = 0;
+
+ if ((handlep = setnetconfig()) == (void *) NULL) {
+ (void) nc_perror("cannot get any transport information");
+ return (FALSE);
+ }
+
+ while ((nconf = getnetconfig(handlep)) != 0 &&
+ (nconf4 == 0 || nconf6 == 0)) {
+ if ((nconf->nc_semantics == NC_TPI_CLTS) &&
+ strcmp(nconf->nc_proto, NC_UDP) == 0) {
+ if (strcmp(nconf->nc_protofmly, NC_INET) == 0)
+ nconf4 = nconf;
+ else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0)
+ nconf6 = nconf;
+ }
+ }
+
+ if (nconf4 == 0 && nconf6 == 0) {
+ (void) endnetconfig(handlep);
+ __msgout("no NC_INET/NC_INET6 NC_TPI_CLTS/NC_UPD transports");
+ return (FALSE);
+ }
+
+ if (nconf4 != 0)
+ fd4 = t_open(nconf4->nc_device, O_RDWR, &tinfo4);
+ if (nconf6 != 0)
+ fd6 = t_open(nconf6->nc_device, O_RDWR, &tinfo6);
+ if (fd4 == -1 && fd6 == -1) {
+ __msgout("unable to open connection for NIS requests");
+ (void) endnetconfig(handlep);
+ return (FALSE);
+ }
+
+ if (fd4 != -1) {
+ if (netdir_options(nconf4, ND_SET_RESERVEDPORT, fd4, 0) == -1) {
+ __msgout("unable to get reserved port for NC_INET");
+ netdir_perror("");
+ (void) endnetconfig(handlep);
+ return (FALSE);
+ }
+ transp4 = svc_tli_create(fd4, nconf4, NULL, 0, 0);
+ }
+
+ if (fd6 != -1) {
+ if (netdir_options(nconf6, ND_SET_RESERVEDPORT, fd6, 0) == -1) {
+ __msgout("unable to get reserved port for NC_INET6");
+ netdir_perror("");
+ (void) endnetconfig(handlep);
+ return (FALSE);
+ }
+ transp6 = svc_tli_create(fd6, nconf6, NULL, 0, 0);
+ }
+
+ if (transp4 == 0 && transp6 == 0) {
+ __msgout("unable to create (YPPASSWDPROG, YPPASSWDVERS)");
+ (void) endnetconfig(handlep);
+ return (FALSE);
+ }
+
+ if (transp4 != 0)
+ svc4 = svc_reg(transp4, YPPASSWDPROG, YPPASSWDVERS,
+ yppasswd_prog, nconf4);
+ if (transp6 != 0)
+ svc6 = svc_reg(transp6, YPPASSWDPROG, YPPASSWDVERS,
+ yppasswd_prog, nconf6);
+ if (svc4 == 0 && svc6 == 0) {
+ __msgout("unable to register (YPPASSWDPROG, YPPASSWDVERS)");
+ if (transp4 != 0)
+ (void) svc_destroy(transp4);
+ if (transp6 != 0)
+ (void) svc_destroy(transp6);
+ (void) endnetconfig(handlep);
+ return (FALSE);
+ }
+ (void) endnetconfig(handlep);
+ return (TRUE);
+}
+
+static void
+nispasswd_prog(rqstp, transp)
+struct svc_req *rqstp;
+register SVCXPRT *transp;
+{
+ union {
+ npd_request nispasswd_authenticate_1_arg;
+ npd_update nispasswd_update_1_arg;
+ } argument;
+ union {
+ nispasswd_authresult nispasswd_authenticate_1_res;
+ nispasswd_updresult nispasswd_update_1_res;
+ } result;
+ bool_t retval;
+ xdrproc_t xdr_argument, xdr_result;
+ bool_t (*local)(char *, void *, struct svc_req *);
+
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ __msgout("received NIS+ null proc call");
+ (void) svc_sendreply(transp,
+ (xdrproc_t)xdr_void, (char *)NULL);
+ return;
+
+ case NISPASSWD_AUTHENTICATE:
+ xdr_argument = (xdrproc_t)xdr_npd_request;
+ xdr_result = (xdrproc_t)xdr_nispasswd_authresult;
+ local = (bool_t (*) (char *, void *, struct svc_req *))
+ nispasswd_authenticate_1_svc;
+ break;
+
+ case NISPASSWD_UPDATE:
+ xdr_argument = (xdrproc_t)xdr_npd_update;
+ xdr_result = (xdrproc_t)xdr_nispasswd_updresult;
+ local = (bool_t (*) (char *, void *, struct svc_req *))
+ nispasswd_update_1_svc;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+ (void) memset((char *)&argument, 0, sizeof (argument));
+ if (svc_getargs(transp, xdr_argument, (caddr_t)&argument) == 0) {
+ svcerr_decode(transp);
+ return;
+ }
+ (void) memset((char *)&result, 0, sizeof (result));
+ retval = (bool_t)(*local)((char *)&argument, (void *)&result, rqstp);
+ if (retval == TRUE && (svc_sendreply(transp, xdr_result,
+ (char *)&result) == 0)) {
+ svcerr_systemerr(transp);
+ }
+ if (svc_freeargs(transp, xdr_argument, (caddr_t)&argument) == 0) {
+ __msgout("unable to free arguments");
+ exit(1);
+ }
+ if (nispasswd_prog_1_freeresult(transp, xdr_result,
+ (caddr_t)&result) == 0)
+ __msgout("unable to free results");
+
+ /* NOTREACHED */
+}
+
+static void
+yppasswd_prog(rqstp, transp)
+struct svc_req *rqstp;
+register SVCXPRT *transp;
+{
+ union {
+ struct yppasswd yppasswdproc_update_1_arg;
+ } argument;
+ union {
+ int yppasswdproc_update_1_res;
+ } result;
+ bool_t retval;
+ xdrproc_t xdr_argument, xdr_result;
+ bool_t (*local)(char *, void *, struct svc_req *);
+#ifdef SHD_WE_ALWAYS
+ struct netconfig *nconf;
+#endif
+
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ __msgout("received NIS null proc call");
+ (void) svc_sendreply(transp,
+ (xdrproc_t)xdr_void, (char *)NULL);
+ return;
+
+ case YPPASSWDPROC_UPDATE:
+ xdr_argument = (xdrproc_t)xdr_yppasswd;
+ xdr_result = (xdrproc_t)xdr_int;
+ local = (bool_t (*) (char *, void *, struct svc_req *))
+ yppasswdproc_update_1_svc;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+#ifdef SHD_WE_ALWAYS
+ /*
+ * update request received, lets just check the port
+ */
+ nconf = (struct netconfig *)getnetconfigent(transp->xp_netid);
+ if (nconf == (struct netconfig *)NULL) {
+ (void) nc_perror("could not get transport information");
+ svcerr_systemerr(transp);
+ return;
+ }
+
+ if ((strcmp(nconf->nc_protofmly, NC_INET) != 0) ||
+ (strcmp(nconf->nc_proto, NC_UDP) != 0) ||
+ (netdir_options(nconf, ND_CHECK_RESERVEDPORT, transp->xp_fd,
+ (void *) &(transp->xp_rtaddr)) != 0)) {
+
+ if (nconf)
+ (void) freenetconfigent(nconf);
+ svcerr_weakauth(transp);
+ return;
+ }
+ if (nconf)
+ (void) freenetconfigent(nconf);
+#endif
+ (void) memset((char *)&argument, 0, sizeof (argument));
+ if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) {
+ svcerr_decode(transp);
+ return;
+ }
+ (void) memset((char *)&result, 0, sizeof (result));
+ retval = (bool_t)(*local)((char *)&argument, (void *)&result, rqstp);
+ if (retval == TRUE && !svc_sendreply(transp, xdr_result,
+ (char *)&result)) {
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument)) {
+ __msgout("unable to free arguments");
+ exit(1);
+ }
+}
+
+/*
+ * 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
+set_rpc_gss_svc_names()
+{
+ mechanism_t **mechs;
+
+ if (mechs = __nis_get_mechanisms(FALSE)) {
+ mechanism_t **mpp;
+ /* "nispasswd" + 1 = 10 */
+ char svc_name[NIS_MAXNAMELEN+10];
+ int slen;
+ char *lh = nis_local_host();
+
+ if (! lh) {
+ syslog(LOG_ERR,
+ "can't set RPC GSS service name: can't get local host name");
+ __nis_release_mechanisms(mechs);
+ return;
+ }
+
+ /* '@' + NUL = 2 */
+ if (strlen(lh) + strlen(NIS_SVCNAME_NISPASSWD) + 2 >
+ sizeof (svc_name)) {
+ syslog(LOG_ERR,
+ "can't set RPC GSS service name: svc_name bufsize too small");
+ __nis_release_mechanisms(mechs);
+ return;
+ }
+ /* service names are of the form svc@server.dom */
+ (void) sprintf(svc_name, "%s@%s", NIS_SVCNAME_NISPASSWD, lh);
+ /* remove trailing '.' */
+ slen = strlen(svc_name);
+ if (svc_name[slen - 1] == '.')
+ svc_name[slen - 1] = '\0';
+
+ for (mpp = mechs; *mpp; mpp++) {
+ mechanism_t *mp = *mpp;
+
+ if (AUTH_DES_COMPAT_CHK(mp))
+ break;
+
+ if (! VALID_MECH_ENTRY(mp))
+ continue;
+
+ if (rpc_gss_set_svc_name(svc_name, mp->mechname,
+ 0, NISPASSWD_PROG,
+ NISPASSWD_VERS)) {
+ if (verbose)
+ syslog(LOG_INFO,
+ "RPC GSS service name for mechanism '%s' set",
+ mp->mechname);
+ } else {
+ rpc_gss_error_t err;
+
+ rpc_gss_get_error(&err);
+ syslog(LOG_ERR,
+"can't set RPC GSS svc name '%s' for mech '%s': RPC GSS err = %d, sys err = %d",
+ svc_name, mp->mechname,
+ err.rpc_gss_error, err.system_error);
+ }
+ }
+ __nis_release_mechanisms(mechs);
+ return;
+ }
+}
diff --git a/usr/src/cmd/rpcsvc/nis/utils/Makefile b/usr/src/cmd/rpcsvc/nis/utils/Makefile
new file mode 100644
index 0000000000..d04ff89a44
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/Makefile
@@ -0,0 +1,173 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/rpcsvc/nis/utils/Makefile
+#
+# Makefile for the nis_plus utility programs.
+#
+
+all:= TARGET= all
+install:= TARGET= install
+clean:= TARGET= clean
+clobber:= TARGET= clobber
+lint:= TARGET= lint
+
+SUBDIRS= nisaddcred
+
+# links to nischmod
+LPROG= nischgrp nischown nischttl
+
+PROG= niscat nismatch nistest nisdefaults nischmod nisrm \
+ nisgrep nismkdir nisrmdir nistbladm nisgrpadm nisln \
+ nisls niserror nispath nisprefadm
+
+SRCS= niscat.c nismatch.c nistest.c nisdefaults.c nischmod.c nisrm.c \
+ nisgrep.c nismkdir.c nisrmdir.c nistbladm.c nisgrpadm.c nisln.c \
+ nisls.c niserror.c nispath.c nisprefadm.c
+
+OBJS= niscat.o nismatch.o nistest.o nisdefaults.o nischmod.o nisrm.o \
+ nisgrep.o nismkdir.o nisrmdir.o nistbladm.o nisgrpadm.o nisln.o \
+ nisls.o niserror.o nispath.o nisprefadm.o
+
+RPROG= nisaddent nisauthconf
+RSRCS= nisaddent.c nisauthconf.c
+ROBJS= nisaddent.o nisauthconf.o
+
+UTILSRCS= nis_util.c
+UTILOBJS= nis_util.o
+
+BACKUP_PROG= nisbackup
+BACKUP_SRCS= nisbackup.cc ../rpc.nisd/nis_log_common.c \
+ ../rpc.nisd/nis_mt.c ../rpc.nisd/nis_cleanup.c
+BACKUP_OBJS= nisbackup.o nis_log_common.o nis_mt.o nis_cleanup.o
+
+RESTORE_PROG= nisrestore
+RESTORE_SRCS= nisrestore.cc ../rpc.nisd/nis_log_common.c \
+ ../rpc.nisd/nis_mt.c ../rpc.nisd/nis_cleanup.c
+RESTORE_OBJS= nisrestore.o nis_log_common.o nis_mt.o nis_cleanup.o
+
+BKRSTPROG= $(BACKUP_PROG) $(RESTORE_PROG)
+
+# XX64 nisrestore and nisbackup require C++
+__GNUC:sh= echo \\043
+$(__GNUC)BKRSTPROG=
+
+DERIVED_FILES= ../rpc.nisd/nis_svc.h
+
+include ../../../Makefile.cmd
+
+ROOTNSLIB= $(ROOT)/usr/lib/nis
+ROOTUSRSBIN= $(ROOT)/usr/sbin
+ROOTNSPROG= $(RPROG:%=$(ROOTNSLIB)/%)
+ROOTLPROG= $(LPROG:%=$(ROOTBIN)/%)
+ROOTUSBPROG= $(BKRSTPROG:%=$(ROOTUSRSBIN)/%)
+CLOBBERFILES += $(RPROG) $(LPROG) $(BACKUP_PROG) $(RESTORE_PROG)\
+ $(DERIVED_FILES)
+
+PROTOCOL_DIR= $(ROOT)/usr/include/rpcsvc
+CPPFLAGS += -I$(SRC)/lib/libnsl/include -I$(SRC)/lib/libnisdb
+LDLIBS += -lnsl util.a
+nispasswd := LDLIBS = $(LDLIBS.cmd) -lnsl
+
+LDLIBS += -lc
+
+.KEEP_STATE:
+
+all: $(DERIVED_FILES) util.a $(SUBDIRS) $(PROG) $(RPROG) $(LPROG) \
+ $(BKRSTPROG)
+
+install: all $(SUBDIRS) $(ROOTPROG) $(ROOTNSPROG) $(ROOTLPROG) $(ROOTUSBPROG)
+
+clean:
+ $(RM) $(OBJS) $(ROBJS) $(UTILOBJS) $(BACKUP_OBJS) $(RESTORE_OBJS) util.a
+
+lint: lint_SRCS
+
+nispasswd:
+ @$(ECHO) "\`\`nispasswd'' is now a link to \`\`passwd''."
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+#
+# build an archive library so that those programs that need stuff can
+# get it, but others won't link in unneeded routines.
+#
+
+util.a: $(UTILOBJS)
+ $(AR) -crv $@ $(UTILOBJS)
+
+$(PROG:%=%): $$@.o util.a
+ $(LINK.c) -o $@ $@.o $(LDLIBS)
+ $(POST_PROCESS)
+
+$(LPROG): nischmod
+ -$(RM) $@ ; $(LN) nischmod $@
+
+$(ROOTLPROG): $(ROOTBIN)/nischmod
+ -$(RM) $@ ; $(LN) $(ROOTBIN)/nischmod $@
+
+$(ROOTNSLIB):
+ $(INS.dir)
+
+$(ROOTNSLIB)/%: % $(ROOTNSLIB)
+ $(INS.file)
+
+$(RESTORE_PROG): $(DERIVED_FILES) $(RESTORE_OBJS) util.a
+ $(LINK.cc) -o $@ $(RESTORE_OBJS) $(LDLIBS) -lnisdb
+ $(POST_PROCESS)
+
+$(BACKUP_PROG): $(DERIVED_FILES) $(BACKUP_OBJS) util.a
+ $(LINK.cc) -o $@ $(BACKUP_OBJS) $(LDLIBS) -lnisdb
+ $(POST_PROCESS)
+
+nis_log_common.o: ../rpc.nisd/nis_log_common.c $(DERIVED_FILES)
+ $(COMPILE.c) ../rpc.nisd/nis_log_common.c
+ $(POST_PROCESS_O)
+
+nis_mt.o: ../rpc.nisd/nis_mt.c $(DERIVED_FILES)
+ $(COMPILE.c) ../rpc.nisd/nis_mt.c
+ $(POST_PROCESS_O)
+
+nis_cleanup.o: ../rpc.nisd/nis_cleanup.c $(DERIVED_FILES)
+ $(COMPILE.c) ../rpc.nisd/nis_cleanup.c
+ $(POST_PROCESS_O)
+
+#
+# Rules for building the derived files :
+#
+$(DERIVED_FILES): $(PROTOCOL_DIR)/nis.x $(PROTOCOL_DIR)/nis_object.x
+ $(RPCGEN) -C -h $(PROTOCOL_DIR)/nis.x |\
+ sed -n -e '/EDIT_START/,$$ p' |\
+ sed -e 's/_2_svc/_svc/' |\
+ sed -e 's/_3_svc/_svc/' |\
+ sed -e 's/_3/_clnt/' |\
+ sed -e 's/_2/_clnt/' >../rpc.nisd/nis_svc.h
+
+include ../../../Makefile.targ
+
+FRC:
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nis_bkrst.h b/usr/src/cmd/rpcsvc/nis/utils/nis_bkrst.h
new file mode 100644
index 0000000000..aba79e1049
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nis_bkrst.h
@@ -0,0 +1,89 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 1996 Sun Microsystems, Inc.
+ */
+
+/*
+ * Definitions for nisbackup(1M) and nisrestore(1M). These are private
+ * definitions and may change without notice.
+ */
+
+#ifndef _NIS_BKRST_H
+#define _NIS_BKRST_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int optind;
+extern char *optarg;
+
+#if defined(__STDC__) || defined(__cplusplus)
+
+extern bool_t db_copy_file(char *, char *);
+extern bool_t db_extract_dict_entries(char *, char **, u_int);
+extern bool_t db_in_dict_file(char *);
+extern db_status db_begin_merge_dict(char *, char *, char *);
+extern db_status db_end_merge_dict();
+extern db_status db_abort_merge_dict();
+extern char *db_perror(db_status);
+extern void sync_header();
+
+#else /* Non-prototype definitions */
+
+extern bool_t db_copy_file();
+extern bool_t db_extract_dict_entries();
+extern bool_t db_in_dict_file();
+extern db_status db_begin_merge_dict();
+extern db_status db_end_merge_dict();
+extern db_status db_abort_merge_dict();
+extern char *db_perror();
+extern void sync_header();
+
+#endif /* defined(__STDC__) || defined(__cplusplus) */
+
+/*
+ * verbose is must be defined for nis_log_common, but is not used, since
+ * the trans.log merge routines output way too much verbiage. Hence, the
+ * static flag, verbiage.
+ */
+bool_t verbose = FALSE;
+
+/*
+ * BACKUPREV is the current revision number for the backup snap shot. If
+ * something changes with the NIS+ database or the nisbackup/nisrestore
+ * snapshot, which effects backwards compatibility, this needs to be rev'd
+ */
+#define BACKUPREV 0xBACDEF01
+#define BACKUPLIST "backup_list"
+#define LASTUPDATE "last.upd"
+#define MAXRETRIES 10 /* Max number of retries for contacting the server */
+#define DOMAIN_ALL 1 /* Mask for -a option */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _NIS_BKRST_H */
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nis_util.c b/usr/src/cmd/rpcsvc/nis/utils/nis_util.c
new file mode 100644
index 0000000000..f1907da713
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nis_util.c
@@ -0,0 +1,869 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * nis_util.c
+ *
+ * Copyright 1988-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <rpcsvc/nis.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <shadow.h>
+#include <stdlib.h>
+
+/*
+ * nisname_index()
+ *
+ * Return pointer to place in string s of first match with char c.
+ * Do not match between quotes ("...").
+ *
+ * Totally rewritten as part of the fix for bug# 1263305.
+ * XXX - This can now probably be replaced by strchr_quotes().
+ */
+char *
+nisname_index(char *s, char c)
+{
+ do {
+ if (*s == '"') {
+ s++;
+ while (*s && *s != '"')
+ s++;
+ } else if (*s == c)
+ return (s);
+ } while (*s++);
+ return (NULL);
+}
+
+/*
+ * Parse a passed name into a basename and search criteria.
+ * if there is no criteria present the *crit == 0. You must
+ * pass in allocated data for the three strings.
+ *
+ * Returns 0 if successful, non-zero otherwise.
+ */
+int
+nisname_split(name, base, crit, max_len)
+ char *name;
+ char *base;
+ char *crit;
+ int max_len;
+{
+ register char *p, *q;
+
+ p = name;
+ while (*p && (isspace(*p)))
+ p++;
+ if (*p != '[') {
+ *crit = 0;
+ return (strlcpy(base, p, max_len) >= max_len);
+ }
+
+ /* it has a criteria, copy the whole thing in */
+ if (strlcpy(crit, p, max_len) >= max_len)
+ return (1);
+ q = nisname_index(crit, ']');
+ if (! q) {
+ *crit = 0; /* error condition */
+ *base = 0;
+ return (1);
+ }
+ q++;
+ if (*q == ',') {
+ *q = 0;
+ q++;
+ }
+ if (strlcpy(base, q, max_len) >= max_len)
+ return (1);
+ *q = 0; /* just in case there wasn't a comma */
+
+ return (0);
+}
+
+bool_t
+nis_verifycred(n, flags)
+ nis_name n;
+ uint_t flags;
+{
+ nis_result *res;
+ int err;
+ char dname[NIS_MAXNAMELEN];
+
+ (void) snprintf(dname, sizeof (dname), "[cname=%s],cred.org_dir.%s", n,
+ nis_domain_of(n));
+ res = nis_list(dname, flags, NULL, NULL);
+ err = (res->status == NIS_SUCCESS);
+ nis_freeresult(res);
+ return (err);
+}
+
+#define NIS_ALL_ACC (NIS_READ_ACC|NIS_MODIFY_ACC|NIS_CREATE_ACC|NIS_DESTROY_ACC)
+
+static int
+parse_rights_field(rights, shift, p)
+ uint_t *rights;
+ int shift;
+ char *p;
+{
+ int set;
+
+ while (*p && (*p != ',')) {
+ switch (*p) {
+ case '=':
+ *rights &= ~(NIS_ALL_ACC << shift);
+ case '+':
+ set = 1;
+ break;
+ case '-':
+ set = 0;
+ break;
+ default:
+ return (0);
+ }
+ for (p++; *p && (*p != ',') && (*p != '=') && (*p != '+') &&
+ (*p != '-'); p++) {
+ switch (*p) {
+ case 'r':
+ if (set)
+ *rights |= (NIS_READ_ACC << shift);
+ else
+ *rights &= ~(NIS_READ_ACC << shift);
+ break;
+ case 'm':
+ if (set)
+ *rights |= (NIS_MODIFY_ACC << shift);
+ else
+ *rights &= ~(NIS_MODIFY_ACC << shift);
+ break;
+ case 'c':
+ if (set)
+ *rights |= (NIS_CREATE_ACC << shift);
+ else
+ *rights &= ~(NIS_CREATE_ACC << shift);
+ break;
+ case 'd':
+ if (set)
+ *rights |= (NIS_DESTROY_ACC << shift);
+ else
+ *rights &= ~(NIS_DESTROY_ACC << shift);
+ break;
+ default:
+ return (0);
+ }
+ }
+ }
+ return (1);
+}
+
+#define NIS_NOBODY_FLD 1
+#define NIS_OWNER_FLD 2
+#define NIS_GROUP_FLD 4
+#define NIS_WORLD_FLD 8
+#define NIS_ALL_FLD NIS_OWNER_FLD|NIS_GROUP_FLD|NIS_WORLD_FLD
+
+int
+parse_rights(rights, p)
+ uint_t *rights;
+ char *p;
+{
+ uint_t f;
+
+ if (p)
+ while (*p) {
+ for (f = 0; (*p != '=') && (*p != '+') && (*p != '-');
+ p++)
+ switch (*p) {
+ case 'n':
+ f |= NIS_NOBODY_FLD;
+ break;
+ case 'o':
+ f |= NIS_OWNER_FLD;
+ break;
+ case 'g':
+ f |= NIS_GROUP_FLD;
+ break;
+ case 'w':
+ f |= NIS_WORLD_FLD;
+ break;
+ case 'a':
+ f |= NIS_ALL_FLD;
+ break;
+ default:
+ return (0);
+ }
+ if (f == 0)
+ f = NIS_ALL_FLD;
+
+ if ((f & NIS_NOBODY_FLD) &&
+ !parse_rights_field(rights, 24, p))
+ return (0);
+
+ if ((f & NIS_OWNER_FLD) &&
+ !parse_rights_field(rights, 16, p))
+ return (0);
+
+ if ((f & NIS_GROUP_FLD) &&
+ !parse_rights_field(rights, 8, p))
+ return (0);
+
+ if ((f & NIS_WORLD_FLD) &&
+ !parse_rights_field(rights, 0, p))
+ return (0);
+
+ while (*(++p))
+ if (*p == ',') {
+ p++;
+ break;
+ }
+ }
+ return (1);
+}
+
+
+int
+parse_flags(flags, p)
+ uint_t *flags;
+ char *p;
+{
+ if (p) {
+ while (*p) {
+ switch (*(p++)) {
+ case 'B':
+ *flags |= TA_BINARY;
+ break;
+ case 'X':
+ *flags |= TA_XDR;
+ break;
+ case 'S':
+ *flags |= TA_SEARCHABLE;
+ break;
+ case 'I':
+ *flags |= TA_CASE;
+ break;
+ case 'C':
+ *flags |= TA_CRYPT;
+ break;
+ default:
+ return (0);
+ }
+ }
+ return (1);
+ } else {
+ fprintf(stderr,
+ "Invalid table schema: At least one column must be searchable.\n");
+ exit(1);
+ }
+}
+
+
+int
+parse_time(time, p)
+ uint32_t *time;
+ char *p;
+{
+ char *s;
+ uint32_t x;
+
+ *time = 0;
+
+ if (p)
+ while (*p) {
+ if (!isdigit(*p))
+ return (0);
+ x = strtol(p, &s, 10);
+ switch (*s) {
+ case '\0':
+ (*time) += x;
+ p = s;
+ break;
+ case 's':
+ case 'S':
+ (*time) += x;
+ p = s+1;
+ break;
+ case 'm':
+ case 'M':
+ (*time) += x*60;
+ p = s+1;
+ break;
+ case 'h':
+ case 'H':
+ (*time) += x*(60*60);
+ p = s+1;
+ break;
+ case 'd':
+ case 'D':
+ (*time) += x*(24*60*60);
+ p = s+1;
+ break;
+ default:
+ return (0);
+ }
+ }
+
+ return (1);
+}
+
+
+static int
+nis_getsubopt(optionsp, tokens, sep, valuep)
+ char **optionsp;
+ char * const *tokens;
+ const int sep; /* if this is a char we get an alignment error */
+ char **valuep;
+{
+ register char *s = *optionsp, *p, *q;
+ register int i, optlen;
+
+ *valuep = NULL;
+ if (*s == '\0')
+ return (-1);
+ q = strchr(s, (char)sep); /* find next option */
+ if (q == NULL) {
+ q = s + strlen(s);
+ } else {
+ *q++ = '\0'; /* mark end and point to next */
+ }
+ p = strchr(s, '='); /* find value */
+ if (p == NULL) {
+ optlen = strlen(s);
+ *valuep = NULL;
+ } else {
+ optlen = p - s;
+ *valuep = ++p;
+ }
+ for (i = 0; tokens[i] != NULL; i++) {
+ if ((optlen == strlen(tokens[i])) &&
+ (strncmp(s, tokens[i], optlen) == 0)) {
+ /* point to next option only if success */
+ *optionsp = q;
+ return (i);
+ }
+ }
+ /* no match, point value at option and return error */
+ *valuep = s;
+ return (-1);
+}
+
+
+nis_object nis_default_obj;
+
+/*
+ * We record the source of the defaults.
+ * 0 => default
+ * 1 => from NIS_DEFAULTS env variable
+ * 2 => from arg passed to nis_defaults_init
+ */
+#define NIS_SRC_DEFAULT 0
+#define NIS_SRC_ENV 1
+#define NIS_SRC_ARG 2
+
+int nis_default_owner_src = NIS_SRC_DEFAULT;
+int nis_default_group_src = NIS_SRC_DEFAULT;
+int nis_default_access_src = NIS_SRC_DEFAULT;
+int nis_default_ttl_src = NIS_SRC_DEFAULT;
+
+static char *nis_defaults_tokens[] = {
+ "owner",
+ "group",
+ "access",
+ "ttl",
+ 0
+};
+
+#define T_OWNER 0
+#define T_GROUP 1
+#define T_ACCESS 2
+#define T_TTL 3
+
+static int
+nis_defaults_set(optstr, src)
+ char *optstr;
+ int src;
+{
+ char str[1024], *p, *v;
+ int i;
+
+ if (strlcpy(str, optstr, sizeof (str)) >= sizeof (str))
+ return (0);
+ p = str;
+
+ while ((i = nis_getsubopt(&p, nis_defaults_tokens, ':', &v)) != -1) {
+ switch (i) {
+ case T_OWNER:
+ if (v == 0 || v[strlen(v)-1] != '.')
+ return (0);
+ nis_default_obj.zo_owner = strdup(v);
+ nis_default_owner_src = src;
+ break;
+ case T_GROUP:
+ if (v == 0 || v[strlen(v)-1] != '.')
+ return (0);
+ nis_default_obj.zo_group = strdup(v);
+ nis_default_group_src = src;
+ break;
+ case T_ACCESS:
+ if ((v == 0) ||
+ (!parse_rights(&(nis_default_obj.zo_access), v)))
+ return (0);
+ nis_default_access_src = src;
+ break;
+ case T_TTL:
+ if ((v == 0) ||
+ !(parse_time(&(nis_default_obj.zo_ttl), v)))
+ return (0);
+ nis_default_ttl_src = src;
+ break;
+ }
+ }
+
+ if (*p)
+ return (0);
+
+ return (1);
+}
+
+extern char *getenv();
+
+int
+nis_defaults_init(optstr)
+ char *optstr;
+{
+ char *envstr;
+
+ /* XXX calling this multiple times may leak memory */
+ memset((char *)&nis_default_obj, 0, sizeof (nis_default_obj));
+
+ nis_default_obj.zo_owner = nis_local_principal();
+ nis_default_obj.zo_group = nis_local_group();
+ nis_default_obj.zo_access = DEFAULT_RIGHTS;
+ nis_default_obj.zo_ttl = 12 * 60 * 60;
+
+ if (envstr = getenv("NIS_DEFAULTS"))
+ if (!nis_defaults_set(envstr, NIS_SRC_ENV)) {
+ fprintf(stderr,
+ "can't parse NIS_DEFAULTS environment variable.\n");
+ return (0);
+ }
+
+ if (optstr)
+ if (!nis_defaults_set(optstr, NIS_SRC_ARG)) {
+ fprintf(stderr, "can't parse nis_defaults argument.\n");
+ return (0);
+ }
+
+ return (1);
+}
+
+
+
+/*
+ * Converts an NIS+ entry object for a passwd table to its
+ * pwent structure.
+ * XXX: This function returns a pointer to a static structure.
+ */
+static struct passwd *
+nis_object_to_pwent(obj, error)
+ nis_object *obj;
+ nis_error *error;
+{
+ static struct passwd pw;
+ static char spacebuf[1024]; /* The pwent structure points to this */
+ static char nullstring; /* used for NULL data */
+ char *tmp;
+ char *end;
+
+ memset((void *)&pw, 0, sizeof (struct passwd));
+ memset((void *)&spacebuf[0], 0, 1024);
+ tmp = &spacebuf[0];
+ end = tmp + sizeof (spacebuf);
+
+ if ((obj->zo_data.zo_type != NIS_ENTRY_OBJ) ||
+ (obj->EN_data.en_cols.en_cols_len < 8)) {
+ *error = NIS_INVALIDOBJ;
+ return (NULL);
+ }
+ if (ENTRY_LEN(obj, 0) == 0) {
+ *error = NIS_INVALIDOBJ;
+ return (NULL);
+ } else {
+ (void) strlcpy(tmp, ENTRY_VAL(obj, 0),
+ end > tmp ? end - tmp : 0);
+ pw.pw_name = tmp;
+ tmp += strlen(pw.pw_name) + 1;
+ }
+
+ if (ENTRY_LEN(obj, 1) == 0) {
+ pw.pw_passwd = &nullstring;
+ } else {
+ /* XXX: Should I be returning X here? */
+ (void) strlcpy(tmp, ENTRY_VAL(obj, 1),
+ end > tmp ? end - tmp : 0);
+ pw.pw_passwd = tmp;
+ tmp += strlen(pw.pw_passwd) + 1;
+ }
+
+ if (ENTRY_LEN(obj, 2) == 0) {
+ *error = NIS_INVALIDOBJ;
+ return (NULL);
+ }
+ pw.pw_uid = atoi(ENTRY_VAL(obj, 2));
+
+ if (ENTRY_LEN(obj, 3) == 0)
+ pw.pw_gid = 0; /* Is this default value? */
+ else
+ pw.pw_gid = atoi(ENTRY_VAL(obj, 3));
+
+ if (ENTRY_LEN(obj, 4) == 0) {
+ pw.pw_gecos = &nullstring;
+ } else {
+ (void) strlcpy(tmp, ENTRY_VAL(obj, 4),
+ end > tmp ? end - tmp : 0);
+ pw.pw_gecos = tmp;
+ tmp += strlen(pw.pw_gecos) + 1;
+ }
+
+ if (ENTRY_LEN(obj, 5) == 0) {
+ pw.pw_dir = &nullstring;
+ } else {
+ (void) strlcpy(tmp, ENTRY_VAL(obj, 5),
+ end > tmp ? end - tmp : 0);
+ pw.pw_dir = tmp;
+ tmp += strlen(pw.pw_dir) + 1;
+ }
+
+ if (ENTRY_LEN(obj, 6) == 0) {
+ pw.pw_shell = &nullstring;
+ } else {
+ (void) strlcpy(tmp, ENTRY_VAL(obj, 6),
+ end > tmp ? end - tmp : 0);
+ pw.pw_shell = tmp;
+ tmp += strlen(pw.pw_shell) + 1;
+ }
+
+ pw.pw_age = &nullstring;
+ pw.pw_comment = &nullstring;
+ *error = NIS_SUCCESS;
+ return (&pw);
+}
+
+
+/*
+ * This will go to the NIS+ master to get the data. This code
+ * is ugly because the internals of the switch had to be opened
+ * up here. Wish there was a way to pass a MASTER_ONLY flag
+ * to getpwuid() and all such getXbyY() calls. Some of this code
+ * is being copied from the NIS+ switch backend.
+ *
+ * XXX: We will not bother to make this MT-safe. If any of the callers
+ * for this function want to use getpwuid_r(), then a corresponding
+ * function will have to written.
+ */
+struct passwd *
+getpwuid_nisplus_master(domain, uid, error)
+ char *domain;
+ uid_t uid;
+ nis_error *error;
+{
+ struct passwd *passwd_ent;
+ nis_result *res;
+ char namebuf[NIS_MAXNAMELEN];
+ uint_t flags;
+
+ (void) snprintf(namebuf, sizeof (namebuf),
+ "[uid=%ld],passwd.org_dir.%s", uid, domain);
+ flags = EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH|USE_DGRAM|MASTER_ONLY;
+ res = nis_list(namebuf, flags, 0, 0);
+ if (res == NULL) {
+ *error = NIS_NOMEMORY;
+ return (NULL);
+ }
+ if (res->status != NIS_SUCCESS) {
+ nis_freeresult(res);
+ *error = res->status;
+ return (NULL);
+ }
+ if (NIS_RES_NUMOBJ(res) == 0) {
+ nis_freeresult(res);
+ *error = NIS_NOTFOUND;
+ return (NULL);
+ }
+
+ passwd_ent = nis_object_to_pwent(NIS_RES_OBJECT(res), error);
+ nis_freeresult(res);
+ return (passwd_ent);
+}
+
+/*
+ * Converts an NIS+ entry object for a shadow table to its
+ * spwent structure. We only fill in the sp_namp and sp_pwdp fields.
+ * XXX: This function returns a pointer to a static structure.
+ */
+static struct spwd *
+nis_object_to_spwent(obj, error)
+ nis_object *obj;
+ nis_error *error;
+{
+ static struct spwd spw;
+ static char spacebuf[1024]; /* The pwent structure points to this */
+ static char nullstring; /* used for NULL data */
+ char *tmp;
+ char *end;
+
+ memset((void *)&spw, 0, sizeof (struct spwd));
+ memset((void *)&spacebuf[0], 0, 1024);
+ tmp = &spacebuf[0];
+ end = tmp + sizeof (spacebuf);
+
+ if ((obj->zo_data.zo_type != NIS_ENTRY_OBJ) ||
+ (obj->EN_data.en_cols.en_cols_len < 8)) {
+ *error = NIS_INVALIDOBJ;
+ return (NULL);
+ }
+ if (ENTRY_LEN(obj, 0) == 0) {
+ *error = NIS_INVALIDOBJ;
+ return (NULL);
+ } else {
+ (void) strlcpy(tmp, ENTRY_VAL(obj, 0),
+ end > tmp ? end - tmp : 0);
+ spw.sp_namp = tmp;
+ tmp += strlen(spw.sp_namp) + 1;
+ }
+
+ if (ENTRY_LEN(obj, 1) == 0) {
+ spw.sp_pwdp = &nullstring;
+ } else {
+ (void) strlcpy(tmp, ENTRY_VAL(obj, 1),
+ end > tmp ? end - tmp : 0);
+ spw.sp_pwdp = tmp;
+ tmp += strlen(spw.sp_pwdp) + 1;
+ }
+
+ *error = NIS_SUCCESS;
+ return (&spw);
+}
+
+
+/*
+ * This will go to the NIS+ master to get the data. This code
+ * is ugly because the internals of the switch had to be opened
+ * up here. Wish there was a way to pass a MASTER_ONLY flag
+ * to getspnam() and all such getXbyY() calls. Some of this code
+ * is being copied from the NIS+ switch backend.
+ *
+ * XXX: We will not bother to make this MT-safe. If any of the callers
+ * for this function want to use getpwuid_r(), then a corresponding
+ * function will have to written.
+ */
+struct spwd *
+getspnam_nisplus_master(domain, name, error)
+ char *domain;
+ char *name;
+ nis_error *error;
+{
+ struct spwd *shadow_ent;
+ nis_result *res;
+ char namebuf[NIS_MAXNAMELEN];
+ uint_t flags;
+
+ (void) snprintf(namebuf, sizeof (namebuf),
+ "[name=%s],passwd.org_dir.%s", name, domain);
+ flags = EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH|USE_DGRAM|MASTER_ONLY;
+ res = nis_list(namebuf, flags, 0, 0);
+ if (res == NULL) {
+ *error = NIS_NOMEMORY;
+ return (NULL);
+ }
+ if (res->status != NIS_SUCCESS) {
+ nis_freeresult(res);
+ *error = res->status;
+ return (NULL);
+ }
+ if (NIS_RES_NUMOBJ(res) == 0) {
+ nis_freeresult(res);
+ *error = NIS_NOTFOUND;
+ return (NULL);
+ }
+
+ shadow_ent = nis_object_to_spwent(NIS_RES_OBJECT(res), error);
+ nis_freeresult(res);
+ return (shadow_ent);
+}
+
+/* begin bug# 1263305 */
+
+/*
+ * __nis_quote_key()
+ *
+ * Enclose NIS+ terminating characters ']' and ',' within '"' and
+ * escape '"' by doubling it so the string is safe to pass to
+ * nis_list(3N) and associated routines. For example, a src string
+ * of "foo[]bar" will result in a dst string of ""foo["]"bar""
+ */
+char *
+__nis_quote_key(const char *src, char *dst, int dstsize)
+{
+
+ char *dstorig = dst;
+ int dstleft;
+
+ dstleft = dstsize - 4;
+ for (; *src != '\0' && dstleft > 0; src++) {
+ switch (*src) {
+ case ']':
+ case ',':
+ /* quote the character */
+ *dst++ = '"';
+ *dst++ = *src;
+ *dst++ = '"';
+ dstleft -= 3;
+ break;
+
+ case '"':
+ /* double the quote */
+ *dst++ = '"';
+ dstleft--;
+ /* fall through... */
+
+ default:
+ *dst++ = *src;
+ dstleft--;
+ break;
+ }
+ }
+ *dst = '\0';
+
+ if (*src)
+ fprintf(stderr,
+ "nis_quote_key: warning: src string too long\n");
+
+ return (dstorig);
+}
+
+/*
+ * *str* routines below originated in libc.
+ */
+
+/*
+ * strpbrk_quotes()
+ *
+ * Like strpbrk except it will not match target chars inside quoted
+ * strings ("...").
+ */
+char *
+strpbrk_quotes(char *string, char *brkset)
+{
+ register const char *p;
+
+ do {
+ for (p = brkset; *p != '\0' && *string != '"' && *p != *string;
+ ++p)
+ ;
+
+ if (*string == '"') {
+ string++;
+ while (*string != '\0' && *string != '"')
+ string++;
+ } else if (*p != '\0')
+ return ((char *)string);
+ }
+ while (*string++);
+ return (NULL);
+}
+
+
+/*
+ * strtok_r_quotes()
+ *
+ * uses strpbrk_quotes and strspn to break string into tokens on
+ * sequentially subsequent calls. returns NULL when no
+ * non-separator characters remain.
+ * `subsequent' calls are calls with first argument NULL.
+ *
+ */
+
+static char *
+strtok_r_quotes(char *string, char *sepset, char **lasts)
+{
+ char *q, *r;
+
+ /* first or subsequent call */
+ if (string == NULL)
+ string = *lasts;
+
+ if (string == 0) /* return if no tokens remaining */
+ return (NULL);
+
+ q = string + strspn(string, sepset); /* skip leading separators */
+
+ if (*q == '\0') /* return if no tokens remaining */
+ return (NULL);
+
+ if ((r = strpbrk_quotes(q, sepset)) == NULL) /* move past token */
+ *lasts = 0; /* indicate this is last token */
+ else {
+ *r = '\0';
+ *lasts = r+1;
+ }
+ return (q);
+}
+
+
+/*
+ * strtok_quotes()
+ *
+ * Like strtok except it will not match target chars within quoted
+ * strings ("...").
+ */
+char *
+strtok_quotes(char *string, char *sepset)
+{
+ static char *lasts;
+
+ return (strtok_r_quotes(string, sepset, &lasts));
+}
+
+/*
+ * strchr_quotes()
+ *
+ * Like strchr but will not match within quoted strings ("...").
+ */
+char *
+strchr_quotes(char *s, char c)
+{
+ do {
+ if (*s == '"') {
+ s++;
+ while (*s && *s != '"')
+ s++;
+ } else if (*s == c)
+ return (s);
+ } while (*s++);
+ return (NULL);
+}
+
+/* end bug# 1263305 */
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/Makefile b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/Makefile
new file mode 100644
index 0000000000..4157496a81
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/Makefile
@@ -0,0 +1,65 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Makefile for the nisaddcred program
+#
+
+PROG= nisaddcred
+
+SRCS= \
+ nisaddcred.c makelocalcred.c makedescred.c \
+ makersacred.c makekerbcred.c makedhextcred.c
+OBJS= \
+ nisaddcred.o makelocalcred.o makedescred.o \
+ makersacred.o makekerbcred.o makedhextcred.o
+UTIL= ../util.a
+
+include $(SRC)/cmd/Makefile.cmd
+
+CPPFLAGS += -I$(SRC)/lib/libnsl/include
+CFLAGS += -I$(ROOT)/usr/include
+LDLIBS += -lnsl $(UTIL)
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTPROG)
+
+lint: lint_SRCS
+
+clean:
+ $(RM) $(OBJS)
+
+$(UTIL):
+ @cd ..; pwd; $(MAKE) util.a
+
+$(PROG): $(OBJS) $(UTIL)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+include $(SRC)/cmd/Makefile.targ
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makedescred.c b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makedescred.c
new file mode 100644
index 0000000000..106df31888
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makedescred.c
@@ -0,0 +1,288 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * makedescred.c
+ *
+ * Copyright (c) 1988-1992,2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * makedescred.c
+ *
+ * Make a "AUTH_DES" credential. This is the old secure rpc credentials from
+ * SunOS 4.0 and Vanilla System V release 4.0.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include <shadow.h>
+#include <string.h>
+#include <ctype.h>
+#include <nsswitch.h>
+#include <netdb.h>
+#include <rpcsvc/nis.h>
+#include <rpcsvc/nis_dhext.h>
+#include <rpc/key_prot.h>
+#include "nisaddcred.h"
+#include <assert.h>
+
+extern char *getpass();
+extern char *crypt();
+extern void __gen_dhkeys(char *, char *, char *);
+extern int add_cred_obj(nis_object *, char *);
+extern int check_switch_policy(char *, char *, struct __nsw_switchconfig *,
+ char *, char *);
+extern nis_error cred_exists(char *, char *, char *);
+extern char *get_password(uid_t, int, char *, char *);
+extern int is_switch_policy(struct __nsw_switchconfig *, char *);
+extern int key_setnet(struct key_netstarg *arg);
+extern int make_dhext_cred(char *, char *, char *, char *);
+extern int modify_cred_obj(nis_object *, char *);
+extern int no_switch_policy(struct __nsw_switchconfig *);
+extern int sanity_checks(char *, char *, char *, char *);
+extern char *switch_policy_str(struct __nsw_switchconfig *);
+extern void write_rootkey(char *, char *, keylen_t, algtype_t);
+extern int xencrypt(char *, char *);
+
+static const char *OPSYS = "unix";
+#define OPSYS_LEN 4
+
+/* ************************ switch functions *************************** */
+
+/* NSW_NOTSUCCESS NSW_NOTFOUND NSW_UNAVAIL NSW_TRYAGAIN */
+#define DEF_ACTION {__NSW_RETURN, __NSW_RETURN, __NSW_CONTINUE, __NSW_CONTINUE}
+
+
+/* ***************************** keylogin stuff *************************** */
+int
+keylogin_des(char *netname, char *secret)
+{
+ struct key_netstarg netst;
+
+ netst.st_pub_key[0] = 0;
+ memcpy(netst.st_priv_key, secret, HEXKEYBYTES);
+ netst.st_netname = netname;
+
+#ifdef NFS_AUTH
+ nra.authtype = AUTH_DES; /* only revoke DES creds */
+ nra.uid = getuid(); /* use the real uid */
+ if (_nfssys(NFS_REVAUTH, &nra) < 0) {
+ perror("Warning: NFS credentials not destroyed");
+ err = 1;
+ }
+#endif /* NFS_AUTH */
+
+
+ /* do actual key login */
+ if (key_setnet(&netst) < 0) {
+ fprintf(stderr, "Could not set %s's secret key\n", netname);
+ fprintf(stderr, "May be the keyserv is down?\n");
+ return (0);
+ }
+
+ return (1);
+}
+
+
+/*
+ * Definitions of the credential table.
+ *
+ * Column Name Contents
+ * ------ ---- --------
+ * 0 cname nis principal name
+ * 1 auth_type DES
+ * 2 auth_name netname
+ * 3 public_auth_data public key
+ * 4 private_auth_data encrypted secret key with checksum
+ */
+
+/*
+ * Function for building DES credentials.
+ *
+ * The domain may be the local domain or some remote domain.
+ * 'domain' should be the same as the domain found in netname,
+ * which should be the home domain of nis+ principal.
+ */
+
+
+int
+make_des_cred_be(char *nis_princ, char *netname, char *domain)
+{
+ nis_object *obj = init_entry();
+ char *pass;
+ uid_t uid;
+ char public[HEXKEYBYTES + 1];
+ char secret[HEXKEYBYTES + 1];
+ char crypt1[HEXKEYBYTES + KEYCHECKSUMSIZE + 1];
+ char target_host[MAXHOSTNAMELEN+1];
+ int same_host = 0;
+ int status, len, addition;
+
+ if (nis_princ == NULL)
+ nis_princ = default_principal(domain);
+
+ if (sanity_checks(nis_princ, netname, domain, "DES") == 0)
+ return (0);
+
+ addition = (cred_exists(nis_princ, "DES", domain) == NIS_NOTFOUND);
+
+ /* Extract user/host information from netname */
+ if (! isdigit(netname[OPSYS_LEN+1])) {
+ uid = 0; /* root */
+ netname2host(netname, target_host, MAXHOSTNAMELEN);
+ len = strlen(my_host)-1; /* ignore trailing dot in my_host */
+ if (len == strlen(target_host) &&
+ strncasecmp(target_host, my_host, len) == 0)
+ same_host = 1;
+ } else {
+ uid = (uid_t)atoi(netname+OPSYS_LEN+1);
+ }
+
+ pass = get_password(uid, same_host, target_host, domain);
+ if (pass == 0)
+ return (0);
+
+ /* Get password with which to encrypt secret key. */
+ (void) printf("%s key pair for %s (%s).\n",
+ addition? "Adding" : "Updating", netname, nis_princ);
+
+
+ /* Encrypt secret key */
+ (void) __gen_dhkeys(public, secret, pass);
+ memcpy(crypt1, secret, HEXKEYBYTES);
+ memcpy(crypt1 + HEXKEYBYTES, secret, KEYCHECKSUMSIZE);
+ crypt1[HEXKEYBYTES + KEYCHECKSUMSIZE] = 0;
+ xencrypt(crypt1, pass);
+
+
+ /* Now we have a key pair, build up the cred entry */
+ ENTRY_VAL(obj, 0) = nis_princ;
+ ENTRY_LEN(obj, 0) = strlen(nis_princ) + 1;
+
+ ENTRY_VAL(obj, 1) = "DES";
+ ENTRY_LEN(obj, 1) = 4;
+
+ ENTRY_VAL(obj, 2) = netname;
+ ENTRY_LEN(obj, 2) = strlen(netname) + 1;
+
+ ENTRY_VAL(obj, 3) = public;
+ ENTRY_LEN(obj, 3) = strlen(public) + 1;
+#ifdef OLD_MODE
+ strcat(ENTRY_VAL(obj, 3), ":");
+ ENTRY_LEN(obj, 3)++;
+#endif
+
+ ENTRY_VAL(obj, 4) = crypt1;
+ ENTRY_LEN(obj, 4) = strlen(crypt1) + 1;
+
+ if (addition) {
+ obj->zo_owner = nis_princ;
+ obj->zo_group = my_group;
+ obj->zo_domain = domain;
+ /* owner: r, group: rmcd */
+ obj->zo_access = ((NIS_READ_ACC<<16)|
+ (NIS_READ_ACC|NIS_MODIFY_ACC|NIS_CREATE_ACC|
+ NIS_DESTROY_ACC)<<8);
+ status = add_cred_obj(obj, domain);
+ } else {
+ obj->EN_data.en_cols.en_cols_val[3].ec_flags |= EN_MODIFIED;
+ obj->EN_data.en_cols.en_cols_val[4].ec_flags |= EN_MODIFIED;
+ status = modify_cred_obj(obj, domain);
+ }
+
+
+ /* attempt keylogin if appropriate */
+ if (status) {
+ if ((uid == my_uid) && ((uid != 0) || same_host))
+ keylogin_des(netname, secret);
+ if ((uid == 0) && same_host)
+ write_rootkey(secret, "des", 192, 0);
+ }
+ return (status);
+}
+
+
+int
+make_des_cred(char *nis_princ, char *netname, char *domain, char *flavor)
+{
+ mechanism_t **mechlist;
+ int status = 0;
+ int i = 0;
+
+ if (mechlist = (mechanism_t **)__nis_get_mechanisms(FALSE)) {
+ while (mechlist[i]) {
+ status = make_dhext_cred(nis_princ, netname,
+ domain,
+ mechlist[i]->alias);
+ if (!status)
+ return (status);
+ i++;
+ }
+ } else
+ status = make_des_cred_be(nis_princ, netname, domain);
+
+ return (status);
+}
+
+
+char *
+get_des_cred(domain, flavor)
+char *domain;
+char *flavor; /* Ignored. */
+{
+ int uid, status;
+ static char netname[MAXNETNAMELEN+1];
+
+ uid = my_uid;
+
+ if (uid == 0)
+ status = host2netname(netname, (char *)NULL, domain);
+ else {
+ /* generate netname using uid and domain information. */
+ int len;
+ len = strlen(domain);
+ if ((len + OPSYS_LEN + 3 + MAXIPRINT) > MAXNETNAMELEN) {
+ printf("Domain name too long: \"%s\"\n", domain);
+ goto not_found;
+ }
+ (void) sprintf(netname, "%s.%d@%s", OPSYS, uid, domain);
+ len = strlen(netname);
+ if (netname[len-1] == '.')
+ netname[len-1] = '\0';
+
+ status = 1;
+ }
+
+ if (status == 1) {
+ printf("DES principal name : \"%s\"\n", netname);
+ return (netname);
+ }
+
+not_found:
+ printf("DES principal name for %d not found\n", uid);
+ return (NULL);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makedhextcred.c b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makedhextcred.c
new file mode 100644
index 0000000000..ff8a432863
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makedhextcred.c
@@ -0,0 +1,677 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * makedescred.c
+ *
+ * Copyright (c) 1997 Sun Microsystems Inc
+ * All Rights Reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * makedescred.c
+ *
+ * Make extended diffie-hellman GSS credential.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <shadow.h>
+#include <nsswitch.h>
+#include <netdb.h>
+#include <rpcsvc/nis.h>
+#include <rpcsvc/nis_dhext.h>
+#include <rpc/key_prot.h>
+#include "nisaddcred.h"
+
+extern char *getpass();
+extern char *crypt();
+extern int add_cred_obj(nis_object *, char *);
+extern nis_error auth_exists(char *, char *, char *, char *);
+extern nis_error cred_exists(char *, char *, char *);
+extern int keylogin_des(char *, char *);
+extern int make_des_cred_be(char *, char *, char *);
+extern int modify_cred_obj(nis_object *, char *);
+
+static int force = 1; /* Eventually, this will be an option */
+
+static const char *OPSYS = "unix";
+#define OPSYS_LEN 4
+#define ROOTKEY_FILE "/etc/.rootkey"
+
+/* ************************ switch functions *************************** */
+
+/* NSW_NOTSUCCESS NSW_NOTFOUND NSW_UNAVAIL NSW_TRYAGAIN */
+#define DEF_ACTION {__NSW_RETURN, __NSW_RETURN, __NSW_CONTINUE, __NSW_CONTINUE}
+
+static struct __nsw_lookup lookup_files = {"files", DEF_ACTION, NULL, NULL},
+ lookup_nis = {"nis", DEF_ACTION, NULL, &lookup_files};
+static struct __nsw_switchconfig publickey_default =
+ {0, "publickey", 2, &lookup_nis};
+
+
+char *
+switch_policy_str(struct __nsw_switchconfig *conf)
+{
+ struct __nsw_lookup *look;
+ static char policy[256];
+ int previous = 0;
+
+ memset((char *)policy, 0, 256);
+
+ for (look = conf->lookups; look; look = look->next) {
+ if (previous)
+ strcat(policy, " ");
+ strcat(policy, look->service_name);
+ previous = 1;
+ }
+
+ return (policy);
+}
+
+
+int
+no_switch_policy(struct __nsw_switchconfig *conf)
+{
+ return (conf == NULL || conf->lookups == NULL);
+}
+
+
+int
+is_switch_policy(struct __nsw_switchconfig *conf, char *target)
+{
+ return (conf && conf->lookups &&
+ strcmp(conf->lookups->service_name, target) == 0 &&
+ conf->lookups->next == NULL);
+}
+
+
+int
+check_switch_policy(char *policy, char *target_service,
+ struct __nsw_switchconfig *default_conf,
+ char *head_msg, char *tail_msg)
+{
+ struct __nsw_switchconfig *conf;
+ enum __nsw_parse_err perr;
+ int policy_correct = 1;
+
+ conf = __nsw_getconfig(policy, &perr);
+ if (no_switch_policy(conf)) {
+ if (!is_switch_policy(default_conf, target_service)) {
+ fprintf(stderr,
+ "\n%s\n There is no publickey entry in %s.\n",
+ head_msg, __NSW_CONFIG_FILE);
+ fprintf(stderr,
+ " The default publickey policy is \"publickey: %s\".\n",
+ switch_policy_str(default_conf));
+ policy_correct = 0;
+ }
+ } else if (!is_switch_policy(conf, target_service)) {
+ fprintf(stderr,
+ "\n%s\n The publickey entry in %s is \"publickey: %s\".\n",
+ head_msg, __NSW_CONFIG_FILE,
+ switch_policy_str(conf));
+ policy_correct = 0;
+ }
+ /* should we exit ? */
+ if (policy_correct == 0)
+ fprintf(stderr,
+ " It should be \"publickey: %s\"%s.\n\n",
+ target_service, tail_msg);
+ if (conf)
+ __nsw_freeconfig(conf);
+
+ return (policy_correct);
+}
+
+/* ******************************************************************** */
+/* Check that data to be entered makes sense */
+int
+sanity_checks(char *nis_princ, char *netname, char *domain, char *authtype)
+{
+ char netdomainaux[MAXHOSTNAMELEN+1];
+ char *princdomain, *netdomain;
+
+ /* Sanity check 0. Do we have a nis+ principal name to work with? */
+ if (nis_princ == NULL) {
+ fprintf(stderr,
+ "%s: you must create a \"local\" credential first.\n",
+ program_name);
+ fprintf(stderr,
+ "rerun this command as : %s local \n", program_name);
+ return (0);
+ }
+
+ /* Sanity check 1. We only deal with one type of netnames. */
+ if (strncmp(netname, OPSYS, OPSYS_LEN) != 0) {
+ fprintf(stderr, "%s: unrecognized netname type: '%s'.\n",
+ program_name, netname);
+ return (0);
+ }
+
+ /* Sanity check 2. Should only add DES cred in home domain. */
+ princdomain = nis_domain_of(nis_princ);
+ if (strcasecmp(princdomain, domain) != 0) {
+ fprintf(stderr,
+"%s: domain of principal '%s' does not match destination domain '%s'.\n",
+ program_name, nis_princ, domain);
+ fprintf(stderr,
+ "Should only add DES credential of principal in its home domain\n");
+ return (0);
+ }
+
+ /*
+ * Sanity check 3: Make sure netname's domain same as principal's
+ * and don't have extraneous dot at the end.
+ */
+ netdomain = (char *)strchr(netname, '@');
+ if (! netdomain) {
+ fprintf(stderr, "%s: invalid netname, missing @: '%s'. \n",
+ program_name, netname);
+ return (0);
+ }
+ if (netname[strlen(netname)-1] == '.')
+ netname[strlen(netname)-1] = '\0';
+ netdomain++; /* skip '@' */
+ strcpy(netdomainaux, netdomain);
+ strcat(netdomainaux, ".");
+
+ if (strcasecmp(princdomain, netdomainaux) != 0) {
+ fprintf(stderr, "%s: domain of netname \"%s\" should "
+ "be same as that of principal \"%s\"\n",
+ program_name, netname, nis_princ);
+ return (0);
+ }
+
+ /* Check publickey policy and warn user if it is not NIS+ */
+ check_switch_policy("publickey", "nisplus", &publickey_default,
+ "WARNING:", " when using NIS+");
+
+ /* Another principal owns same credentials? (exits if that happens) */
+ (void) auth_exists(nis_princ, netname, authtype, domain);
+
+ return (1); /* all passed */
+}
+
+/* ***************************** keylogin stuff *************************** */
+static int
+keylogin(char *netname, char *secret, char *flavor,
+ keylen_t keylen, algtype_t algtype)
+{
+ mechanism_t **mechs;
+ int mcount;
+
+ if (mechs = __nis_get_mechanisms(FALSE)) {
+ for (mcount = 0; mechs[mcount]; mcount++) {
+ if (keylen == mechs[mcount]->keylen &&
+ algtype == mechs[mcount]->algtype) {
+ if (key_setnet_g(netname, secret,
+ mechs[mcount]->keylen,
+ NULL, 0,
+ mechs[mcount]->algtype)
+ < 0) {
+ fprintf(stderr,
+ "Could not set %s's %s secret key\n",
+ netname, flavor);
+ fprintf(stderr,
+ "May be the keyserv is down?\n");
+ return (0);
+ }
+ }
+ }
+ } else {
+ if (keylen == 192 && algtype == 0)
+ return (keylogin_des(netname, secret));
+ }
+ return (1);
+}
+
+/*
+ * fgets is broken in that if it reads a NUL character it will always return
+ * EOF. This replacement can deal with NULs
+ */
+static char *
+fgets_ignorenul(char *s, int n, FILE *stream)
+{
+ int fildes = fileno(stream);
+ int i = 0;
+ int rs = 0;
+ char c;
+
+ if (fildes < 0)
+ return (NULL);
+
+ while (i < n - 1) {
+ rs = read(fildes, &c, 1);
+ switch (rs) {
+ case 1:
+ break;
+ case 0:
+ /* EOF */
+ if (i > 0)
+ s[i] = '\0';
+ return (NULL);
+ break;
+ default:
+ return (NULL);
+ }
+ switch (c) {
+ case '\0':
+ break;
+ case '\n':
+ s[i] = c;
+ s[++i] = '\0';
+ return (s);
+ default:
+ if (c != '\0')
+ s[i++] = c;
+ }
+ }
+ s[i] = '\0';
+ return (s);
+}
+
+#define ROOTKEY_FILE_BACKUP "/etc/.rootkey.bak"
+/* Should last until 16384-bit DH keys */
+#define MAXROOTKEY_LINE_LEN 4224
+#define MAXROOTKEY_LEN 4096
+
+/* write unencrypted secret key into root key file */
+void
+write_rootkey(char *secret, char *flavor, keylen_t keylen, algtype_t algtype)
+{
+ char line[MAXROOTKEY_LINE_LEN];
+ char keyent[MAXROOTKEY_LEN];
+ algtype_t atent;
+ int rootfd, bakfd, hexkeybytes;
+ bool_t lineone = TRUE;
+ bool_t gotit = FALSE;
+ FILE *rootfile, *bakfile;
+
+ unlink(ROOTKEY_FILE_BACKUP);
+ if ((rename(ROOTKEY_FILE, ROOTKEY_FILE_BACKUP)) < 0) {
+ if ((bakfd = creat(ROOTKEY_FILE_BACKUP, 0600)) < 0) {
+ perror("Could not create /etc/.rootkey.bak");
+ goto rootkey_err;
+ }
+ close(bakfd);
+ }
+
+ if ((rootfd = open(ROOTKEY_FILE, O_WRONLY+O_CREAT, 0600)) < 0) {
+ perror("Could not open /etc/.rootkey for writing");
+ fprintf(stderr,
+ "Attempting to restore original /etc/.rootkey\n");
+ rename(ROOTKEY_FILE_BACKUP, ROOTKEY_FILE);
+ goto rootkey_err;
+ }
+ if (!(rootfile = fdopen(rootfd, "w"))) {
+ perror("Could not open /etc/.rootkey for writing");
+ fprintf(stderr,
+ "Attempting to restore original /etc/.rootkey\n");
+ close(rootfd);
+ unlink(ROOTKEY_FILE);
+ rename(ROOTKEY_FILE_BACKUP, ROOTKEY_FILE);
+ goto rootkey_err;
+ }
+ if (!(bakfile = fopen(ROOTKEY_FILE_BACKUP, "r"))) {
+ perror("Could not open /etc/.rootkey.bak for reading");
+ fprintf(stderr,
+ "Attempting to restore original /etc/.rootkey\n");
+ fclose(rootfile);
+ unlink(ROOTKEY_FILE);
+ rename(ROOTKEY_FILE_BACKUP, ROOTKEY_FILE);
+ goto rootkey_err;
+ }
+
+ hexkeybytes = ((keylen + 7) / 8) * 2;
+
+ while (fgets_ignorenul(line, MAXROOTKEY_LINE_LEN, bakfile)) {
+ sscanf(line, "%s %d", keyent, &atent);
+ /*
+ * 192-bit keys always go on the first line
+ */
+ if (lineone) {
+ lineone = FALSE;
+ if (keylen == 192) {
+ gotit = TRUE;
+ fprintf(rootfile, "%s\n", secret);
+ } else
+ fprintf(rootfile, "%s", line);
+ fflush(rootfile);
+ } else {
+ if ((strlen(keyent) == hexkeybytes) &&
+ (atent == algtype)) {
+ /*
+ * Silently remove lines with the same
+ * keylen/algtype
+ */
+ if (gotit)
+ continue;
+ else
+ gotit = TRUE;
+
+ fprintf(rootfile, "%s %d\n", secret, algtype);
+ } else
+ fprintf(rootfile, "%s", line);
+ fflush(rootfile);
+ }
+ }
+
+ /* Append key to rootkey file */
+ if (!gotit) {
+ if (keylen == 192)
+ fprintf(rootfile, "%s\n", secret);
+ else {
+ if (lineone)
+ fprintf(rootfile, "\n");
+ fprintf(rootfile, "%s %d\n", secret, algtype);
+ }
+ }
+ fflush(rootfile);
+ fclose(rootfile);
+ fclose(bakfile);
+ unlink(ROOTKEY_FILE_BACKUP);
+ return;
+
+rootkey_err:
+ fprintf(stderr, "WARNING: Could not write %s key to /etc/.rootkey\n",
+ flavor);
+}
+
+
+char *
+get_password(uid_t uid, int same_host, char *target_host, char *domain)
+{
+ static char password[256];
+ char prompt[256];
+ char *encrypted_password, *login_password = NULL, *pass;
+ struct passwd *pw;
+ int passwords_matched = 0;
+ struct passwd *domain_getpwuid();
+ struct spwd *domain_getspnam();
+
+ struct spwd *spw;
+
+ /* ignore password checking when the -l option is used */
+ if (nispasswd[0] != '\0')
+ return (nispasswd);
+
+ if (uid == 0) {
+ if (same_host) {
+ /*
+ * The root user is never in the NIS+
+ * data base. Get it locally.
+ */
+ pw = getpwuid(0);
+ if (! pw) {
+ fprintf(stderr,
+ "%s: unable to locate password record for uid %d\n",
+ program_name, uid);
+ return (0);
+ }
+ spw = getspnam(pw->pw_name);
+ if (!spw) {
+ fprintf(stderr,
+ "%s: unable to locate password record for uid 0\n",
+ program_name);
+ return (0);
+ }
+ login_password = spw->sp_pwdp;
+ }
+ } else {
+ pw = domain_getpwuid(domain, uid);
+ if (pw) {
+ /* get password from shadow */
+ spw = domain_getspnam(domain, pw->pw_name);
+ if (spw) {
+ login_password = spw->sp_pwdp;
+ }
+ } else {
+ return (0);
+ }
+ }
+
+ if ((uid == my_uid) && ((uid != 0) || same_host))
+ sprintf(prompt, "Enter login password:");
+ else if (uid == 0) {
+ sprintf(prompt, "Enter %s's root login password:",
+ target_host);
+ } else
+ sprintf(prompt, "Enter %s's login password:",
+ pw->pw_name);
+ pass = getpass(prompt);
+ if (strlen(pass) == 0) {
+ (void) fprintf(stderr, "%s: Password unchanged.\n",
+ program_name);
+ return (0);
+ }
+ strcpy(password, pass);
+
+
+ /* Verify that password supplied matches login password */
+ if (login_password && (strlen(login_password) != 0)) {
+ encrypted_password = crypt(password, login_password);
+ if (strcmp(encrypted_password, login_password) == 0)
+ passwords_matched = 1;
+ else {
+ fprintf(stderr,
+ "%s: %s: password differs from login password.\n",
+ program_name, force? "WARNING" : "ERROR");
+ if (!force)
+ return (0);
+ }
+ }
+
+ /* Check for mis-typed password */
+ if (!passwords_matched) {
+ pass = getpass("Retype password:");
+ if (strcmp(password, pass) != 0) {
+ (void) fprintf(stderr, "%s: password incorrect.\n",
+ program_name);
+ return (0);
+ }
+ }
+
+ return (password);
+}
+
+
+/*
+ * Definitions of the credential table.
+ *
+ * Column Name Contents
+ * ------ ---- --------
+ * 0 cname nis principal name
+ * 1 auth_type DES
+ * 2 auth_name netname
+ * 3 public_auth_data public key
+ * 4 private_auth_data encrypted secret key with checksum
+ */
+
+/*
+ * Function for building DES credentials.
+ *
+ * The domain may be the local domain or some remote domain.
+ * 'domain' should be the same as the domain found in netname,
+ * which should be the home domain of nis+ principal.
+ */
+
+int
+make_dhext_cred(nis_princ, netname, domain, flavor)
+ char *nis_princ; /* NIS+ principal name */
+ char *netname; /* AUTH_DES netname */
+ char *domain; /* Domain name */
+ char *flavor; /* Mech alias */
+{
+ nis_object *obj = init_entry();
+ uid_t uid;
+ static char *pass;
+ char *public, *secret, *crypt1;
+ char authtype[MECH_MAXATNAME];
+ char target_host[MAXHOSTNAMELEN+1];
+ int same_host = 0;
+ int status, len, addition;
+ keylen_t kl;
+ algtype_t at;
+ size_t hexkeybytes;
+
+ if (nis_princ == NULL)
+ nis_princ = default_principal(domain);
+
+ if (strcmp(flavor, "dh192-0") == 0)
+ return (make_des_cred_be(nis_princ, netname, domain));
+
+ __nis_mechalias2authtype(flavor, authtype, MECH_MAXATNAME);
+
+ if (__nis_translate_mechanism(flavor, &kl, &at) == -1)
+ goto badstatus;
+
+ if (sanity_checks(nis_princ, netname, domain, authtype) == 0)
+ goto badstatus;
+
+ addition = (cred_exists(nis_princ, authtype, domain) == NIS_NOTFOUND);
+
+ /* Extract user/host information from netname */
+ if (! isdigit(netname[OPSYS_LEN+1])) {
+ uid = 0; /* root */
+ netname2host(netname, target_host, MAXHOSTNAMELEN);
+ len = strlen(my_host)-1; /* ignore trailing dot in my_host */
+ if (len == strlen(target_host) &&
+ strncasecmp(target_host, my_host, len) == 0)
+ same_host = 1;
+ } else {
+ uid = (uid_t)atoi(netname+OPSYS_LEN+1);
+ }
+
+ if (!pass)
+ pass = get_password(uid, same_host, target_host, domain);
+ if (!pass)
+ goto badstatus;
+
+ /* Get password with which to encrypt secret key. */
+ (void) printf("%s %s key pair for %s (%s).\n",
+ addition? "Adding" : "Updating", flavor,
+ netname, nis_princ);
+
+ hexkeybytes = ((kl + 7) / 8) * 2;
+ if (!(public = (char *)malloc(hexkeybytes + 1)))
+ goto badstatus;
+ if (!(secret = (char *)malloc(hexkeybytes + 1)))
+ goto badstatus;
+
+ /* Encrypt secret key */
+ if (!(__gen_dhkeys_g(public, secret, kl, at, pass)))
+ goto badstatus;
+ if (!(xencrypt_g(secret, kl, at, pass, netname, &crypt1, TRUE)))
+ goto badstatus;
+
+ /* Now we have a key pair, build up the cred entry */
+ ENTRY_VAL(obj, 0) = nis_princ;
+ ENTRY_LEN(obj, 0) = strlen(nis_princ) + 1;
+
+ ENTRY_VAL(obj, 1) = authtype;
+ ENTRY_LEN(obj, 1) = strlen(authtype) + 1;
+
+ ENTRY_VAL(obj, 2) = netname;
+ ENTRY_LEN(obj, 2) = strlen(netname) + 1;
+
+ ENTRY_VAL(obj, 3) = public;
+ ENTRY_LEN(obj, 3) = strlen(public) + 1;
+
+ ENTRY_VAL(obj, 4) = crypt1;
+ ENTRY_LEN(obj, 4) = strlen(crypt1) + 1;
+
+ if (addition) {
+ obj->zo_owner = nis_princ;
+ obj->zo_group = my_group;
+ obj->zo_domain = domain;
+ /* owner: r, group: rmcd */
+ obj->zo_access = ((NIS_READ_ACC<<16)|
+ (NIS_READ_ACC|NIS_MODIFY_ACC|NIS_CREATE_ACC|
+ NIS_DESTROY_ACC)<<8);
+ status = add_cred_obj(obj, domain);
+ } else {
+ obj->EN_data.en_cols.en_cols_val[3].ec_flags |= EN_MODIFIED;
+ obj->EN_data.en_cols.en_cols_val[4].ec_flags |= EN_MODIFIED;
+ status = modify_cred_obj(obj, domain);
+ }
+
+
+ /* attempt keylogin if appropriate */
+ if (status) {
+ if ((uid == my_uid) && ((uid != 0) || same_host))
+ keylogin(netname, secret, flavor, kl, at);
+ if ((uid == 0) && same_host)
+ write_rootkey(secret, flavor, kl, at);
+ }
+
+ goto cleanup;
+badstatus:
+ status = 0;
+
+cleanup:
+
+ return (status);
+}
+
+
+char *
+get_dhext_cred(char *domain, char *flavor)
+{
+ int uid, status;
+ static char netname[MAXNETNAMELEN+1];
+
+ uid = my_uid;
+
+ if (uid == 0)
+ status = host2netname(netname, (char *)NULL, domain);
+ else {
+ /* generate netname using uid and domain information. */
+ int len;
+ len = strlen(domain);
+ if ((len + OPSYS_LEN + 3 + MAXIPRINT) > MAXNETNAMELEN) {
+ printf("Domain name too long: \"%s\"\n", domain);
+ goto not_found;
+ }
+ (void) sprintf(netname, "%s.%d@%s", OPSYS, uid, domain);
+ len = strlen(netname);
+ if (netname[len-1] == '.')
+ netname[len-1] = '\0';
+
+ status = 1;
+ }
+
+ if (status == 1) {
+ printf("DES principal name : \"%s\"\n", netname);
+ return (netname);
+ }
+
+not_found:
+ printf("DES principal name for %d not found\n", uid);
+ return (NULL);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makekerbcred.c b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makekerbcred.c
new file mode 100644
index 0000000000..3ea21a7d5d
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makekerbcred.c
@@ -0,0 +1,47 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * makekerbcred.c
+ *
+ * Copyright (c) 1988-1992 Sun Microsystems Inc
+ * All Rights Reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+int
+make_kerb_cred(np, p, d, flavor)
+ char *np;
+ char *p;
+ char *d;
+ char *flavor;
+{
+ return (0);
+}
+
+char *
+get_kerb_cred(domain, flavor)
+char *domain;
+char *flavor;
+{
+ return (0);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makelocalcred.c b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makelocalcred.c
new file mode 100644
index 0000000000..be352ed3dd
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makelocalcred.c
@@ -0,0 +1,296 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * makelocalcred.c
+ *
+ * Copyright (c) 1988-1992 Sun Microsystems Inc
+ * All Rights Reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * makelocalcred.c
+ *
+ * Make a "local" credential. The local credential is used to map from
+ * a UID in the nis_local_directory() to a principal name in some other
+ * NIS+ directory. Needless to say the principal name is required and
+ * this function must be run as the NIS+ administrator.
+ */
+
+#include <stdio.h>
+#include <pwd.h>
+#include <limits.h>
+#include <rpcsvc/nis.h>
+#include "nisaddcred.h"
+
+/*
+ * _getgroupsbymember(uname, gid_array, maxgids, numgids):
+ * This function can be found in libc/port/gen/getgrnam_r.c.
+ * It's a private interface mainly for initgroups(). It returns the
+ * group ids of groups of which the specified user is a member.
+ */
+extern int _getgroupsbymember(const char *, gid_t[], int, int);
+
+#define EVAL(e, c) (ENTRY_LEN(e, c) > 0 ? ENTRY_VAL(e, c) : "")
+
+struct cback_info {
+ char *username;
+ int gidcnt;
+ int maxgids;
+ gid_t *gidlist;
+};
+
+static
+int
+add_gidval(info, gid)
+ struct cback_info *info;
+ gid_t gid;
+{
+ int i;
+
+ for (i = 0; i < info->gidcnt; i++) {
+ if (info->gidlist[i] == gid)
+ return (1); /* don't insert dup, but not error */
+ }
+ if (info->gidcnt >= info->maxgids)
+ return (0); /* no room */
+
+ info->gidlist[info->gidcnt] = gid;
+ info->gidcnt++;
+
+ return (1);
+}
+
+int
+cback(table, entry, udata)
+ nis_name table;
+ nis_object *entry;
+ void *udata;
+{
+ struct cback_info *info = (struct cback_info *)udata;
+ char *username = info->username;
+ int len;
+ char *p;
+ char *members;
+ char *gid;
+ char *gname;
+ int gidval;
+
+ len = strlen(username);
+
+ members = EVAL(entry, 3);
+ while (*members) {
+ while (*members && isspace(*members))
+ members++;
+ if (*members == '\0')
+ break;
+
+ p = members;
+ while (*members && *members != ',')
+ members++;
+
+ if (members - p == len && strncmp(p, username, len) == 0) {
+ gid = EVAL(entry, 2);
+ if (isdigit(gid[0])) {
+ gidval = atoi(gid);
+ if (! add_gidval(info, gidval))
+ return (1); /* no more room */
+ }
+ }
+
+ if (*members == ',')
+ members++;
+ }
+
+ return (0);
+}
+
+/*
+ * Get groups from an NIS+ domain. If that doesn't yield any
+ * groups and the domain was not specified on the command line,
+ * then we try the local routines for getting group ids.
+ */
+static
+int
+__getnisgroupsbymember(domain, uid, username, basegid, maxgids, gidlist)
+ char *domain;
+ uid_t uid;
+ char *username;
+ gid_t basegid;
+ int maxgids;
+ gid_t *gidlist;
+{
+ struct cback_info info;
+ nis_result *res;
+ char name[1024];
+ u_long flags = EXPAND_NAME|MASTER_ONLY|FOLLOW_PATH|FOLLOW_LINKS;
+
+ info.username = username;
+ info.gidcnt = 0;
+ info.maxgids = NGROUPS_MAX;
+ info.gidlist = gidlist;
+
+ add_gidval(&info, basegid);
+
+ sprintf(name, "group.org_dir.%s", domain);
+ res = nis_list(name, flags, cback, (void *)&info);
+ nis_freeresult(res);
+
+#ifdef USE_LOCAL_INFO
+ /*
+ * If no domain was specified on the command line and we didn't get
+ * any extra gids from NIS+, then try getting them locally.
+ */
+ if (! explicit_domain && info.gidcnt == 1) {
+ if (uid == my_uid)
+ info.gidcnt = getgroups(info.maxgids, info.gidlist);
+ else
+ info.gidcnt = _getgroupsbymember(username, info.gidlist,
+ info.maxgids, 1);
+
+ /* if getgroups failed, put basegid back in list */
+ if (info.gidcnt <= 0) {
+ info.gidcnt = 0;
+ add_gidval(&info, basegid);
+ }
+ }
+#endif /* USE_LOCAL_INFO */
+
+ return (info.gidcnt);
+}
+
+int
+make_local_cred(nisprinc, uidstr, domain, flavor)
+ char *nisprinc;
+ char *uidstr;
+ char *domain;
+ char *flavor; /* Ignored. */
+{
+ nis_object *obj = init_entry();
+ nis_error err;
+ int i, gidlen;
+ uid_t uid;
+ gid_t gidlist[NGROUPS_MAX];
+ struct passwd *pw;
+ char nisname[NIS_MAXNAMELEN+1],
+ pdata[NIS_MAXATTRVAL+1],
+ gidstr[MAXIPRINT+1];
+ int status, addition;
+ struct passwd *domain_getpwuid();
+
+ if (!isdigit(*uidstr)) {
+ fprintf(stderr,
+ "%s: invalid local principal '%s' (must be number)\n",
+ program_name, uidstr);
+ return (0);
+ }
+
+ uid = (uid_t)atoi(uidstr);
+ if (uid == 0) {
+ fprintf(stderr, "%s: need not add LOCAL entry for root\n",
+ program_name);
+ return (0);
+ }
+
+ pw = domain_getpwuid(domain, uid);
+ if (!pw)
+ return (0);
+
+ if (nisprinc == 0)
+ sprintf(nisname, "%s.%s", pw->pw_name, domain);
+ else
+ strcpy(nisname, nisprinc);
+
+ /* Another principal owns same credentials? (exits if that happens) */
+ (void) auth_exists(nisname, uidstr, "LOCAL", domain);
+
+ addition = (cred_exists(nisname, "LOCAL", domain) == NIS_NOTFOUND);
+
+ /* build up list of group ids */
+ if ((gidlen = __getnisgroupsbymember(domain, uid,
+ pw->pw_name, pw->pw_gid, NGROUPS_MAX, gidlist)) <= 0) {
+ fprintf(stderr,
+ "Failed to get group information for user %s\n", pw->pw_name);
+ return (0);
+ }
+ pdata[0] = '\0';
+ for (i = 0; i < gidlen; i++) {
+ if ((i + 1) < gidlen)
+ sprintf(gidstr, "%d,", gidlist[i]);
+ else
+ sprintf(gidstr, "%d", gidlist[i]);
+ strcat(pdata, gidstr);
+ }
+
+ ENTRY_VAL(obj, 0) = nisname;
+ ENTRY_LEN(obj, 0) = strlen(nisname) + 1;
+
+ ENTRY_VAL(obj, 1) = "LOCAL";
+ ENTRY_LEN(obj, 1) = 6;
+
+ ENTRY_VAL(obj, 2) = uidstr;
+ ENTRY_LEN(obj, 2) = strlen(uidstr)+1;
+
+ ENTRY_VAL(obj, 3) = pdata;
+ ENTRY_LEN(obj, 3) = strlen(pdata) + 1;
+
+ ENTRY_VAL(obj, 4) = "";
+ ENTRY_LEN(obj, 4) = 0;
+
+ if (addition) {
+ obj->zo_owner = my_nisname;
+ obj->zo_group = my_group;
+ obj->zo_domain = domain;
+ /* owner: rmcd, group: rmcd */
+ obj->zo_access = ((NIS_READ_ACC|NIS_MODIFY_ACC|
+ NIS_CREATE_ACC|NIS_DESTROY_ACC)<<8) |
+ ((NIS_READ_ACC|NIS_MODIFY_ACC|
+ NIS_CREATE_ACC|NIS_DESTROY_ACC)<<16);
+ status = add_cred_obj(obj, domain);
+ } else {
+ /* columns that could have changed */
+ obj->EN_data.en_cols.en_cols_val[2].ec_flags |= EN_MODIFIED;
+ obj->EN_data.en_cols.en_cols_val[3].ec_flags |= EN_MODIFIED;
+
+ status = modify_cred_obj(obj, domain);
+ }
+ return (status);
+}
+
+
+
+
+/*
+ * Return a string representation of the "LOCAL" authentication name.
+ * In this case it is easy, its our uid.
+ */
+char *
+get_local_cred(domain, flavor)
+ char *domain; /* ignored for local case */
+ char *flavor; /* ignored */
+{
+ static char myname[MAXIPRINT];
+
+ sprintf(myname, "%d", my_uid);
+ return (myname);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makersacred.c b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makersacred.c
new file mode 100644
index 0000000000..2f6b66c82b
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makersacred.c
@@ -0,0 +1,47 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * makersacred.c
+ *
+ * Copyright (c) 1988-1992 Sun Microsystems Inc
+ * All Rights Reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+int
+make_rsa_cred(np, p, d, flavor)
+ char *np;
+ char *p;
+ char *d;
+ char *flavor;
+{
+ return (0);
+}
+
+char *
+get_rsa_cred(domain, flavor)
+char *domain;
+char *flavor;
+{
+ return (0);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makesyscred.c b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makesyscred.c
new file mode 100644
index 0000000000..32d21ea780
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/makesyscred.c
@@ -0,0 +1,191 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * makesyscred.c
+ *
+ * This module makes the "AUTH_SYS" credential and stores it in the credential
+ * table.
+ */
+
+#include <stdio.h>
+#include <pwd.h>
+#include <rpcsvc/nis.h>
+#include "nisaddcred.h"
+
+extern char *strchr();
+
+int
+make_sys_cred(np, p, d, flavor)
+ char *np;
+ char *p;
+ char *d;
+ char *flavor; /* Ignored. */
+{
+ nis_result *res;
+ nis_object *obj = init_entry();
+ uid_t uid;
+ gid_t gid;
+ int grplist[32];
+ int ngrps, i;
+ struct passwd *pw;
+ char sysname[32],
+ *s,
+ luid[16],
+ lgids[64],
+ buf[16],
+ pname[1024],
+ tname[1024];
+ nis_error err;
+
+ uid = atoi(p);
+
+ if ((uid != geteuid()) && (geteuid() != 0)) {
+ fprintf(stderr, "must be root to add anothers credentials.\n");
+ return (0);
+ }
+
+ s = strchr(p, ',');
+ if (! s) {
+ fprintf(stderr, "badly formed AUTH_SYS principal name.\n");
+ return (0);
+ }
+ s++;
+ gid = atoi(s);
+
+ pw = getpwuid(uid);
+ if (! pw) {
+ /*
+ * If NIS+ is the name service for the passwd data,
+ * maybe this answer came from an out of date replica
+ * server. So lets try the NIS+ Master server.
+ */
+ pw = getpwuid_nisplus_master(uid, &err);
+ if (pw == NULL) {
+ if (err == NIS_NOTFOUND)
+ fprintf(stderr,
+ "%s: no password entry for uid %d\n",
+ program_name, uid);
+ else
+ fprintf(stderr,
+ "%s: could not get the password entry for uid %d: %s\n",
+ program_name, uid, nis_sperrno(err));
+ return (0);
+ }
+ }
+
+ ENTRY_VAL(obj, 0) = np;
+ ENTRY_LEN(obj, 0) = strlen(np) + 1;
+
+ ENTRY_VAL(obj, 1) = "SYS";
+ ENTRY_LEN(obj, 1) = 4;
+
+ ENTRY_VAL(obj, 2) = p;
+ ENTRY_LEN(obj, 2) = strlen(p)+1;
+
+ if ((uid != geteuid()) && (pw->pw_uid != uid)) {
+ ngrps = initgroups(pw->pw_name, pw->pw_gid);
+ if (ngrps == -1) {
+ perror("initgroups:");
+ return (0);
+ }
+ }
+ ngrps = getgroups(32, grplist);
+ if (ngrps == -1) {
+ perror("getgroups:");
+ return (0);
+ }
+
+ lgids[0] = '\0';
+ for (i = 0; i < ngrps; i++) {
+ if (i+1 < ngrps)
+ sprintf(buf, "%d,", grplist[i]);
+ else
+ sprintf(buf, "%d", grplist[i]);
+ strcat(lgids, buf);
+ }
+
+ ENTRY_VAL(obj, 3) = lgids;
+ ENTRY_LEN(obj, 3) = strlen(lgids) + 1;
+
+ ENTRY_VAL(obj, 4) = ""; /* no private data */
+ ENTRY_LEN(obj, 4) = 0;
+
+ sprintf(tname, "%s.%s", CRED_TABLE, d);
+ obj->zo_owner = pname;
+ obj->zo_domain = d;
+ res = nis_add_entry(tname, obj, 0);
+ switch (res->status) {
+ case NIS_TRYAGAIN :
+ fprintf(stderr, "NIS+ server busy, try again later.\n");
+ i = 0;
+ break;
+ case NIS_PERMISSION :
+ fprintf(stderr,
+ "Insufficent permission to update/create credentials\n");
+ i = 0;
+ break;
+ case NIS_SUCCESS :
+ i = 1;
+ break;
+ default :
+ fprintf(stderr, "Error creating credential, NIS+ error %s\n",
+ nis_sperrno(res->status));
+ break;
+ }
+ nis_freeresult(res);
+ return (i);
+}
+
+char *
+get_sys_cred(char *domain, /* Ignored. */
+ char *flavor) /* Ignored. */
+{
+ static char myname[64];
+ struct passwd *pw;
+ int uid;
+ nis_error err;
+
+ uid = geteuid();
+ pw = getpwuid(uid);
+ if (! pw) {
+ /*
+ * If NIS+ is the name service for the passwd data,
+ * maybe this answer came from an out of date replica
+ * server. So lets try the NIS+ Master server.
+ */
+ pw = getpwuid_nisplus_master(uid, &err);
+ if (pw == NULL) {
+ if (err == NIS_NOTFOUND)
+ fprintf(stderr,
+ "%s: no password entry for uid %d\n",
+ program_name, uid);
+ else
+ fprintf(stderr,
+ "%s: could not get the password entry for uid %d: %s\n",
+ program_name, uid, nis_sperrno(err));
+ return (NULL);
+ }
+ }
+
+ sprintf(myname, "%d,%d", pw->pw_uid, pw->pw_gid);
+ return (myname);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/nisaddcred.c b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/nisaddcred.c
new file mode 100644
index 0000000000..110fde9654
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/nisaddcred.c
@@ -0,0 +1,742 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * nisaddcred.c
+ *
+ * Copyright 1988-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * nisaddcred.c
+ *
+ * This utility is used to add credentials for a user to the NIS+ databases.
+ * Its syntax is as follows :
+ * nisaddcred [-p principal] [-P nis+_principal] [-l login_passwd] flavor
+ * nisaddcred -r [nis+_principal]
+ */
+
+/*
+ * The code is set up so that it may be possible to pass 'domain'
+ * as an optional argument. However, it is not clear whether it
+ * makes any sense to addcreds to other domains, and whether
+ * the code itself would work.
+ */
+
+#include <stdio.h>
+#include <malloc.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/systeminfo.h>
+#include <pwd.h>
+#include <limits.h>
+#include <rpcsvc/nis.h>
+#include <rpcsvc/nis_dhext.h>
+#define CRED_TABLE "cred.org_dir"
+
+void
+usage(cmd)
+ char *cmd;
+{
+ fprintf(stderr,
+"usage:\n\t%s [-p principal] [-P NIS+_principal] [-l login_password] ",
+ cmd);
+ fprintf(stderr, "flavor\n");
+ fprintf(stderr,
+ "\t%s -r [NIS+_principal]\n", cmd);
+ exit(1);
+}
+
+extern int make_des_cred(), make_kerb_cred(),
+ make_rsa_cred(), make_local_cred(), make_dhext_cred();
+extern char *get_des_cred(), *get_kerb_cred(),
+ *get_rsa_cred(), *get_local_cred(), *get_dhext_cred();
+
+static
+struct {
+ char *name;
+ int (*makecred)();
+ char *(*pname)();
+} known_flavors[] = {
+ { "local", make_local_cred, get_local_cred }, /* LOCAL ALWAYS 0! */
+ { "des", make_des_cred, get_des_cred },
+#ifdef KERB_RSA_CREDS
+ { "kerb", make_kerb_cred, get_kerb_cred },
+ { "rsa", make_rsa_cred, get_rsa_cred },
+#endif /* KERB_RSA_CREDS */
+ { NULL, NULL}};
+
+extern int optind;
+extern char *optarg;
+extern nis_name nis_local_host();
+
+char *program_name = "nisaddcred";
+uid_t my_uid;
+nis_name my_nisname = 0;
+char *my_host;
+char *my_group;
+char nispasswd[sizeof (des_block)+1];
+int explicit_domain; /* if true, then a domain was specified on command line */
+
+/*
+ * Similar to nis_local_principal (nis_subr.c) except
+ * this gets the results from the MASTER_ONLY and no FOLLOW_PATH.
+ * We only want the master because we'll be making updates there,
+ * and also the replicas may not have seen the 'nisaddacred local'
+ * that may have just occurred.
+ * Returns NULL if not found.
+ */
+char *
+default_principal(directory)
+char *directory;
+{
+ nis_result *res;
+ char buf[NIS_MAXNAMELEN+1];
+ static char principal_name[NIS_MAXNAMELEN+1];
+ uid_t uid;
+
+
+ uid = my_uid;
+
+ if (uid == 0)
+ return (nis_local_host());
+
+ sprintf(buf, "[auth_name=%d,auth_type=LOCAL],%s.%s",
+ uid, CRED_TABLE, directory);
+
+ if (buf[strlen(buf)-1] != '.')
+ strcat(buf, ".");
+
+ res = nis_list(buf, MASTER_ONLY+USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS,
+ NULL, NULL);
+
+ if (res == NULL) {
+ fprintf(stderr,
+ "%s: unable to get result from NIS+ server.",
+ program_name);
+ exit(1);
+ }
+ switch (res->status) {
+ case NIS_SUCCESS:
+ if (res->objects.objects_len > 1) {
+ /*
+ * More than one principal with same uid?
+ * something wrong with cred table. Should be unique
+ * Warn user and continue.
+ */
+ fprintf(stderr,
+ "%s: LOCAL entry for %d in directory \"%s\" not unique",
+ program_name, uid, directory);
+ }
+ strcpy(principal_name, ENTRY_VAL(res->objects.objects_val, 0));
+ nis_freeresult(res);
+ return (principal_name);
+
+ case NIS_NOTFOUND:
+ nis_freeresult(res);
+ return (NULL);
+
+ case NIS_TRYAGAIN :
+ fprintf(stderr,
+ "%s: NIS+ server busy, try again later.\n",
+ program_name);
+ exit(1);
+
+ case NIS_PERMISSION :
+ fprintf(stderr,
+ "%s: insufficent permission to update credentials.\n",
+ program_name);
+ exit(1);
+
+ default:
+ fprintf(stderr,
+ "%s: error talking to server, NIS+ error: %s.\n",
+ program_name, nis_sperrno(res->status));
+ exit(1);
+ }
+ return (NULL);
+}
+
+/* Check whether this principal already has this type of credentials */
+nis_error
+cred_exists(char *nisprinc, char *flavor, char *domain)
+{
+ char sname[NIS_MAXNAMELEN+1];
+ nis_result *res;
+ nis_error status;
+
+ sprintf(sname, "[cname=\"%s\",auth_type=%s],%s.%s",
+ nisprinc, flavor, CRED_TABLE, domain);
+ if (sname[strlen(sname)-1] != '.')
+ strcat(sname, ".");
+
+ /* Don't want FOLLOW_PATH here */
+ res = nis_list(sname, MASTER_ONLY+USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS,
+ NULL, NULL);
+
+ status = res->status;
+ switch (status) {
+ case NIS_NOTFOUND:
+ break;
+ case NIS_TRYAGAIN :
+ fprintf(stderr, "%s: NIS+ server busy, try again later.\n",
+ program_name);
+ exit(1);
+ case NIS_PERMISSION :
+ fprintf(stderr,
+ "%s: insufficent permission to look at credentials table\n",
+ program_name);
+ exit(1);
+ case NIS_SUCCESS:
+ case NIS_S_SUCCESS:
+ break;
+ default:
+ fprintf(stderr,
+ "%s: error looking at cred table, NIS+ error: %s\n",
+ program_name, nis_sperrno(res->status));
+ exit(1);
+ }
+ nis_freeresult(res);
+ return (status);
+}
+
+/* Check that someone else don't have the same auth information already */
+nis_error
+auth_exists(char *princname, char *auth_name, char *auth_type, char *domain)
+{
+ char sname[NIS_MAXNAMELEN+1];
+ nis_result *res;
+ nis_error status;
+ char *foundprinc;
+
+ sprintf(sname, "[auth_name=%s,auth_type=%s],%s.%s",
+ auth_name, auth_type, CRED_TABLE, domain);
+ if (sname[strlen(sname)-1] != '.')
+ strcat(sname, ".");
+ /* Don't want FOLLOW_PATH here */
+ res = nis_list(sname, MASTER_ONLY+USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS,
+ NULL, NULL);
+
+ status = res->status;
+ switch (res->status) {
+ case NIS_NOTFOUND:
+ break;
+ case NIS_TRYAGAIN :
+ fprintf(stderr,
+ "%s: NIS+ server busy, try again later.\n",
+ program_name);
+ exit(1);
+ case NIS_PERMISSION :
+ fprintf(stderr,
+ "%s: insufficent permission to look up old credentials.\n",
+ program_name);
+ exit(1);
+ case NIS_SUCCESS:
+ foundprinc = ENTRY_VAL(res->objects.objects_val, 0);
+ if (strcmp(foundprinc, princname) != 0) {
+ fprintf(stderr,
+ "%s: %s credentials with auth_name '%s' already belongs to '%s'.\n",
+ program_name, auth_type, auth_name, foundprinc);
+ exit(1);
+ }
+ break;
+ default:
+ fprintf(stderr,
+ "%s: error looking at cred table, NIS+ error: %s\n",
+ program_name, nis_sperrno(res->status));
+ exit(1);
+ }
+ nis_freeresult(res);
+ return (status);
+}
+
+int
+modify_cred_obj(obj, domain)
+ char *domain;
+ nis_object *obj;
+{
+ int status = 0;
+ char sname[NIS_MAXNAMELEN+1];
+ nis_result *res;
+
+ sprintf(sname, "%s.%s", CRED_TABLE, domain);
+ res = nis_modify_entry(sname, obj, 0);
+ switch (res->status) {
+ case NIS_TRYAGAIN :
+ fprintf(stderr,
+ "%s: NIS+ server busy, try again later.\n",
+ program_name);
+ exit(1);
+ case NIS_PERMISSION :
+ fprintf(stderr,
+ "%s: insufficent permission to update credentials.\n",
+ program_name);
+ exit(1);
+ case NIS_SUCCESS :
+ status = 1;
+ break;
+ default:
+ fprintf(stderr,
+ "%s: error creating credential, NIS+ error: %s.\n",
+ program_name, nis_sperrno(res->status));
+ exit(1);
+ }
+ nis_freeresult(res);
+ return (status);
+}
+
+
+int
+add_cred_obj(obj, domain)
+ char *domain;
+ nis_object *obj;
+{
+ int status = 0;
+ char sname[NIS_MAXNAMELEN+1];
+ nis_result *res;
+
+ /* Assume check for cred_exists performed already */
+
+ sprintf(sname, "%s.%s", CRED_TABLE, domain);
+ res = nis_add_entry(sname, obj, 0);
+ switch (res->status) {
+ case NIS_TRYAGAIN :
+ fprintf(stderr,
+ "%s: NIS+ server busy, try again later.\n",
+ program_name);
+ exit(1);
+ case NIS_PERMISSION :
+ fprintf(stderr,
+ "%s: insufficent permission to update credentials.\n",
+ program_name);
+ exit(1);
+ case NIS_SUCCESS :
+ status = 1;
+ break;
+ default:
+ fprintf(stderr,
+ "%s: error creating credential, NIS+ error: %s.\n",
+ program_name, nis_sperrno(res->status));
+ exit(1);
+ }
+ nis_freeresult(res);
+ return (status);
+}
+
+void
+unknown_flavor(char *flavor)
+{
+ mechanism_t **mechlist;
+ int i;
+
+ mechlist = __nis_get_mechanisms(FALSE);
+ fprintf(stderr, "%s: unknown flavor '%s'\n", program_name, flavor);
+
+ fprintf(stderr, "known flavors are: ");
+ for (i = 0; known_flavors[i].name; i++)
+ if (known_flavors[i+1].name)
+ fprintf(stderr, "%s, ", known_flavors[i].name);
+ else {
+ if (mechlist &&
+ !(AUTH_DES_COMPAT_CHK(mechlist[0]) &&
+ !mechlist[1])) {
+ int count;
+
+ fprintf(stderr, "%s, ", known_flavors[i].name);
+ for (count = 0; mechlist[count]; count++) {
+ if (mechlist[count+1])
+ fprintf(stderr, "%s\n",
+ mechlist[count]->alias);
+ else
+ fprintf(stderr, "%s.\n",
+ mechlist[count]->alias);
+ }
+ __nis_release_mechanisms(mechlist);
+ } else
+ fprintf(stderr, "%s.\n",
+ known_flavors[i].name);
+ }
+ exit(1);
+}
+
+void
+perform_add(flavor, domain, nisprinc, princ)
+ char *flavor;
+ char *domain;
+ char *nisprinc;
+ char *princ;
+{
+ int i, status, fl;
+ int (*mc)();
+ char *(*gp)();
+
+ fl = strlen(flavor);
+ for (i = 0; i < fl; i++)
+ *(flavor+i) = tolower(*(flavor+i));
+
+ /* See if given flavor is one that we know */
+ for (i = 0, mc = NULL; !mc && known_flavors[i].name; i++)
+ if (strcmp(known_flavors[i].name, flavor) == 0) {
+ mc = known_flavors[i].makecred;
+ gp = known_flavors[i].pname;
+ break;
+ }
+
+ if (! mc) {
+ keylen_t kl;
+ algtype_t at;
+
+ if (strcmp(flavor, "dh192-0")) {
+ /* It could be a GSS/DHEXT type */
+ if (__nis_translate_mechanism(flavor, &kl, &at) == -1)
+ unknown_flavor(flavor);
+ } else {
+ mc = make_dhext_cred;
+ gp = get_des_cred;
+ }
+
+ mc = make_dhext_cred;
+ gp = get_dhext_cred;
+ }
+
+ /*
+ * Call the function that will build the appropriate credential.
+ * When called, both the principal *and* the domain must be specified.
+ * some functions will complain if the domain and the domain of the
+ * principal (if fully qualified) differ.
+ */
+ if (! princ) {
+ princ = (*gp)(domain, flavor);
+ if (princ == NULL) {
+ fprintf(stderr,
+ "%s: unable to determine your principal name for this flavor.\n",
+ program_name);
+ exit(1);
+ }
+ }
+
+ status = (*mc)(nisprinc, princ, domain, flavor);
+ if (! status) {
+ fprintf(stderr, "%s: unable to create credential.\n",
+ program_name);
+ exit(1);
+ }
+}
+
+void
+perform_remove(domain, nis_princ)
+char *domain;
+char *nis_princ;
+{
+ char sname[NIS_MAXNAMELEN+1];
+ nis_result *res;
+
+ if (nis_princ == NULL)
+ nis_princ = default_principal(domain);
+
+ sprintf(sname, "[cname=\"%s\"],%s.%s", nis_princ, CRED_TABLE, domain);
+ if (sname[strlen(sname)-1] != '.')
+ strcat(sname, ".");
+ res = nis_remove_entry(sname, 0, REM_MULTIPLE);
+
+ if (res->status != NIS_SUCCESS) {
+ nis_perror(res->status, "could not remove entry");
+ exit(1);
+ }
+ nis_freeresult(res);
+
+/* Should do keylogout here if removing my own des credentials */
+}
+
+
+nis_object *
+init_entry()
+{
+ static nis_object obj;
+ static entry_col cred_data[10];
+ entry_obj *eo;
+
+ memset((char *)(&obj), 0, sizeof (obj));
+ memset((char *)(cred_data), 0, sizeof (entry_col) * 10);
+
+ obj.zo_name = "cred";
+ obj.zo_group = "";
+ obj.zo_ttl = 43200;
+ obj.zo_data.zo_type = NIS_ENTRY_OBJ;
+ eo = &(obj.EN_data);
+ eo->en_type = "cred_tbl";
+ eo->en_cols.en_cols_val = cred_data;
+ eo->en_cols.en_cols_len = 5;
+ cred_data[4].ec_flags |= EN_CRYPT;
+ return (&obj);
+}
+
+void
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *princ = NULL;
+ char *nisprinc = NULL;
+ char *domain = nis_local_directory();
+ char *flavor = NULL;
+ int len;
+ int c;
+ char *p;
+ int add_op = 1; /* whether this operation is an add */
+
+ program_name = argv[0];
+ nispasswd[0] = '\0';
+
+ if (argc == 1)
+ usage(program_name);
+
+ while ((c = getopt(argc, argv, "p:P:l:r")) != -1) {
+ switch (c) {
+ case 'p' :
+ if (add_op == 0) {
+ fprintf(stderr,
+ "%s: cannot combine any other option with -r.\n",
+ program_name);
+ usage(program_name);
+ }
+ princ = optarg;
+ break;
+ case 'P' :
+ if (add_op == 0) {
+ fprintf(stderr,
+ "%s: cannot combine any other option with -r.\n",
+ program_name);
+ usage(program_name);
+ }
+ nisprinc = optarg;
+ break;
+ case 'r':
+ if ((princ != NULL) || (nisprinc != NULL) ||
+ (nispasswd[0] != '\0')) {
+ fprintf(stderr,
+ "%s: cannot combine any other option with -r.\n",
+ program_name);
+ usage(program_name);
+ }
+ add_op = 0;
+ if ((argc == 2) && getuid() == 0) {
+ struct stat buf;
+ int ret;
+ char *ptr, ansbuf[BUFSIZ];
+
+ ret = stat("/var/nis/data", &buf);
+ if (ret == 0) {
+ fputs("\n\tThis machine appears to be a NIS+ server!!!\n",
+ stderr);
+ fputs("Are you sure you want to REMOVE it's credential?(Y/N) ",
+ stderr);
+ ansbuf[0] = '\0';
+ ptr = gets(ansbuf);
+ if (ansbuf[0] != 'y' &&
+ ansbuf[0] != 'Y') {
+ fputs("\nOkay not REMOVING this NIS+ server's credential\n",
+ stderr);
+ exit(1);
+ }
+ }
+ }
+ break;
+ case 'l':
+ if (add_op == 0) {
+ fprintf(stderr,
+ "%s: cannot combine any other option with -r.\n",
+ program_name);
+ usage(program_name);
+ }
+ strncpy(nispasswd, optarg, sizeof (des_block));
+ nispasswd[sizeof (des_block)] = '\0';
+ break;
+ case '?' :
+ default :
+ fprintf(stderr, "%s: unrecognized option.\n",
+ program_name);
+ usage(program_name);
+ break;
+ }
+ }
+
+
+ if (add_op) {
+ if (optind == argc) {
+ fprintf(stderr,
+ "%s: Authentication Flavor name required.\n",
+ program_name);
+ usage(program_name);
+ }
+ flavor = argv[optind++];
+
+ if (optind < argc) {
+ domain = argv[optind++];
+ explicit_domain++;
+ }
+ } else {
+ if (optind < argc)
+ nisprinc = argv[optind++];
+ if (optind < argc) {
+ domain = argv[optind++];
+ explicit_domain++;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr, "%s: too many parameters\n", program_name);
+ usage(program_name);
+ }
+
+ /* Information about user running nisaddcred */
+ my_uid = geteuid();
+ my_nisname = default_principal(nis_local_directory());
+ my_host = nis_local_host();
+ my_group = nis_local_group();
+
+ /*
+ * Do a centralized check of nisprinc to make sure that
+ * it has a trailing ".", which is a common mistake that
+ * we can easily correct.
+ */
+ if (nisprinc) {
+ len = strlen(nisprinc);
+ if (len == 0 || nisprinc[len-1] != '.') {
+ p = malloc(len + 2); /* 1 for '.' and 1 for '\0' */
+ if (p == NULL) {
+ fprintf(stderr,
+ "%s: out of memory\n", program_name);
+ exit(1);
+ }
+ strcpy(p, nisprinc);
+ strcat(p, ".");
+ nisprinc = p;
+ }
+ }
+
+ /*
+ * Do the same checking for domain, if it was specified on
+ * the command line.
+ */
+ if (explicit_domain) {
+ len = strlen(domain);
+ if (len == 0 || domain[len-1] != '.') {
+ p = malloc(len + 2); /* 1 for '.' and 1 for '\0' */
+ if (p == NULL) {
+ fprintf(stderr,
+ "%s: out of memory\n", program_name);
+ exit(1);
+ }
+ strcpy(p, domain);
+ strcat(p, ".");
+ domain = p;
+ }
+ }
+
+ if (add_op) {
+ perform_add(flavor, domain, nisprinc, princ);
+ } else
+ perform_remove(domain, nisprinc);
+ exit(0);
+}
+
+/*
+ * Get the password entry corresponding to 'uid' in the specified
+ * domain. If the domain is NULL and we can't get the information,
+ * then we get it locally (if USE_LOCAL_INFO is defined).
+ */
+struct passwd *
+domain_getpwuid(domain, uid)
+ char *domain;
+ int uid;
+{
+ struct passwd *pw;
+ nis_error err;
+ struct passwd *getpwuid_nisplus_master();
+
+ pw = getpwuid_nisplus_master(domain, uid, &err);
+
+#ifdef USE_LOCAL_INFO
+ /*
+ * If no domain was specified on the command line and we didn't
+ * get a password entry from NIS+, then try getting it locally.
+ */
+ if (! explicit_domain && pw == 0)
+ pw = getpwuid(uid);
+#endif /* USE_LOCAL_INFO */
+
+ if (pw == 0) {
+ if (err == NIS_NOTFOUND)
+ fprintf(stderr,
+ "%s: no password entry found for uid %d\n",
+ program_name, uid);
+ else
+ fprintf(stderr,
+ "%s: could not get the password entry for uid %d: %s\n",
+ program_name, uid, nis_sperrno(err));
+ }
+
+ return (pw);
+}
+
+/*
+ * Get the shadow entry corresponding to 'name' in the specified
+ * domain. If the domain is NULL and we can't get the information,
+ * then we get it locally (if USE_LOCAL_INFO is defined).
+ */
+struct spwd *
+domain_getspnam(domain, name)
+ char *domain;
+ char *name;
+{
+ struct spwd *spw;
+ nis_error err;
+ struct spwd *getspnam_nisplus_master();
+
+ spw = getspnam_nisplus_master(domain, name, &err);
+
+#ifdef USE_LOCAL_INFO
+ /*
+ * If no domain was specified on the command line and we didn't
+ * get a shadow entry from NIS+, then try getting it locally.
+ */
+ if (! explicit_domain && spw == 0)
+ spw = getspnam(name);
+#endif /* USE_LOCAL_INFO */
+
+ if (spw == 0) {
+ if (err == NIS_NOTFOUND)
+ fprintf(stderr,
+ "%s: no password entry found for user %s\n",
+ program_name, name);
+ else
+ fprintf(stderr,
+ "%s: could not get the password entry for user %s: %s\n",
+ program_name, name, nis_sperrno(err));
+ }
+
+ return (spw);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/nisaddcred.h b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/nisaddcred.h
new file mode 100644
index 0000000000..8e8f99b2cf
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nisaddcred/nisaddcred.h
@@ -0,0 +1,44 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * nisaddcred.h
+ *
+ * Copyright (c) 1988-1995 Sun Microsystems, Inc.
+ * All Rights Reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#define MAXIPRINT (11) /* max length of printed integer */
+#define CRED_TABLE "cred.org_dir"
+
+extern nis_object *init_entry(void);
+extern char *default_principal(char *);
+extern char *program_name;
+extern uid_t my_uid;
+extern nis_name my_nisname;
+extern char *my_host;
+extern char *my_group;
+extern char nispasswd[];
+extern int explicit_domain;
+extern struct passwd *getpwuid_nisplus_master(uid_t, nis_error *);
+extern int addonly;
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisaddent.c b/usr/src/cmd/rpcsvc/nis/utils/nisaddent.c
new file mode 100644
index 0000000000..da972cd52f
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nisaddent.c
@@ -0,0 +1,5226 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 1988-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * nisaddent.c
+ *
+ * utility to add/merge /etc files and YP dbm files into nis+ tables
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ndbm.h>
+#include <sys/param.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/nis.h>
+#include <rpcsvc/nis_dhext.h>
+#include <pwd.h>
+#include <auth_attr.h>
+#include <exec_attr.h>
+#include <prof_attr.h>
+#include <user_attr.h>
+#undef GROUP
+#undef opaque
+#include <bsm/libbsm.h>
+#include <nss_dbdefs.h>
+
+#define NIS_ALL_ACC (NIS_READ_ACC|NIS_MODIFY_ACC|NIS_CREATE_ACC|NIS_DESTROY_ACC)
+#define NIS_WORLD_MASK(a) (a)
+#define NIS_GROUP_MASK(a) (a << 8)
+#define NIS_OWNER_MASK(a) (a << 16)
+#define NIS_NOBODY_MASK(a) (a << 24)
+
+/* same as in nis_util.c (we really need a .h) */
+#define NIS_SRC_DEFAULT 0
+#define NIS_SRC_ENV 1
+#define NIS_SRC_ARG 2
+
+#define BIGBUF 8192
+
+
+extern int optind;
+extern char *optarg;
+
+extern nis_object nis_default_obj;
+extern int nis_default_access_src;
+
+extern char *__nis_quote_key(const char *, char *, int);
+extern char *strpbrk_quotes(char *, char *);
+extern char *strtok_quotes(char *, char *);
+extern char *strchr_quotes(char *, char);
+
+extern char *_strtok_escape(char *, char *, char **); /* from libnsl */
+
+/*
+ * Temporary files used by merge_file. The 'created' variables
+ * record whether or not we created the file. An exit routine
+ * will unlink the files if they were created.
+ */
+char tmpdirbuf[MAXPATHLEN];
+char tmppagbuf[MAXPATHLEN];
+int created_dir;
+int created_pag;
+
+struct file_loc {
+ off_t offset;
+ size_t size;
+};
+
+struct line_buf {
+ char *str;
+ int len;
+ int alloc;
+};
+
+
+struct ttypelist_t {
+ char *ttype; /* type tag */
+ char *ypmap; /* nis map to use */
+ char *nistbl; /* nis+ table to use */
+ char *ta_type; /* nis+ table type */
+ char *clrsrch; /* nis+ search critera to clear table */
+ int niskeycol; /* nis+ key column */
+ int filekeycol; /* file key column */
+ char *filesep; /* chars marking columns in files */
+ char *(*dbmniskey)(); /* routine to turn dbm key into nis key */
+ datum (*nisdbmkey)(); /* routine to turn nis entry into dbm key */
+ datum (*filedbmkey)(); /* routine to turn file line into dbm key */
+ int (*genent)(); /* routine to turn line into nis+ entries */
+ void (*dump)(); /* routine to print nis+ table */
+ nis_result *(*dump_match)(); /* routine to match entries */
+ int (*filedbmline)(); /* routine to turn file line into dbm line */
+ void (*filetodbm)(); /* routine to convert to dbm key/content */
+ char *(*fetchdbm)(); /* routine to fetch a nis map line */
+ void (*printfkeystr)(); /* routine to print keystr for -v */
+} *tt;
+
+char *nisdomain;
+mechanism_t **mechs; /* A list of mechs */
+
+char ta_name[NIS_MAXNAMELEN];
+char ta_type[BUFSIZ];
+nis_object *ta_obj;
+
+FILE *etcf = 0;
+DBM *dbmf = 0;
+
+#define OP_ADD 0
+#define OP_REPLACE 1
+#define OP_MERGE 2
+#define OP_DUMP 3
+
+#define F_VERBOSE 0x1
+#define F_PASSWD 0x2
+#define F_QUICK 0x4
+
+int exit_val = 0;
+
+unsigned flags = 0;
+int oldpubkeymode = 0;
+
+unsigned nent_add = 0;
+unsigned nent_del = 0;
+
+ulong_t fpath = 0, allres = 0, master = 0;
+
+int user_attr_keycol[USERATTR_DB_NKEYCOL] = {
+ USERATTR_KEYCOL0, USERATTR_KEYCOL1
+};
+
+int exec_attr_keycol[EXECATTR_DB_NKEYCOL] = {
+ EXECATTR_KEYCOL0, EXECATTR_KEYCOL1, EXECATTR_KEYCOL2
+};
+
+#define PARSE_ERR_MSG_LEN 512
+static char parse_err_msg [PARSE_ERR_MSG_LEN];
+
+char null_string[] = "";
+
+/* the followings are defined to fix cstyle complain of line too long */
+#define ENTRY_COL(c) \
+ zo_data.objdata_u.en_data.en_cols.en_cols_val[(c)]
+
+#define OBJ_COL_VAL(i, c) \
+ res->objects.objects_val[(i)].ENTRY_COL(c).ec_value.ec_value_val
+
+/*
+ * The following is a version of OBJ_COL_VALUE that returns a pointer
+ * to an empty string, rather than a NULL pointer, if the entry does not
+ * exist. Involves two derefs but this only a cmd line app so performance
+ * should not be an issue. The null string is defined as a constant so a new
+ * copy of it is not created for each macro invocation.
+ */
+char *nullStr = "";
+#define OBJ_COL_VAL_NO_NULL(i, c) \
+ OBJ_COL_VAL(i, c) == NULL ? nullStr : OBJ_COL_VAL(i, c)
+
+#define KEYVAL(c) \
+ entry->ENTRY_COL(c).ec_value.ec_value_val
+
+/* get either key value or, if it is null, an empty string */
+#define NKEYVAL(c) ((KEYVAL(c))?(KEYVAL(c)):"")
+
+#define OBJ_COL_NAME(c) \
+ ta_obj->zo_data.objdata_u.ta_data.ta_cols.ta_cols_val[(c)].tc_name
+
+void
+usage()
+{
+ fprintf(stderr, "usage:\tnisaddent [-D defaults] [-Parv]");
+ fprintf(stderr, " [-t table] type [nisdomain]\n\n");
+
+ fprintf(stderr, "\tnisaddent [-D defaults] [-Parmv] -f file");
+ fprintf(stderr, " [-t table] type [nisdomain]\n\n");
+
+ fprintf(stderr, "\tnisaddent [-D defaults] [-Parmv] -y ypdomain");
+
+ fprintf(stderr, " [-Y map] [-t table] type\n\t\t[nisdomain]\n\n");
+ fprintf(stderr,
+ " nisaddent [-AM] -d [-t table] type [nisdomain]\n");
+ exit(1);
+}
+
+void
+line_buf_expand(line)
+ struct line_buf *line;
+{
+ if (line->alloc == 0) {
+ line->alloc = BUFSIZ;
+ line->str = (char *)malloc(line->alloc);
+ } else {
+ line->alloc += BUFSIZ;
+ line->str = (char *)realloc(line->str, line->alloc);
+ }
+
+ if (line->str == 0) {
+ fprintf(stderr, "line_buf_expand: out of memory\n");
+ exit(1);
+ }
+}
+
+void
+line_buf_init(line)
+ struct line_buf *line;
+{
+ memset((char *)line, 0, sizeof (*line));
+ line_buf_expand(line);
+}
+
+void
+print2buf(struct line_buf *line, char *toprint)
+{
+ int toprintlen = 0;
+
+ /* has print buffer line been exhausted */
+ if ((toprintlen = strlen(toprint)) + line->len > (line->alloc - 1)) {
+ do {
+ if (line->alloc == 0) {
+ line->alloc = BUFSIZ;
+ line->str = (char *)malloc(line->alloc);
+ } else {
+ line->alloc += BUFSIZ;
+ line->str = (char *)realloc(line->str,
+ line->alloc);
+ }
+ if (line->str == 0) {
+ fprintf(stderr, "print2buf: out of memory\n");
+ exit(1);
+ }
+ } while (toprintlen > line->alloc);
+ }
+ /* now add new 'toprint' data to buffer */
+ strcat(line->str, toprint);
+ line->len += toprintlen;
+}
+
+void
+print2buf_init(struct line_buf *line)
+{
+ memset((char *)line, 0, sizeof (*line));
+ line->str = NULL;
+ line->alloc = 0;
+ line->len = 0;
+ line_buf_expand(line);
+}
+
+void
+print2buf_destroy(struct line_buf *line)
+{
+ free(line->str);
+ line->str = NULL;
+ line->alloc = 0;
+ line->len = 0;
+}
+
+
+int
+col_equal(tc, ec1, ec2, mod)
+ table_col *tc;
+ entry_col *ec1;
+ entry_col *ec2;
+ int mod;
+{
+ /* ignore unset columns in modified entries */
+ if (mod && (ec1->ec_value.ec_value_val == 0))
+ return (1);
+
+ if (ec1->ec_value.ec_value_len != ec2->ec_value.ec_value_len)
+ return (0);
+
+ if (ec1->ec_value.ec_value_val == 0 ||
+ ec2->ec_value.ec_value_val == 0) {
+ if (ec1->ec_value.ec_value_val == 0 &&
+ ec2->ec_value.ec_value_val == 0)
+ return (1);
+ else
+ return (0);
+ }
+
+ if (tc->tc_flags & TA_CASE)
+ return (strncasecmp(ec1->ec_value.ec_value_val,
+ ec2->ec_value.ec_value_val,
+ ec1->ec_value.ec_value_len) == 0);
+ else
+ return (strncmp(ec1->ec_value.ec_value_val,
+ ec2->ec_value.ec_value_val,
+ ec1->ec_value.ec_value_len) == 0);
+}
+
+int
+entry_equal(e1, e2, mod)
+ nis_object *e1;
+ nis_object *e2;
+ int mod;
+{
+ int i;
+ table_col *tc;
+ entry_col *ec1, *ec2;
+
+ for (i = e1->zo_data.objdata_u.en_data.en_cols.en_cols_len,
+ tc = ta_obj->zo_data.objdata_u.ta_data.ta_cols.ta_cols_val,
+ ec1 = e1->zo_data.objdata_u.en_data.en_cols.en_cols_val,
+ ec2 = e2->zo_data.objdata_u.en_data.en_cols.en_cols_val;
+ i > 0;
+ tc++, ec1++, ec2++, i--)
+ if (!col_equal(tc, ec1, ec2, mod))
+ return (0);
+
+ return (1);
+}
+
+int
+entry_inresult(e, r, mod)
+ nis_object *e;
+ nis_result *r;
+ int mod;
+{
+ int i;
+
+ for (i = r->objects.objects_len-1; i >= 0; i--) {
+ if (entry_equal(e, &(r->objects.objects_val[i]), mod))
+ return (1);
+ }
+
+ return (0);
+}
+
+int
+addentry(table, entry, udata, mod)
+ nis_name table;
+ nis_object *entry;
+ void *udata;
+ int mod;
+{
+ nis_result *ares;
+
+ if (udata && entry_inresult(entry, (nis_result*)udata, mod))
+ return (0);
+
+ if (flags & F_VERBOSE)
+ (*(tt->printfkeystr))("adding/updating \"%s\"\n", entry);
+
+ if (mod) {
+ ares = nis_modify_entry(table, entry, 0);
+ if (ares->status == NIS_NOTFOUND)
+ ares = nis_add_entry(table, entry, 0);
+ } else
+ ares = nis_add_entry(table, entry, ADD_OVERWRITE);
+ if (ares->status == NIS_SUCCESS)
+ nent_add++;
+ else {
+ if (!(flags & F_VERBOSE))
+ (*(tt->printfkeystr))("adding/updating "
+ "\"%s\"\n", entry);
+ if (mod)
+ nis_perror(ares->status, "can't modify entry");
+ else
+ nis_perror(ares->status, "can't add entry");
+ }
+ nis_freeresult(ares);
+
+ return (0);
+}
+
+int
+matchentry(table, entry, udata, mod)
+ nis_name table;
+ nis_object *entry;
+ void *udata;
+ int mod;
+{
+ datum key, val;
+
+ if (udata && entry_inresult(entry, (nis_result*)udata, mod))
+ return (1);
+
+ return (0);
+}
+
+
+int
+removeentry(table, entry)
+ nis_name table;
+ nis_object *entry;
+{
+ nis_result *rres;
+
+ if (flags & F_VERBOSE)
+ (*(tt->printfkeystr))("removing %s\n", entry);
+
+ rres = nis_remove_entry(table, entry, 0);
+ if (rres->status == NIS_SUCCESS)
+ nent_del++;
+ else {
+ if (!(flags & F_VERBOSE))
+ (*(tt->printfkeystr))("removing \"%s\"\n", entry);
+ nis_perror(rres->status, "can't remove entry");
+ }
+ nis_freeresult(rres);
+
+ return (0);
+}
+
+
+/*
+ * return a string of tab characters that when concatenated with the
+ * specified string leave the cursor at the beginning of the specified
+ * column. if the string is too long, then return a single space. the
+ * specified column must be less than 10.
+ */
+
+char *
+tabtocol(str, col)
+ char *str;
+ int col;
+{
+ int c, l;
+ char *p;
+
+ for (c = l = 0, p = str; *p; p++) {
+ if (*p == '\t' || ++l == 8) {
+ c++;
+ l = 0;
+ }
+ }
+
+ if (c < col)
+ return (&("\t\t\t\t\t\t\t\t\t\t"[10-col+c]));
+
+ return (" ");
+}
+
+char *
+dbmniskey_publickey(datum key)
+{
+ static char buf[NIS_MAXNAMELEN];
+ char netname[MAXNETNAMELEN], *tmp;
+
+ strcpy(netname, key.dptr);
+ if (tmp = strchr(netname, ':'))
+ *tmp = '\0';
+
+ sprintf(buf, "%s=%.*s", OBJ_COL_NAME(tt->niskeycol),
+ strlen(netname), netname);
+
+ return (buf);
+}
+
+char *
+dbmniskey_attr(datum key)
+{
+ int i, numkeycol;
+ int *keycol;
+ char *p, *s;
+ static char buf[NIS_MAXNAMELEN];
+
+ s = key.dptr;
+ buf[0] = 0;
+
+ if (strcmp(tt->ttype, NSS_DBNAM_EXECATTR) == 0) {
+ numkeycol = EXECATTR_DB_NKEYCOL;
+ keycol = exec_attr_keycol;
+ } else if (strcmp(tt->ttype, NSS_DBNAM_USERATTR) == 0) {
+ numkeycol = USERATTR_DB_NKEYCOL;
+ keycol = user_attr_keycol;
+ }
+
+ for (i = 0; i < numkeycol; i++) {
+ p = strpbrk(s, tt->filesep);
+ if (p == NULL) {
+ (void) snprintf(buf, NIS_MAXNAMELEN, "%s%s=%.*s", buf,
+ OBJ_COL_NAME(keycol[i]), key.dsize, s);
+ break;
+ }
+ (void) snprintf(buf, NIS_MAXNAMELEN, "%s%s=%.*s%s", buf,
+ OBJ_COL_NAME(keycol[i]), (p - s), s, tt->filesep);
+ s = ++p;
+ }
+ if (p = strrchr(buf, ':'))
+ *p = 0;
+
+ return (buf);
+}
+
+char *
+dbmniskey(key)
+ datum key;
+{
+ static char buf[NIS_MAXNAMELEN];
+
+ sprintf(buf, "%s=%.*s", OBJ_COL_NAME(tt->niskeycol),
+ key.dsize, key.dptr);
+
+ return (buf);
+}
+
+datum
+nisdbmkey(entry)
+ nis_object *entry;
+{
+ static char buf[BUFSIZ+1];
+ datum key;
+
+ strcpy(buf, NKEYVAL(tt->niskeycol));
+
+ key.dptr = buf;
+ key.dsize = strlen(buf);
+
+ return (key);
+}
+
+datum
+nisdbmkey_attr(nis_object *entry)
+{
+ int i;
+ int numkeycol;
+ int *keycol;
+ char *p, *s;
+ datum key;
+ static char buf[BUFSIZ+1];
+
+ buf[0] = 0;
+
+ if (strcmp(tt->ttype, NSS_DBNAM_EXECATTR) == 0) {
+ numkeycol = EXECATTR_DB_NKEYCOL;
+ keycol = exec_attr_keycol;
+ } else if (strcmp(tt->ttype, NSS_DBNAM_USERATTR) == 0) {
+ numkeycol = USERATTR_DB_NKEYCOL;
+ keycol = user_attr_keycol;
+ }
+
+ for (i = 0; i < numkeycol; i++) {
+ s = NKEYVAL(keycol[i]);
+ if ((s == NULL) || (strcmp(s, "") == 0))
+ continue;
+ (void) snprintf(buf, BUFSIZ, "%s%s%s", buf, s, tt->filesep);
+ }
+ if (p = strrchr(buf, ':'))
+ *p = 0;
+
+ key.dptr = buf;
+ key.dsize = strlen(buf);
+
+ return (key);
+}
+
+datum
+nisdbmkey_publickey(entry)
+ nis_object *entry;
+{
+ static char buf[BUFSIZ+1];
+ datum key;
+
+ if (mechs) {
+ char mechalias[MECH_MAXALIASNAME], keylen[256];
+ keylen_t bitlen;
+ algtype_t algtype;
+
+ if (!__nis_authtype2mechalias(NKEYVAL(1), mechalias,
+ MECH_MAXALIASNAME))
+ goto nomechs;
+
+ if (__nis_translate_mechanism(mechalias, &bitlen,
+ &algtype) < 0)
+ goto nomechs;
+
+ if (bitlen == 192)
+ strcpy(keylen, "DES");
+ else
+ snprintf(keylen, 256, "%d", bitlen);
+ snprintf(buf, BUFSIZ, "%s:%s:%d", NKEYVAL(2), keylen, algtype);
+ } else {
+nomechs:
+ strcpy(buf, NKEYVAL(2));
+ }
+ key.dptr = buf;
+ key.dsize = strlen(buf);
+
+ return (key);
+}
+
+datum
+filedbmkey_attr(char *line)
+{
+ int i, j, skip;
+ int numkeycol;
+ int *keycol;
+ char *p = NULL;
+ char *q = NULL;
+ char *s = NULL;
+ static char buf[BUFSIZ+1];
+ datum key;
+
+ s = line;
+ buf[0] = 0;
+
+ if (strcmp(tt->ttype, NSS_DBNAM_EXECATTR) == 0) {
+ numkeycol = EXECATTR_DB_NKEYCOL;
+ keycol = exec_attr_keycol;
+ } else if (strcmp(tt->ttype, NSS_DBNAM_USERATTR) == 0) {
+ numkeycol = USERATTR_DB_NKEYCOL;
+ keycol = user_attr_keycol;
+ }
+
+ for (i = 0; i < numkeycol; i++) {
+ for (j = keycol[i]; j >= 0; j--) {
+ p = strpbrk(s, tt->filesep);
+ if (j > 0) {
+ if (p == NULL) {
+ key.dptr = 0;
+ key.dsize = 0;
+ return (key);
+ }
+ skip = strspn(p, tt->filesep);
+ if (skip > 1) {
+ if ((j = j - skip) <= 0)
+ j = 1;
+ }
+ s = p + skip;
+ }
+ }
+ if (p) {
+ sprintf(buf, "%s%.*s:", buf, (p - s), s);
+ } else {
+ if (buf[0] == 0)
+ strcpy(buf, s);
+ break;
+ }
+ }
+
+ if (q = strrchr(buf, ':'))
+ *q = 0;
+
+ key.dptr = buf;
+ key.dsize = strlen(buf);
+
+ return (key);
+}
+
+datum
+filedbmkey_publickey(line)
+ char *line;
+{
+ static char buf[BUFSIZ+1];
+ int i;
+ char *p;
+ datum key;
+
+
+ if (mechs) {
+ char netname[MAXNETNAMELEN], keys[8704];
+ char keylen[256];
+ char *pubkey, *privkey, *algtype;
+ int bitlen = 0;
+
+ sscanf(line, "%s %s", netname, keys);
+
+ pubkey = keys;
+ /*
+ * We don't use the private key, but we need to get to
+ * the algtype
+ */
+ if (privkey = strchr(keys, ':')) {
+ *privkey++ = '\0';
+ /* Get rid of extra :'s */
+ if (*privkey == ':')
+ privkey++;
+ }
+ if (algtype = strchr(privkey, ':'))
+ *algtype++ = '\0';
+ else
+ algtype = "0";
+
+ bitlen = (strlen(pubkey) / 2) * 8;
+ if (bitlen == 192)
+ strcpy(keylen, "DES");
+ else
+ snprintf(keylen, 256, "%d", bitlen);
+
+ snprintf(buf, BUFSIZ, "%s:%s:%s", netname, keylen, algtype);
+ } else {
+ for (i = tt->filekeycol; i >= 0; i--) {
+ p = strpbrk(line, tt->filesep);
+ if (i > 0) {
+ if (p == 0) {
+ key.dptr = 0;
+ key.dsize = 0;
+ return (key);
+ }
+ line = p + strspn(p, tt->filesep);
+ }
+ }
+
+ if (p)
+ sprintf(buf, "%.*s", p-line, line);
+ else
+ strcpy(buf, line);
+ }
+
+ key.dptr = buf;
+ key.dsize = strlen(buf);
+
+ return (key);
+}
+
+/*
+ * Note that most of the filedbmkey() code is duplicated in
+ * filedbmkey_netid() below.
+ */
+datum
+filedbmkey(line)
+ char *line;
+{
+ static char buf[BUFSIZ+1];
+ int i;
+ char *p;
+ datum key;
+
+ for (i = tt->filekeycol; i >= 0; i--) {
+ p = strpbrk(line, tt->filesep);
+ if (i > 0) {
+ if (p == 0) {
+ key.dptr = 0;
+ key.dsize = 0;
+ return (key);
+ }
+ line = p + strspn(p, tt->filesep);
+ }
+ }
+
+ if (p)
+ sprintf(buf, "%.*s", p-line, line);
+ else
+ strcpy(buf, line);
+
+ key.dptr = buf;
+ key.dsize = strlen(buf);
+
+ return (key);
+}
+
+/*
+ * filedbmkey_netid() duplicates filedbmkey() above, but also
+ * removes trailing dots.
+ */
+datum
+filedbmkey_netid(line)
+ char *line;
+{
+ static char buf[BUFSIZ+1];
+ int i;
+ char *p;
+ datum key;
+
+ for (i = tt->filekeycol; i >= 0; i--) {
+ p = strpbrk(line, tt->filesep);
+ if (i > 0) {
+ if (p == 0) {
+ key.dptr = 0;
+ key.dsize = 0;
+ return (key);
+ }
+ line = p + strspn(p, tt->filesep);
+ }
+ }
+
+ if (p)
+ sprintf(buf, "%.*s", p-line, line);
+ else
+ strcpy(buf, line);
+
+ key.dptr = buf;
+ key.dsize = strlen(buf);
+
+ /* Strip any trailing dot from key */
+ if (key.dsize > 0 && buf[key.dsize-1] == '.') {
+ buf[key.dsize-1] = '\0';
+ key.dsize--;
+ }
+
+ return (key);
+}
+
+int
+blankline(line)
+ char *line;
+{
+ char *p;
+
+ for (p = line; *p; p++)
+ if (*p != ' ' && *p != '\t')
+ return (0);
+ return (1);
+}
+
+/*
+ * Read a line into a line_buf starting at offset 'n'. We expand the
+ * line_buf when needed. We read until '\n' or EOF. If we don't read
+ * any characters before EOF we return 0. Otherwise, we return a pointer
+ * to the string read.
+ */
+
+char *
+fget_line_at(line, n, fp)
+ struct line_buf *line;
+ int n;
+ FILE *fp;
+{
+ int c;
+
+ line->len = n;
+ while (1) {
+ c = fgetc(fp);
+ if (c == -1)
+ break;
+ if (line->len >= line->alloc)
+ line_buf_expand(line);
+ line->str[line->len++] = c;
+ if (c == '\n')
+ break;
+ }
+
+ /* null terminate */
+ if (line->len >= line->alloc)
+ line_buf_expand(line);
+ line->str[line->len++] = 0;
+
+ /* if we read no characters, return NULL to indicate EOF */
+ if (line->str[0] == '\0')
+ return (0);
+
+ return (line->str);
+}
+
+
+int
+filedbmline(line, etcf, lineno, loc)
+ struct line_buf *line;
+ FILE *etcf;
+ int *lineno;
+ struct file_loc *loc;
+{
+ int len = 0;
+
+ loc->offset = ftell(etcf);
+ while (1) {
+ if (fget_line_at(line, len, etcf) == 0)
+ return (0);
+
+ if (lineno)
+ (*lineno)++;
+
+ len = strlen(line->str);
+ if (line->str[len-1] == '\n') {
+ line->str[len-1] = 0;
+ len -= 1;
+ }
+
+ if (!blankline(line->str))
+ break;
+
+ len = 0;
+ loc->offset = ftell(etcf);
+ }
+
+ loc->size = len;
+ return (1);
+}
+
+int
+filedbmline_comment(line, etcf, lineno, loc)
+ struct line_buf *line;
+ FILE *etcf;
+ int *lineno;
+ struct file_loc *loc;
+{
+ int len = 0;
+
+ loc->offset = ftell(etcf);
+ while (1) {
+ if (fget_line_at(line, len, etcf) == 0)
+ return (0);
+
+ if (lineno)
+ (*lineno)++;
+
+ len = strlen(line->str);
+ if (len >= 2 &&
+ line->str[0] != '#' &&
+ line->str[len-2] == '\\' && line->str[len-1] == '\n') {
+ line->str[len-2] = 0;
+ len -= 2;
+ continue; /* append next line at end */
+ }
+
+ if (line->str[len-1] == '\n') {
+ line->str[len-1] = 0;
+ len -= 1;
+ }
+
+ if (!blankline(line->str) && line->str[0] != '#')
+ break;
+
+ len = 0;
+ loc->offset = ftell(etcf);
+ }
+
+ loc->size = len;
+ return (1);
+}
+
+int
+filedbmline_plus(line, etcf, lineno, loc)
+ struct line_buf *line;
+ FILE *etcf;
+ int *lineno;
+ struct file_loc *loc;
+{
+ int len = 0;
+
+ loc->offset = ftell(etcf);
+ while (1) {
+ if (fget_line_at(line, len, etcf) == 0)
+ return (0);
+
+ if (lineno)
+ (*lineno)++;
+
+ len = strlen(line->str);
+ if (line->str[len-1] == '\n') {
+ line->str[len-1] = 0;
+ len -= 1;
+ }
+
+ if (!blankline(line->str) &&
+ line->str[0] != '+' && line->str[0] != '-')
+ break;
+
+ len = 0;
+ loc->offset = ftell(etcf);
+ }
+
+ loc->size = len;
+ return (1);
+}
+
+
+char *
+fetchdbm(key)
+ datum key;
+{
+ static char line[BUFSIZ+1];
+ datum val;
+
+ val = dbm_fetch(dbmf, key);
+ if (val.dptr) {
+ sprintf(line, "%.*s", val.dsize, val.dptr);
+ return (line);
+ }
+ return (0);
+}
+
+char *
+fetchdbm_addkey(key)
+ datum key;
+{
+ static char line[BUFSIZ+1];
+ datum val;
+
+ val = dbm_fetch(dbmf, key);
+ if (val.dptr) {
+ sprintf(line, "%.*s %.*s",
+ key.dsize, key.dptr,
+ val.dsize, val.dptr);
+ return (line);
+ } else if (key.dptr[key.dsize-1] == NULL) {
+ key.dsize -= 1;
+ val = dbm_fetch(dbmf, key);
+ if (val.dptr) {
+ sprintf(line, "%.*s %.*s",
+ key.dsize, key.dptr,
+ val.dsize, val.dptr);
+ return (line);
+ }
+ }
+ return (0);
+}
+
+char *
+fetchfile(key)
+ datum key;
+{
+ static struct line_buf line;
+ char *p;
+ datum content;
+ datum lkey;
+ struct file_loc loc;
+
+ content = dbm_fetch(dbmf, key);
+ if (content.dptr == 0)
+ return (0); /* not in file */
+ memcpy((char *)&loc, (char *)content.dptr, sizeof (loc));
+
+ if (fseek(etcf, loc.offset, SEEK_SET) == -1) {
+ fprintf(stderr, "error seeking on file\n");
+ exit(1);
+ }
+
+ if (line.alloc == 0)
+ line_buf_init(&line);
+
+ if (tt->filedbmline(&line, etcf, 0, &loc))
+ return (line.str);
+ return (0);
+}
+/*
+ * Convert a line into key and content. The key is included
+ * in the content.
+ */
+void
+filetodbm(line, key, content)
+ char *line;
+ datum *key;
+ datum *content;
+{
+ int i;
+ char *p;
+
+ content->dptr = line;
+ content->dsize = strlen(line);
+
+ for (i = tt->filekeycol; i >= 0; i--) {
+ p = strpbrk(line, tt->filesep);
+ if (i > 0) {
+ if (p == 0) {
+ key->dptr = 0;
+ key->dsize = 0;
+ return;
+ }
+ line = p + strspn(p, tt->filesep);
+ }
+ }
+
+ if (p) {
+ key->dptr = line;
+ key->dsize = p-line;
+ } else {
+ key->dptr = line;
+ key->dsize = strlen(line);
+ }
+}
+
+/*
+ * Convert a line into key and content. Unlike filetodbm, the key
+ * is not included in the content.
+ */
+void
+filetodbm_keyvalue(line, key, content)
+ char *line;
+ datum *key;
+ datum *content;
+{
+ int i;
+ char *p;
+
+ for (i = tt->filekeycol; i >= 0; i--) {
+ p = strpbrk(line, tt->filesep);
+ if (i > 0) {
+ if (p == 0) {
+ key->dptr = 0;
+ key->dsize = 0;
+ return;
+ }
+ line = p + strspn(p, tt->filesep);
+ }
+ }
+
+ if (p) {
+ key->dptr = line;
+ key->dsize = p-line;
+ line = p + strspn(p, tt->filesep);
+ content->dptr = line;
+ content->dsize = strlen(line);
+ } else {
+ key->dptr = line;
+ key->dsize = strlen(line);
+ content->dptr = "";
+ content->dsize = 0;
+ }
+}
+
+/*
+ * Convert a line into key and content. The key is included
+ * in the content.
+ */
+void
+filetodbm_attr(char *line, datum *key, datum *content)
+{
+ int i, j, skip, numkeycol;
+ int *keycol;
+ char *p;
+
+ content->dptr = line;
+ content->dsize = strlen(line);
+
+ if (strcmp(tt->ttype, NSS_DBNAM_EXECATTR) == 0) {
+ numkeycol = EXECATTR_DB_NKEYCOL;
+ keycol = exec_attr_keycol;
+ } else if (strcmp(tt->ttype, NSS_DBNAM_USERATTR) == 0) {
+ numkeycol = USERATTR_DB_NKEYCOL;
+ keycol = user_attr_keycol;
+ }
+
+ for (i = 0; i < numkeycol; i++) {
+ for (j = keycol[i]; j >= 0; j--) {
+ p = strpbrk(line, tt->filesep);
+ if (j > 0) {
+ if (p == NULL) {
+ key->dptr = 0;
+ key->dsize = 0;
+ return;
+ }
+ skip = strspn(p, tt->filesep);
+ if (skip > 1) {
+ if ((j = j - skip) <= 0)
+ j = 1;
+ }
+ line = p + skip;
+ }
+ }
+ }
+
+ if (p) {
+ key->dptr = line;
+ key->dsize = p - line;
+ } else {
+ key->dptr = line;
+ key->dsize = strlen(line);
+ }
+}
+
+void
+printfkeystr_0(fstr, entry)
+ char *fstr;
+ nis_object *entry;
+{
+ char keystr[BUFSIZ];
+
+ sprintf(keystr, "%s", NKEYVAL(0));
+ printf(fstr, keystr);
+}
+
+void
+printfkeystr_01(fstr, entry)
+ char *fstr;
+ nis_object *entry;
+{
+ char keystr[BUFSIZ];
+
+ sprintf(keystr, "%s %s", NKEYVAL(0), NKEYVAL(1));
+ printf(fstr, keystr);
+}
+
+void
+printfkeystr_cname(fstr, entry)
+ char *fstr;
+ nis_object *entry;
+{
+ char keystr[BUFSIZ];
+
+ if (strcasecmp(NKEYVAL(0), NKEYVAL(1)) == 0)
+ sprintf(keystr, "%s", NKEYVAL(0));
+ else
+ sprintf(keystr, "%s (%s)", NKEYVAL(0), NKEYVAL(1));
+
+ printf(fstr, keystr);
+}
+
+
+#define GENENT_OK 0
+#define GENENT_PARSEERR 1
+#define GENENT_CBERR 2
+#define GENENT_ERR 3
+
+
+/*
+ * /etc/hosts
+ * nis+ table: (hosts_tbl) cname, name, addr, comment
+ *
+ */
+
+int
+genent_hosts(line, cback, udata)
+ char *line;
+ int (*cback)();
+ void *udata;
+{
+ char buf[BUFSIZ+1];
+ char *t;
+ nis_object eobj;
+ entry_col ecol[4];
+ char *cname;
+
+ /*
+ * don't clobber our argument
+ */
+ if (strlen(line) >= sizeof (buf)) {
+ strcpy(parse_err_msg, "line too long");
+ return (GENENT_PARSEERR);
+ }
+ strcpy(buf, line);
+
+ /*
+ * clear column data
+ */
+ memset((char *)ecol, 0, sizeof (ecol));
+
+ /*
+ * comment (col 3)
+ */
+ t = strchr(buf, '#');
+ if (t) {
+ *t++ = 0;
+ ecol[3].ec_value.ec_value_val = t;
+ ecol[3].ec_value.ec_value_len = strlen(t)+1;
+ } else {
+ ecol[3].ec_value.ec_value_val = 0;
+ ecol[3].ec_value.ec_value_len = 0;
+ }
+
+ /*
+ * addr(col 2)
+ */
+ if ((t = strtok(buf, " \t")) == 0) {
+ strcpy(parse_err_msg, "no host");
+ return (GENENT_PARSEERR);
+ }
+ ecol[2].ec_value.ec_value_val = t;
+ ecol[2].ec_value.ec_value_len = strlen(t)+1;
+
+ /*
+ * cname (col 0)
+ */
+ if ((t = strtok(NULL, " \t")) == 0) {
+ strcpy(parse_err_msg, "no cname");
+ return (GENENT_PARSEERR);
+ }
+ ecol[0].ec_value.ec_value_val = t;
+ ecol[0].ec_value.ec_value_len = strlen(t)+1;
+ cname = t;
+
+ /*
+ * build entry
+ */
+ eobj = nis_default_obj;
+ eobj.zo_data.zo_type = NIS_ENTRY_OBJ;
+ eobj.EN_data.en_type = ta_type;
+ eobj.EN_data.en_cols.en_cols_val = ecol;
+ eobj.EN_data.en_cols.en_cols_len = 4;
+
+ if (!cback)
+ cback = addentry;
+
+ /*
+ * name (col 1)
+ */
+ do {
+ /*
+ * don't clobber comment in canonical entry
+ */
+ if (t != cname && strcasecmp(t, cname) == 0)
+ continue;
+
+ ecol[1].ec_value.ec_value_val = t;
+ ecol[1].ec_value.ec_value_len = strlen(t)+1;
+
+ if ((*cback)(ta_name, &eobj, udata, 0))
+ return (GENENT_CBERR);
+
+ /*
+ * only put comment in canonical entry
+ */
+ ecol[3].ec_value.ec_value_val = 0;
+ ecol[3].ec_value.ec_value_len = 0;
+
+ } while (t = strtok(NULL, " \t"));
+
+ return (GENENT_OK);
+}
+
+/*
+ * List all of the entries that have the same cname as the passed
+ * entry.
+ */
+nis_result *
+dump_match_cname(table, entry)
+ nis_name table;
+ nis_object *entry;
+{
+ nis_result *res = 0;
+ char *c0, *c1, *c2;
+ char srch[NIS_MAXNAMELEN];
+
+ if ((c0 = KEYVAL(0)) && *c0 &&
+ (c1 = KEYVAL(1)) && (c2 = KEYVAL(2)) &&
+ (strcasecmp(c0, c1) == 0)) {
+ sprintf(srch, "[cname=%s],%s", c0, table);
+ res = nis_list(srch, allres|master, 0, 0);
+ }
+
+ return (res);
+}
+
+/*
+ * List all of the entries that have the same cname and address as the passed
+ * entry.
+ */
+nis_result *
+dump_match_hosts(table, entry)
+ nis_name table;
+ nis_object *entry;
+{
+ nis_result *res = 0;
+ char *c0, *c1, *c2;
+ char srch[NIS_MAXNAMELEN];
+
+ if ((c0 = KEYVAL(0)) && *c0 &&
+ (c1 = KEYVAL(1)) && (c2 = KEYVAL(2)) &&
+ (strcasecmp(c0, c1) == 0)) {
+ sprintf(srch, "[cname=%s,addr=%s],%s", c0, c2, table);
+ res = nis_list(srch, allres|master, 0, 0);
+ }
+
+ return (res);
+}
+
+void
+dump_hosts(res)
+ nis_result *res;
+{
+ int i, j;
+ char buf[BUFSIZ+1], *c0, *c1, *c2, *c3, *c0a, *c1a;
+
+ for (i = 0; i < res->objects.objects_len; i++) {
+ if ((c0 = OBJ_COL_VAL(i, 0)) && *c0 &&
+ (c1 = OBJ_COL_VAL(i, 1)) && (c2 = OBJ_COL_VAL(i, 2)) &&
+ (strcasecmp(c0, c1) == 0)) {
+ strcpy(buf, c2);
+ strcat(buf, tabtocol(buf, 2));
+ strcat(buf, c0);
+ for (j = 0; j < res->objects.objects_len; j++)
+ if (j != i &&
+ (c0a = OBJ_COL_VAL(j, 0)) && *c0a &&
+ (c1a = OBJ_COL_VAL(j, 1)) &&
+ strcasecmp(c0, c0a) == 0) {
+ strcat(buf, " ");
+ strcat(buf, c1a);
+ *c0a = 0;
+ }
+ if ((c3 = OBJ_COL_VAL(i, 3)) && !blankline(c3)) {
+ strcat(buf, tabtocol(buf, 5));
+ strcat(buf, "#");
+ strcat(buf, c3);
+ }
+ *c0 = 0;
+ printf("%s\n", buf);
+ }
+ }
+}
+
+
+/*
+ * /etc/passwd
+ * /etc/shadow
+ * nis+ table: (passwd_tbl) name, passwd, uid, gid, gcos, home, shell, shadow
+ *
+ */
+
+int
+genent_passwd(line, cback, udata)
+ char *line;
+ int (*cback)();
+ void *udata;
+{
+ char buf[BUFSIZ+1];
+ char *s, *t;
+ nis_object eobj;
+ entry_col ecol[8];
+ char *name, pname[NIS_MAXNAMELEN];
+ char aname[NIS_MAXNAMELEN];
+
+ /*
+ * don't clobber our argument
+ */
+ if (strlen(line) >= sizeof (buf)) {
+ strcpy(parse_err_msg, "line too long");
+ return (GENENT_PARSEERR);
+ }
+ strcpy(buf, line);
+ t = buf;
+
+ /* ignore empty entries */
+ if (*t == '\0')
+ return (GENENT_OK);
+
+ /*
+ * clear column data
+ */
+ memset((char *)ecol, 0, sizeof (ecol));
+
+ /*
+ * name (col 0)
+ */
+ if ((s = strchr(t, ':')) == 0) {
+ strcpy(parse_err_msg, "no password");
+ return (GENENT_PARSEERR);
+ }
+ *s++ = 0;
+ ecol[0].ec_value.ec_value_val = t;
+ ecol[0].ec_value.ec_value_len = strlen(t)+1;
+ name = t;
+ t = s;
+
+ /*
+ * passwd (col 1)
+ */
+ if ((s = strchr(t, ':')) == 0) {
+ strcpy(parse_err_msg, "no uid");
+ return (GENENT_PARSEERR);
+ }
+ *s++ = 0;
+#ifndef FOURDOTX
+ if ((dbmf && ! etcf) || (flags & F_PASSWD)) {
+#endif
+ ecol[1].ec_value.ec_value_val = t;
+ ecol[1].ec_value.ec_value_len = strlen(t)+1;
+ ecol[1].ec_flags = EN_CRYPT|EN_MODIFIED;
+#ifndef FOURDOTX
+ }
+#endif
+ t = s;
+
+ /*
+ * uid (col 2)
+ */
+ if ((s = strchr(t, ':')) == 0 || s == t) {
+ strcpy(parse_err_msg, "no gid");
+ return (GENENT_PARSEERR);
+ }
+ *s++ = 0;
+ ecol[2].ec_value.ec_value_val = t;
+ ecol[2].ec_value.ec_value_len = strlen(t)+1;
+ ecol[2].ec_flags = EN_MODIFIED;
+ t = s;
+
+ /*
+ * gid (col 3)
+ */
+ if ((s = strchr(t, ':')) == 0 || s == t) {
+ strcpy(parse_err_msg, "no gcos");
+ return (GENENT_PARSEERR);
+ }
+ *s++ = 0;
+ ecol[3].ec_value.ec_value_val = t;
+ ecol[3].ec_value.ec_value_len = strlen(t)+1;
+ ecol[3].ec_flags = EN_MODIFIED;
+ t = s;
+
+ /*
+ * gcos (col 4)
+ */
+ if ((s = strchr(t, ':')) == 0) {
+ strcpy(parse_err_msg, "no home");
+ return (GENENT_PARSEERR);
+ }
+ *s++ = 0;
+ ecol[4].ec_value.ec_value_val = t;
+ ecol[4].ec_value.ec_value_len = strlen(t)+1;
+ ecol[4].ec_flags = EN_MODIFIED;
+ t = s;
+
+ /*
+ * home (col 5)
+ */
+ if ((s = strchr(t, ':')) == 0) {
+ strcpy(parse_err_msg, "no shell");
+ return (GENENT_PARSEERR);
+ }
+ *s++ = 0;
+ ecol[5].ec_value.ec_value_val = t;
+ ecol[5].ec_value.ec_value_len = strlen(t)+1;
+ ecol[5].ec_flags = EN_MODIFIED;
+ t = s;
+
+ /*
+ * shell (col 6)
+ */
+ ecol[6].ec_value.ec_value_val = t;
+ ecol[6].ec_value.ec_value_len = strlen(t)+1;
+ ecol[6].ec_flags = EN_MODIFIED;
+
+ /*
+ * NIS+ principal name
+ */
+ sprintf(pname, "%s.%s", name, nisdomain);
+
+ /*
+ * build entry
+ */
+ eobj = nis_default_obj;
+ eobj.zo_owner = pname;
+ /*
+ * If access mode did not come from -D option,
+ * then ignore it.
+ */
+ if (nis_default_access_src != NIS_SRC_ARG)
+ eobj.zo_access = NIS_OWNER_MASK(NIS_READ_ACC);
+ eobj.zo_data.zo_type = NIS_ENTRY_OBJ;
+ eobj.EN_data.en_type = ta_type;
+ eobj.EN_data.en_cols.en_cols_val = ecol;
+ eobj.EN_data.en_cols.en_cols_len = 8;
+
+ if (!cback)
+ cback = addentry;
+
+ /* specify entry by name alone (ignore uid) */
+ sprintf(aname, "[name=%s],%s", name, ta_name);
+
+ if ((*cback)(aname, &eobj, udata, 1))
+ return (GENENT_CBERR);
+
+ return (GENENT_OK);
+}
+
+void
+dump_passwd(res)
+ nis_result *res;
+{
+ int i;
+ char *c0, *c1, *c2, *c3, *c4, *c5, *c6;
+
+ for (i = 0; i < res->objects.objects_len; i++) {
+ if (c0 = OBJ_COL_VAL(i, 0)) {
+ c1 = OBJ_COL_VAL(i, 1);
+ c1 = c1 ? c1 : null_string;
+ c2 = OBJ_COL_VAL(i, 2);
+ c2 = c2 ? c2 : null_string;
+ c3 = OBJ_COL_VAL(i, 3);
+ c3 = c3 ? c3 : null_string;
+ c4 = OBJ_COL_VAL(i, 4);
+ c4 = c4 ? c4 : null_string;
+ c5 = OBJ_COL_VAL(i, 5);
+ c5 = c5 ? c5 : null_string;
+ c6 = OBJ_COL_VAL(i, 6);
+ c6 = c6 ? c6 : null_string;
+ printf("%s:%s:%s:%s:%s:%s:%s\n",
+ c0,
+#ifndef FOURDOTX
+ "x",
+#else
+ c1,
+#endif
+ c2, c3, c4, c5, c6);
+ }
+ }
+}
+
+int
+genent_shadow(line, cback, udata)
+ char *line;
+ int (*cback)();
+ void *udata;
+{
+ char buf[BUFSIZ+1];
+ char *s, *t;
+ nis_object eobj;
+ entry_col ecol[8];
+ char *name, mname[NIS_MAXNAMELEN], pname[NIS_MAXNAMELEN];
+
+ /*
+ * don't clobber our argument
+ */
+ if (strlen(line) >= sizeof (buf)) {
+ strcpy(parse_err_msg, "line too long");
+ return (GENENT_PARSEERR);
+ }
+ strcpy(buf, line);
+ t = buf;
+
+ /* ignore empty entries */
+ if (*t == '\0')
+ return (GENENT_OK);
+
+ /*
+ * clear column data
+ */
+ memset((char *)ecol, 0, sizeof (ecol));
+
+ /*
+ * name (col 0)
+ */
+ if ((s = strchr(t, ':')) == 0) {
+ strcpy(parse_err_msg, "no password");
+ return (GENENT_PARSEERR);
+ }
+ *s++ = 0;
+ ecol[0].ec_value.ec_value_val = t;
+ ecol[0].ec_value.ec_value_len = strlen(t)+1;
+ name = t;
+ t = s;
+
+ /*
+ * passwd (col 1)
+ */
+ if ((s = strchr(t, ':')) == 0) {
+ strcpy(parse_err_msg, "no shadow");
+ return (GENENT_PARSEERR);
+ }
+ *s++ = 0;
+ ecol[1].ec_value.ec_value_val = t;
+ ecol[1].ec_value.ec_value_len = strlen(t)+1;
+ ecol[1].ec_flags = EN_CRYPT|EN_MODIFIED;
+ t = s;
+
+ /*
+ * shadow (col 7)
+ */
+ ecol[7].ec_value.ec_value_val = t;
+ ecol[7].ec_value.ec_value_len = strlen(t)+1;
+ ecol[7].ec_flags = EN_MODIFIED;
+
+ /*
+ * NIS+ principal name
+ */
+ sprintf(pname, "%s.%s", name, nisdomain);
+
+ /*
+ * build entry
+ */
+ eobj = nis_default_obj;
+ eobj.zo_owner = pname;
+ /*
+ * If access mode did not come from -D option,
+ * then ignore it.
+ */
+ if (nis_default_access_src != NIS_SRC_ARG)
+ eobj.zo_access = NIS_OWNER_MASK(NIS_READ_ACC);
+ eobj.zo_data.zo_type = NIS_ENTRY_OBJ;
+ eobj.EN_data.en_type = ta_type;
+ eobj.EN_data.en_cols.en_cols_val = ecol;
+ eobj.EN_data.en_cols.en_cols_len = 8;
+
+ if (!cback)
+ cback = addentry;
+
+ /* specify entry by name alone (ignore uid) */
+ sprintf(mname, "[name=%s],%s", name, ta_name);
+
+ if ((*cback)(mname, &eobj, udata, 1))
+ return (GENENT_CBERR);
+
+ return (GENENT_OK);
+}
+
+void
+dump_shadow(res)
+ nis_result *res;
+{
+ int i;
+ char *c0, *c1, *c7;
+
+ for (i = 0; i < res->objects.objects_len; i++) {
+ if (c0 = OBJ_COL_VAL(i, 0)) {
+ c1 = OBJ_COL_VAL(i, 1);
+ c1 = c1 ? c1 : null_string;
+ if (c7 = OBJ_COL_VAL(i, 7))
+ printf("%s:%s:%s\n", c0, c1, c7);
+ else
+ printf("%s:%s::::::\n", c0, c1);
+ }
+ }
+}
+
+
+/*
+ * /etc/ethers
+ * nis+ table: (ethers_tbl) addr, name, comment
+ *
+ */
+
+int
+genent_ethers(line, cback, udata)
+ char *line;
+ int (*cback)();
+ void *udata;
+{
+ char buf[BUFSIZ+1];
+ char *t;
+ nis_object eobj;
+ entry_col ecol[3];
+ char *name;
+ char *addr, aname[NIS_MAXNAMELEN];
+
+ /*
+ * don't clobber our argument
+ */
+ if (strlen(line) >= sizeof (buf)) {
+ strcpy(parse_err_msg, "line too long");
+ return (GENENT_PARSEERR);
+ }
+ strcpy(buf, line);
+
+ /*
+ * clear column data
+ */
+ memset((char *)ecol, 0, sizeof (ecol));
+
+ /*
+ * comment (col 2)
+ */
+ t = strchr(buf, '#');
+ if (t) {
+ *t++ = 0;
+ ecol[2].ec_value.ec_value_val = t;
+ ecol[2].ec_value.ec_value_len = strlen(t)+1;
+ } else {
+ ecol[2].ec_value.ec_value_val = 0;
+ ecol[2].ec_value.ec_value_len = 0;
+ }
+
+ /*
+ * addr(col 0)
+ */
+ if ((t = strtok(buf, " \t")) == 0) {
+ strcpy(parse_err_msg, "no name");
+ return (GENENT_PARSEERR);
+ }
+ ecol[0].ec_value.ec_value_val = t;
+ ecol[0].ec_value.ec_value_len = strlen(t)+1;
+ addr = t;
+
+ /*
+ * name(col 1)
+ */
+ if ((t = strtok(NULL, " \t")) == 0) {
+ strcpy(parse_err_msg, "no white space allowed in name");
+ return (GENENT_PARSEERR);
+ }
+ ecol[1].ec_value.ec_value_val = t;
+ ecol[1].ec_value.ec_value_len = strlen(t)+1;
+ name = t;
+
+ /*
+ * build entry
+ */
+ eobj = nis_default_obj;
+ eobj.zo_data.zo_type = NIS_ENTRY_OBJ;
+ eobj.EN_data.en_type = ta_type;
+ eobj.EN_data.en_cols.en_cols_val = ecol;
+ eobj.EN_data.en_cols.en_cols_len = 3;
+
+ if (!cback)
+ cback = addentry;
+
+ /* specify entry by addr alone (ignore name) */
+ sprintf(aname, "[addr=%s],%s", addr, ta_name);
+
+ if ((*cback)(aname, &eobj, udata, 0))
+ return (GENENT_CBERR);
+
+ return (GENENT_OK);
+}
+
+void
+dump_ethers(res)
+ nis_result *res;
+{
+ int i;
+ char buf[BUFSIZ+1], *c0, *c1, *c2;
+
+ for (i = 0; i < res->objects.objects_len; i++) {
+ if ((c0 = OBJ_COL_VAL(i, 0)) &&
+ (c1 = OBJ_COL_VAL(i, 1))) {
+ strcpy(buf, c0);
+ strcat(buf, tabtocol(buf, 2));
+ strcat(buf, c1);
+ if ((c2 = OBJ_COL_VAL(i, 2)) &&
+ !blankline(c2)) {
+ strcat(buf, tabtocol(buf, 5));
+ strcat(buf, "#");
+ strcat(buf, c2);
+ }
+ printf("%s\n", buf);
+ }
+ }
+}
+
+
+/*
+ * /etc/group
+ * nis+ table: (group_tbl) name, passwd, gid, members
+ *
+ */
+
+int
+genent_group(line, cback, udata)
+ char *line;
+ int (*cback)();
+ void *udata;
+{
+ char buf[BIGBUF+1];
+ char *s, *t;
+ nis_object eobj;
+ entry_col ecol[4];
+ char *name;
+
+ /*
+ * don't clobber our argument
+ */
+ if (strlen(line) >= sizeof (buf)) {
+ strcpy(parse_err_msg, "line too long");
+ return (GENENT_PARSEERR);
+ }
+ strcpy(buf, line);
+ t = buf;
+
+ /* ignore empty entries */
+ if (*t == '\0')
+ return (GENENT_OK);
+
+ /*
+ * clear column data
+ */
+ memset((char *)ecol, 0, sizeof (ecol));
+
+ /*
+ * name (col 0)
+ */
+ if ((s = strchr(t, ':')) == 0) {
+ strcpy(parse_err_msg, "no passwd");
+ return (GENENT_PARSEERR);
+ }
+ *s++ = 0;
+ ecol[0].ec_value.ec_value_val = t;
+ ecol[0].ec_value.ec_value_len = strlen(t)+1;
+ name = t;
+ t = s;
+
+ /*
+ * passwd (col 1)
+ */
+ if ((s = strchr(t, ':')) == 0) {
+ strcpy(parse_err_msg, "no gid");
+ return (GENENT_PARSEERR);
+ }
+ *s++ = 0;
+ ecol[1].ec_value.ec_value_val = t;
+ ecol[1].ec_value.ec_value_len = strlen(t)+1;
+ ecol[1].ec_flags = EN_CRYPT;
+ t = s;
+
+ /*
+ * gid (col 2)
+ */
+ if ((s = strchr(t, ':')) == 0 || s == t) {
+ strcpy(parse_err_msg, "no members");
+ return (GENENT_PARSEERR);
+ }
+ *s++ = 0;
+ ecol[2].ec_value.ec_value_val = t;
+ ecol[2].ec_value.ec_value_len = strlen(t)+1;
+ t = s;
+
+ /*
+ * members (col 3)
+ */
+ ecol[3].ec_value.ec_value_val = t;
+ ecol[3].ec_value.ec_value_len = strlen(t)+1;
+
+ /*
+ * build entry
+ */
+ eobj = nis_default_obj;
+ eobj.zo_data.zo_type = NIS_ENTRY_OBJ;
+ eobj.EN_data.en_type = ta_type;
+ eobj.EN_data.en_cols.en_cols_val = ecol;
+ eobj.EN_data.en_cols.en_cols_len = 4;
+
+ if (!cback)
+ cback = addentry;
+
+ if ((*cback)(ta_name, &eobj, udata, 0))
+ return (GENENT_CBERR);
+
+ return (GENENT_OK);
+}
+
+void
+dump_group(res)
+ nis_result *res;
+{
+ int i;
+ char *c0, *c1, *c2, *c3;
+
+ for (i = 0; i < res->objects.objects_len; i++) {
+ if (c0 = OBJ_COL_VAL(i, 0)) {
+ c1 = OBJ_COL_VAL(i, 1);
+ c1 = c1 ? c1 : null_string;
+ c2 = OBJ_COL_VAL(i, 2);
+ c2 = c2 ? c2 : null_string;
+ c3 = OBJ_COL_VAL(i, 3);
+ c3 = c3 ? c3 : null_string;
+ printf("%s:%s:%s:%s\n", c0, c1, c2, c3);
+ }
+ }
+}
+
+
+/*
+ * /etc/netmasks
+ * nis+ table: (netmasks_tbl) number, mask, comment
+ *
+ */
+
+int
+genent_netmasks(line, cback, udata)
+ char *line;
+ int (*cback)();
+ void *udata;
+{
+ char buf[BUFSIZ+1];
+ char *t;
+ nis_object eobj;
+ entry_col ecol[3];
+ char *addr, aname[NIS_MAXNAMELEN];
+
+ /*
+ * don't clobber our argument
+ */
+ if (strlen(line) >= sizeof (buf)) {
+ strcpy(parse_err_msg, "line too long");
+ return (GENENT_PARSEERR);
+ }
+ strcpy(buf, line);
+
+ /*
+ * clear column data
+ */
+ memset((char *)ecol, 0, sizeof (ecol));
+
+ /*
+ * comment (col 2)
+ */
+ t = strchr(buf, '#');
+ if (t) {
+ *t++ = 0;
+ ecol[2].ec_value.ec_value_val = t;
+ ecol[2].ec_value.ec_value_len = strlen(t)+1;
+ } else {
+ ecol[2].ec_value.ec_value_val = 0;
+ ecol[2].ec_value.ec_value_len = 0;
+ }
+
+ /*
+ * addr(col 0)
+ */
+ if ((t = strtok(buf, " \t")) == 0) {
+ strcpy(parse_err_msg, "no mask");
+ return (GENENT_PARSEERR);
+ }
+ ecol[0].ec_value.ec_value_val = t;
+ ecol[0].ec_value.ec_value_len = strlen(t)+1;
+ addr = t;
+
+ /*
+ * mask (col 1)
+ */
+ if ((t = strtok(NULL, " \t")) == 0) {
+ strcpy(parse_err_msg, "no mask");
+ return (GENENT_PARSEERR);
+ }
+ ecol[1].ec_value.ec_value_val = t;
+ ecol[1].ec_value.ec_value_len = strlen(t)+1;
+
+ /*
+ * build entry
+ */
+ eobj = nis_default_obj;
+ eobj.zo_data.zo_type = NIS_ENTRY_OBJ;
+ eobj.EN_data.en_type = ta_type;
+ eobj.EN_data.en_cols.en_cols_val = ecol;
+ eobj.EN_data.en_cols.en_cols_len = 3;
+
+ if (!cback)
+ cback = addentry;
+
+ /* specify entry by addr alone (ignore mask) */
+ sprintf(aname, "[addr=%s],%s", addr, ta_name);
+
+ if ((*cback)(aname, &eobj, udata, 0))
+ return (GENENT_CBERR);
+
+ return (GENENT_OK);
+}
+
+void
+dump_netmasks(res)
+ nis_result *res;
+{
+ int i;
+ char buf[BUFSIZ+1], *c0, *c1, *c2;
+
+ for (i = 0; i < res->objects.objects_len; i++) {
+ if ((c0 = OBJ_COL_VAL(i, 0)) && (c1 = OBJ_COL_VAL(i, 1))) {
+ strcpy(buf, c0);
+ strcat(buf, tabtocol(buf, 2));
+ strcat(buf, c1);
+ if ((c2 = OBJ_COL_VAL(i, 2)) &&
+ !blankline(c2)) {
+ strcat(buf, tabtocol(buf, 5));
+ strcat(buf, "#");
+ strcat(buf, c2);
+ }
+ printf("%s\n", buf);
+ }
+ }
+}
+
+
+/*
+ * /etc/networks
+ * nis+ table: (networks_tbl) cname, name, addr, comment
+ *
+ */
+
+int
+genent_networks(line, cback, udata)
+ char *line;
+ int (*cback)();
+ void *udata;
+{
+ char buf[BUFSIZ+1];
+ char *t;
+ nis_object eobj;
+ entry_col ecol[4];
+ char *cname;
+
+ /*
+ * don't clobber our argument
+ */
+ if (strlen(line) >= sizeof (buf)) {
+ strcpy(parse_err_msg, "line too long");
+ return (GENENT_PARSEERR);
+ }
+ strcpy(buf, line);
+
+ /*
+ * clear column data
+ */
+ memset((char *)ecol, 0, sizeof (ecol));
+
+ /*
+ * comment (col 3)
+ */
+ t = strchr(buf, '#');
+ if (t) {
+ *t++ = 0;
+ ecol[3].ec_value.ec_value_val = t;
+ ecol[3].ec_value.ec_value_len = strlen(t)+1;
+ } else {
+ ecol[3].ec_value.ec_value_val = 0;
+ ecol[3].ec_value.ec_value_len = 0;
+ }
+
+ /*
+ * cname(col 0)
+ */
+ if ((t = strtok(buf, " \t")) == 0) {
+ strcpy(parse_err_msg, "no number");
+ return (GENENT_PARSEERR);
+ }
+ ecol[0].ec_value.ec_value_val = t;
+ ecol[0].ec_value.ec_value_len = strlen(t)+1;
+ cname = t;
+
+ /*
+ * number (col 2)
+ */
+ if ((t = strtok(NULL, " \t")) == 0) {
+ strcpy(parse_err_msg, "no number");
+ return (GENENT_PARSEERR);
+ }
+ ecol[2].ec_value.ec_value_val = t;
+ ecol[2].ec_value.ec_value_len = strlen(t)+1;
+
+ /*
+ * build entry
+ */
+ eobj = nis_default_obj;
+ eobj.zo_data.zo_type = NIS_ENTRY_OBJ;
+ eobj.EN_data.en_type = ta_type;
+ eobj.EN_data.en_cols.en_cols_val = ecol;
+ eobj.EN_data.en_cols.en_cols_len = 4;
+
+ if (!cback)
+ cback = addentry;
+
+ /*
+ * name (col 1)
+ */
+ t = cname;
+ do {
+ /*
+ * don't clobber comment in canonical entry
+ */
+ if (t != cname && strcasecmp(t, cname) == 0)
+ continue;
+
+ ecol[1].ec_value.ec_value_val = t;
+ ecol[1].ec_value.ec_value_len = strlen(t)+1;
+
+ if ((*cback)(ta_name, &eobj, udata, 0))
+ return (GENENT_CBERR);
+
+ /*
+ * only put comment in canonical entry
+ */
+ ecol[3].ec_value.ec_value_val = 0;
+ ecol[3].ec_value.ec_value_len = 0;
+
+ } while (t = strtok(NULL, " \t"));
+
+ return (GENENT_OK);
+}
+
+void
+dump_networks(res)
+ nis_result *res;
+{
+ int i, j, a;
+ char buf[BUFSIZ+1], *c0, *c1, *c2, *c3, *c0a, *c1a;
+
+ for (i = 0; i < res->objects.objects_len; i++) {
+ if ((c0 = OBJ_COL_VAL(i, 0)) && *c0 &&
+ (c1 = OBJ_COL_VAL(i, 1)) && (c2 = OBJ_COL_VAL(i, 2)) &&
+ (strcasecmp(c0, c1) == 0)) {
+ strcpy(buf, c0);
+ strcat(buf, tabtocol(buf, 2));
+ strcat(buf, c2);
+ for (a = j = 0; j < res->objects.objects_len; j++)
+ if (j != i &&
+ (c0a = OBJ_COL_VAL(j, 0)) && *c0a &&
+ (c1a = OBJ_COL_VAL(j, 1)) &&
+ strcasecmp(c0, c0a) == 0) {
+ if (a++ > 0)
+ strcat(buf, " ");
+ else
+ strcat(buf, tabtocol(buf, 4));
+ strcat(buf, c1a);
+ *c0a = 0;
+ }
+ if ((c3 = OBJ_COL_VAL(i, 3)) && !blankline(c3)) {
+ strcat(buf, tabtocol(buf, 6));
+ strcat(buf, "#");
+ strcat(buf, c3);
+ }
+ *c0 = 0;
+ printf("%s\n", buf);
+ }
+ }
+}
+
+
+/*
+ * /etc/protocols
+ * nis+ table: (protocols_tbl) cname, name, number, comment
+ *
+ */
+
+int
+genent_protocols(line, cback, udata)
+ char *line;
+ int (*cback)();
+ void *udata;
+{
+ char buf[BUFSIZ+1];
+ char *t;
+ nis_object eobj;
+ entry_col ecol[4];
+ char *cname;
+
+ /*
+ * don't clobber our argument
+ */
+ if (strlen(line) >= sizeof (buf)) {
+ strcpy(parse_err_msg, "line too long");
+ return (GENENT_PARSEERR);
+ }
+ strcpy(buf, line);
+
+ /*
+ * clear column data
+ */
+ memset((char *)ecol, 0, sizeof (ecol));
+
+ /*
+ * comment (col 3)
+ */
+ t = strchr(buf, '#');
+ if (t) {
+ *t++ = 0;
+ ecol[3].ec_value.ec_value_val = t;
+ ecol[3].ec_value.ec_value_len = strlen(t)+1;
+ } else {
+ ecol[3].ec_value.ec_value_val = 0;
+ ecol[3].ec_value.ec_value_len = 0;
+ }
+
+ /*
+ * cname(col 0)
+ */
+ if ((t = strtok(buf, " \t")) == 0) {
+ strcpy(parse_err_msg, "no number");
+ return (GENENT_PARSEERR);
+ }
+ ecol[0].ec_value.ec_value_val = t;
+ ecol[0].ec_value.ec_value_len = strlen(t)+1;
+ cname = t;
+
+ /*
+ * number (col 2)
+ */
+ if ((t = strtok(NULL, " \t")) == 0) {
+ strcpy(parse_err_msg, "no number");
+ return (GENENT_PARSEERR);
+ }
+ ecol[2].ec_value.ec_value_val = t;
+ ecol[2].ec_value.ec_value_len = strlen(t)+1;
+
+ /*
+ * build entry
+ */
+ eobj = nis_default_obj;
+ eobj.zo_data.zo_type = NIS_ENTRY_OBJ;
+ eobj.EN_data.en_type = ta_type;
+ eobj.EN_data.en_cols.en_cols_val = ecol;
+ eobj.EN_data.en_cols.en_cols_len = 4;
+
+ if (!cback)
+ cback = addentry;
+
+ /*
+ * name (col 1)
+ */
+ t = cname;
+ do {
+ /*
+ * don't clobber comment in canonical entry
+ */
+ if (t != cname && strcasecmp(t, cname) == 0)
+ continue;
+
+ ecol[1].ec_value.ec_value_val = t;
+ ecol[1].ec_value.ec_value_len = strlen(t)+1;
+
+ if ((*cback)(ta_name, &eobj, udata, 0))
+ return (GENENT_CBERR);
+
+ /*
+ * only put comment in canonical entry
+ */
+ ecol[3].ec_value.ec_value_val = 0;
+ ecol[3].ec_value.ec_value_len = 0;
+
+ } while (t = strtok(NULL, " \t"));
+
+ return (GENENT_OK);
+}
+
+void
+dump_protocols(res)
+ nis_result *res;
+{
+ int i, j, a;
+ char buf[BUFSIZ+1], *c0, *c1, *c2, *c3, *c0a, *c1a;
+
+ for (i = 0; i < res->objects.objects_len; i++) {
+ if ((c0 = OBJ_COL_VAL(i, 0)) && *c0 &&
+ (c1 = OBJ_COL_VAL(i, 1)) && (c2 = OBJ_COL_VAL(i, 2)) &&
+ (strcasecmp(c0, c1) == 0)) {
+ strcpy(buf, c0);
+ strcat(buf, tabtocol(buf, 2));
+ strcat(buf, c2);
+ for (a = j = 0; j < res->objects.objects_len; j++)
+ if (j != i &&
+ (c0a = OBJ_COL_VAL(j, 0)) && *c0a &&
+ (c1a = OBJ_COL_VAL(j, 1)) &&
+ strcasecmp(c0, c0a) == 0) {
+ if (a++ > 0)
+ strcat(buf, " ");
+ else
+ strcat(buf, tabtocol(buf, 4));
+ strcat(buf, c1a);
+ *c0a = 0;
+ }
+ if ((c3 = OBJ_COL_VAL(i, 3)) && !blankline(c3)) {
+ strcat(buf, tabtocol(buf, 6));
+ strcat(buf, "#");
+ strcat(buf, c3);
+ }
+ *c0 = 0;
+ printf("%s\n", buf);
+ }
+ }
+}
+
+
+/*
+ * /etc/rpc
+ * nis+ table: (rpc_tbl) cname, name, number, comment
+ *
+ */
+
+int
+genent_rpc(line, cback, udata)
+ char *line;
+ int (*cback)();
+ void *udata;
+{
+ char buf[BUFSIZ+1];
+ char *t;
+ nis_object eobj;
+ entry_col ecol[4];
+ char *cname;
+
+ /*
+ * don't clobber our argument
+ */
+ if (strlen(line) >= sizeof (buf)) {
+ strcpy(parse_err_msg, "line too long");
+ return (GENENT_PARSEERR);
+ }
+ strcpy(buf, line);
+
+ /*
+ * clear column data
+ */
+ memset((char *)ecol, 0, sizeof (ecol));
+
+ /*
+ * comment (col 3)
+ */
+ t = strchr(buf, '#');
+ if (t) {
+ *t++ = 0;
+ ecol[3].ec_value.ec_value_val = t;
+ ecol[3].ec_value.ec_value_len = strlen(t)+1;
+ } else {
+ ecol[3].ec_value.ec_value_val = 0;
+ ecol[3].ec_value.ec_value_len = 0;
+ }
+
+ /*
+ * cname(col 0)
+ */
+ if ((t = strtok(buf, " \t")) == 0) {
+ strcpy(parse_err_msg, "no number");
+ return (GENENT_PARSEERR);
+ }
+ ecol[0].ec_value.ec_value_val = t;
+ ecol[0].ec_value.ec_value_len = strlen(t)+1;
+ cname = t;
+
+ /*
+ * number (col 2)
+ */
+ if ((t = strtok(NULL, " \t")) == 0) {
+ strcpy(parse_err_msg, "no number");
+ return (GENENT_PARSEERR);
+ }
+ ecol[2].ec_value.ec_value_val = t;
+ ecol[2].ec_value.ec_value_len = strlen(t)+1;
+
+ /*
+ * build entry
+ */
+ eobj = nis_default_obj;
+ eobj.zo_data.zo_type = NIS_ENTRY_OBJ;
+ eobj.EN_data.en_type = ta_type;
+ eobj.EN_data.en_cols.en_cols_val = ecol;
+ eobj.EN_data.en_cols.en_cols_len = 4;
+
+ if (!cback)
+ cback = addentry;
+
+ /*
+ * name (col 1)
+ */
+ t = cname;
+ do {
+ /*
+ * don't clobber comment in canonical entry
+ */
+ if (t != cname && strcasecmp(t, cname) == 0)
+ continue;
+
+ ecol[1].ec_value.ec_value_val = t;
+ ecol[1].ec_value.ec_value_len = strlen(t)+1;
+
+ if ((*cback)(ta_name, &eobj, udata, 0))
+ return (GENENT_CBERR);
+
+ /*
+ * only put comment in canonical entry
+ */
+ ecol[3].ec_value.ec_value_val = 0;
+ ecol[3].ec_value.ec_value_len = 0;
+
+ } while (t = strtok(NULL, " \t"));
+
+ return (GENENT_OK);
+}
+
+void
+dump_rpc(res)
+ nis_result *res;
+{
+ int i, j, a;
+ char buf[BUFSIZ+1], *c0, *c1, *c2, *c3, *c0a, *c1a;
+
+ for (i = 0; i < res->objects.objects_len; i++) {
+ if ((c0 = OBJ_COL_VAL(i, 0)) && *c0 &&
+ (c1 = OBJ_COL_VAL(i, 1)) && (c2 = OBJ_COL_VAL(i, 2)) &&
+ (strcasecmp(c0, c1) == 0)) {
+ strcpy(buf, c0);
+ strcat(buf, tabtocol(buf, 2));
+ strcat(buf, c2);
+ for (a = j = 0; j < res->objects.objects_len; j++)
+ if (j != i &&
+ (c0a = OBJ_COL_VAL(j, 0)) && *c0a &&
+ (c1a = OBJ_COL_VAL(j, 1)) &&
+ strcasecmp(c0, c0a) == 0) {
+ if (a++ > 0)
+ strcat(buf, " ");
+ else
+ strcat(buf, tabtocol(buf, 4));
+ strcat(buf, c1a);
+ *c0a = 0;
+ }
+ if ((c3 = OBJ_COL_VAL(i, 3)) &&
+ !blankline(c3)) {
+ strcat(buf, tabtocol(buf, 6));
+ strcat(buf, "#");
+ strcat(buf, c3);
+ }
+ *c0 = 0;
+ printf("%s\n", buf);
+ }
+ }
+}
+
+
+/*
+ * /etc/services
+ * nis+ table: (services_tbl) cname, name, proto, port, comment
+ *
+ */
+
+int
+genent_services(line, cback, udata)
+ char *line;
+ int (*cback)();
+ void *udata;
+{
+ char buf[BUFSIZ+1];
+ char *t, *p;
+ nis_object eobj;
+ entry_col ecol[5];
+ char *cname;
+
+ /*
+ * don't clobber our argument
+ */
+ if (strlen(line) >= sizeof (buf)) {
+ strcpy(parse_err_msg, "line too long");
+ return (GENENT_PARSEERR);
+ }
+ strcpy(buf, line);
+
+ /*
+ * clear column data
+ */
+ memset((char *)ecol, 0, sizeof (ecol));
+
+ /*
+ * comment (col 4)
+ */
+ t = strchr(buf, '#');
+ if (t) {
+ *t++ = 0;
+ ecol[4].ec_value.ec_value_val = t;
+ ecol[4].ec_value.ec_value_len = strlen(t)+1;
+ } else {
+ ecol[4].ec_value.ec_value_val = 0;
+ ecol[4].ec_value.ec_value_len = 0;
+ }
+
+ /*
+ * cname(col 0)
+ */
+ if ((t = strtok(buf, " \t")) == 0) {
+ strcpy(parse_err_msg, "no port");
+ return (GENENT_PARSEERR);
+ }
+ ecol[0].ec_value.ec_value_val = t;
+ ecol[0].ec_value.ec_value_len = strlen(t)+1;
+ cname = t;
+
+ /*
+ * port (col 3)
+ */
+ if ((t = strtok(NULL, " \t")) == 0) {
+ strcpy(parse_err_msg, "no protocol");
+ return (GENENT_PARSEERR);
+ }
+ if ((p = strchr(t, '/')) == 0) {
+ strcpy(parse_err_msg, "bad port/proto");
+ return (GENENT_PARSEERR);
+ }
+ *(p++) = 0;
+ ecol[3].ec_value.ec_value_val = t;
+ ecol[3].ec_value.ec_value_len = strlen(t)+1;
+
+ /*
+ * proto (col 2)
+ */
+ ecol[2].ec_value.ec_value_val = p;
+ ecol[2].ec_value.ec_value_len = strlen(p)+1;
+
+ /*
+ * build entry
+ */
+ eobj = nis_default_obj;
+ eobj.zo_data.zo_type = NIS_ENTRY_OBJ;
+ eobj.EN_data.en_type = ta_type;
+ eobj.EN_data.en_cols.en_cols_val = ecol;
+ eobj.EN_data.en_cols.en_cols_len = 5;
+
+ if (!cback)
+ cback = addentry;
+
+ /*
+ * name (col 1)
+ */
+ t = cname;
+ do {
+ /*
+ * don't clobber comment in canonical entry
+ */
+ if (t != cname && strcasecmp(t, cname) == 0)
+ continue;
+
+ ecol[1].ec_value.ec_value_val = t;
+ ecol[1].ec_value.ec_value_len = strlen(t)+1;
+
+ if ((*cback)(ta_name, &eobj, udata, 0))
+ return (GENENT_CBERR);
+
+ /*
+ * only put comment in canonical entry
+ */
+ ecol[4].ec_value.ec_value_val = 0;
+ ecol[4].ec_value.ec_value_len = 0;
+
+ } while (t = strtok(NULL, " \t"));
+
+ return (GENENT_OK);
+}
+
+/*
+ * List all of the entries that match the cname, proto, and port
+ * of the passed entry.
+ */
+nis_result *
+dump_match_services(table, entry)
+ nis_name table;
+ nis_object *entry;
+{
+ nis_result *res = 0;
+ char *c0, *c1, *c2, *c3;
+ char srch[NIS_MAXNAMELEN];
+
+ if ((c0 = KEYVAL(0)) && *c0 &&
+ (c1 = KEYVAL(1)) && (c2 = KEYVAL(2)) && (c3 = KEYVAL(3)) &&
+ (strcasecmp(c0, c1) == 0)) {
+ sprintf(srch, "[cname=%s,proto=%s,port=%s],%s",
+ c0, c2, c3, table);
+ res = nis_list(srch, allres|master, 0, 0);
+ }
+
+ return (res);
+}
+
+void
+dump_services(res)
+ nis_result *res;
+{
+ int i, j, a;
+ char buf[BUFSIZ+1], *c0, *c1, *c2, *c3, *c4, *c0a, *c1a, *c2a, *c3a;
+
+ for (i = 0; i < res->objects.objects_len; i++) {
+ if ((c0 = OBJ_COL_VAL(i, 0)) && *c0 &&
+ (c1 = OBJ_COL_VAL(i, 1)) && (c2 = OBJ_COL_VAL(i, 2)) &&
+ (c3 = OBJ_COL_VAL(i, 3)) && (strcasecmp(c0, c1) == 0)) {
+ strcpy(buf, c0);
+ strcat(buf, tabtocol(buf, 2));
+ strcat(buf, c3);
+ strcat(buf, "/");
+ strcat(buf, c2);
+ for (a = j = 0; j < res->objects.objects_len; j++)
+ if (j != i &&
+ (c0a = OBJ_COL_VAL(j, 0)) && *c0a &&
+ (c1a = OBJ_COL_VAL(j, 1)) &&
+ (c2a = OBJ_COL_VAL(j, 2)) &&
+ (c3a = OBJ_COL_VAL(j, 3)) &&
+ strcasecmp(c0, c0a) == 0 &&
+ strcasecmp(c2, c2a) == 0 &&
+ strcasecmp(c3, c3a) == 0) {
+ if (a++ > 0)
+ strcat(buf, " ");
+ else
+ strcat(buf, tabtocol(buf, 4));
+ strcat(buf, c1a);
+ *c0a = 0;
+ }
+ if ((c4 = OBJ_COL_VAL(i, 4)) && !blankline(c4)) {
+ strcat(buf, tabtocol(buf, 6));
+ strcat(buf, "#");
+ strcat(buf, c4);
+ }
+ *c0 = 0;
+ printf("%s\n", buf);
+ }
+ }
+}
+
+char *
+dbmniskey_services(key)
+ datum key;
+{
+ static char buf[NIS_MAXNAMELEN];
+ char *p;
+
+ if ((p = strchr(key.dptr, '/')) == 0) {
+ sprintf(buf, "%s=%.*s", OBJ_COL_NAME(2),
+ key.dsize, key.dptr);
+ } else {
+ sprintf(buf, "%s=%.*s,%s=%.*s", OBJ_COL_NAME(2),
+ key.dsize-(p-key.dptr)-1, p+1,
+ OBJ_COL_NAME(3), p-key.dptr, key.dptr);
+ }
+
+ return (buf);
+}
+
+datum
+nisdbmkey_services(entry)
+ nis_object *entry;
+{
+ static char buf[BUFSIZ+1];
+ datum key;
+
+ sprintf(buf, "%s/%s", NKEYVAL(3), NKEYVAL(2));
+ key.dptr = buf;
+ key.dsize = strlen(buf);
+
+ return (key);
+}
+
+void
+printfkeystr_services(fstr, entry)
+ char *fstr;
+ nis_object *entry;
+{
+ char keystr[BUFSIZ];
+
+ if (strcasecmp(NKEYVAL(0), NKEYVAL(1)) == 0)
+ sprintf(keystr, "%s %s/%s", NKEYVAL(0), NKEYVAL(3),
+ NKEYVAL(2));
+ else
+ sprintf(keystr, "%s %s/%s (%s)", NKEYVAL(0), NKEYVAL(3),
+ NKEYVAL(2), NKEYVAL(1));
+
+ printf(fstr, keystr);
+}
+
+
+/*
+ * /etc/publickey
+ * /etc/netid
+ * nis+ table: (cred_tbl) cname, auth_type, auth_name, public_data,
+ * private_data
+ *
+ */
+
+int
+genent_publickey(line, cback, udata)
+ char *line;
+ int (*cback)();
+ void *udata;
+{
+ char buf[BUFSIZ+1], tmpbuf[BUFSIZ+1], cname[NIS_MAXNAMELEN];
+ char *t, *p;
+ nis_object eobj;
+ entry_col ecol[5];
+ int uid;
+ struct passwd *pwd;
+ char aname[NIS_MAXNAMELEN], auth_type[MECH_MAXATNAME+1];
+ keylen_t keylen;
+ algtype_t algtype;
+
+ /*
+ * don't clobber our argument
+ */
+ if (strlen(line) >= sizeof (buf)) {
+ strcpy(parse_err_msg, "line too long");
+ return (GENENT_PARSEERR);
+ }
+ strcpy(buf, line);
+
+ /*
+ * clear column data
+ */
+ memset((char *)ecol, 0, sizeof (ecol));
+
+ /*
+ * auth_name (col 2)
+ */
+ if ((t = strtok(buf, " \t")) == 0) {
+ strcpy(parse_err_msg, "no cname");
+ return (GENENT_PARSEERR);
+ }
+ ecol[2].ec_value.ec_value_val = t;
+ ecol[2].ec_value.ec_value_len = strlen(t)+1;
+
+ /*
+ * Special case: /etc/publickey usually has an entry
+ * for principal "nobody". We skip it because it does
+ * not apply to NIS+.
+ */
+ if (strcmp(t, "nobody") == 0)
+ return (GENENT_OK);
+
+ /*
+ * cname (col 0)
+ */
+ if (strncmp(t, "unix.", 5)) {
+ strcpy(parse_err_msg, "bad cname");
+ return (GENENT_PARSEERR);
+ }
+ strcpy(tmpbuf, &(t[5]));
+ if ((p = strchr(tmpbuf, '@')) == 0) {
+ strcpy(parse_err_msg, "bad cname");
+ return (GENENT_PARSEERR);
+ }
+ *(p++) = 0;
+ if (isdigit(*tmpbuf)) {
+ nis_error st;
+ extern struct passwd *getpwuid_nisplus_master();
+
+ uid = atoi(tmpbuf);
+ /*
+ * don't generate entries for uids without passwd entries
+ * first try it in the regular way, and if that fails, then
+ * a lookup to the NIS+ Master server to be sure.
+ */
+ if (((pwd = getpwuid(uid)) == 0) &&
+ ((pwd = getpwuid_nisplus_master(nisdomain,
+ uid, &st)) == 0)) {
+ fprintf(stderr,
+ "can't map uid %d to username, skipping\n",
+ uid);
+ return (GENENT_OK);
+ }
+ strcpy(cname, pwd->pw_name);
+ } else
+ strcpy(cname, tmpbuf);
+ if (*p != '.')
+ strcat(cname, ".");
+ strcat(cname, p);
+ if (cname[strlen(cname)-1] != '.')
+ strcat(cname, ".");
+ /*
+ * XXX complain if domain in netname not same as nisdomain?
+ */
+ ecol[0].ec_value.ec_value_val = cname;
+ ecol[0].ec_value.ec_value_len = strlen(cname)+1;
+
+ /*
+ * public_data (col 3)
+ */
+ if ((t = strtok(NULL, " \t")) == 0) {
+ strcpy(parse_err_msg, "no private_data");
+ return (GENENT_PARSEERR);
+ }
+ if ((p = strchr(t, ':')) == 0) {
+ strcpy(parse_err_msg, "bad public_data");
+ return (GENENT_PARSEERR);
+ }
+ *(p++) = 0;
+ ecol[3].ec_value.ec_value_val = t;
+ ecol[3].ec_value.ec_value_len = strlen(t)+1;
+ keylen = (strlen(t) / 2) * 8;
+
+ /*
+ * private_data (col 4) and algtype extraction
+ */
+ if (*p == ':')
+ p++;
+ t = p;
+ if (!(t = strchr(t, ':'))) {
+ if (!oldpubkeymode)
+ printf(
+ "WARNING: No algtype data found in publickey file, assuming 0\n");
+ algtype = 0;
+ } else {
+ *t = '\0';
+ t++;
+ algtype = atoi(t);
+ }
+ ecol[4].ec_value.ec_value_val = p;
+ ecol[4].ec_value.ec_value_len = strlen(p)+1;
+ ecol[4].ec_flags = EN_CRYPT;
+
+ /*
+ * auth_type (col 1)
+ */
+ if (!(__nis_keyalg2authtype(keylen, algtype, auth_type,
+ MECH_MAXATNAME)))
+ return (GENENT_PARSEERR);
+
+ ecol[1].ec_value.ec_value_val = auth_type;
+ ecol[1].ec_value.ec_value_len = strlen(auth_type)+1;
+
+ /*
+ * build entry
+ */
+ eobj = nis_default_obj;
+ eobj.zo_owner = cname;
+ eobj.zo_access = NIS_OWNER_MASK(NIS_READ_ACC) |
+ NIS_GROUP_MASK(NIS_ALL_ACC);
+ eobj.zo_data.zo_type = NIS_ENTRY_OBJ;
+ eobj.EN_data.en_type = ta_type;
+ eobj.EN_data.en_cols.en_cols_val = ecol;
+ eobj.EN_data.en_cols.en_cols_len = 5;
+
+ if (!cback)
+ cback = addentry;
+
+ /* specify entry by cname/auth_type alone (ignore auth_name) */
+ sprintf(aname, "[cname=%s,auth_type=%s],%s", cname, auth_type,
+ ta_name);
+
+ if ((*cback)(aname, &eobj, udata, 0))
+ return (GENENT_CBERR);
+
+ return (GENENT_OK);
+}
+
+void
+dump_publickey(res)
+ nis_result *res;
+{
+ int i, mcount = 0, authlistcount = 0, filter = 0;
+ char buf[BUFSIZ+1], *c0, *c1, *c2, *c3, *c4;
+ char **authlist = NULL;
+
+ /*
+ * If RPCSEC_GSS is configured on this system, a list of
+ * auth_types needs to be generated to filter entries (since
+ * dumptable has ignored tt->clrsrch). Otherwise, the
+ * nis_list has prefiltered out the extra entries in
+ * dumptable.
+ */
+ if (mechs) {
+ while (mechs[mcount]) {
+ char *authtype;
+
+ authtype = (char *)malloc(MECH_MAXATNAME+1);
+ if (__nis_mechalias2authtype(mechs[mcount]->alias,
+ authtype,
+ MECH_MAXATNAME)) {
+ authlist = (char **)realloc(authlist,
+ sizeof (char *) * (authlistcount + 2));
+ authlist[authlistcount] = authtype;
+ authlistcount++;
+ } else
+ free(authtype);
+ mcount++;
+ }
+ if (authlistcount) {
+ authlist[authlistcount] = NULL;
+ filter++;
+ }
+ }
+
+ for (i = 0; i < res->objects.objects_len; i++) {
+ if ((c0 = OBJ_COL_VAL(i, 0)) && (c1 = OBJ_COL_VAL(i, 1)) &&
+ (c2 = OBJ_COL_VAL(i, 2)) && (c3 = OBJ_COL_VAL(i, 3)) &&
+ (c4 = OBJ_COL_VAL(i, 4))) {
+ /*
+ * If we have a filter list, then we walk
+ * through it until we find a matching
+ * auth_type. Otherwise we go on to the next
+ * result entry.
+ */
+ if (filter) {
+ int gotit = 0;
+
+ authlistcount = 0;
+ while (authlist[authlistcount]) {
+ if (strcmp(authlist[authlistcount],
+ c1) == 0) {
+ gotit++;
+ break;
+ }
+ authlistcount++;
+ }
+ if (!gotit)
+ continue;
+ }
+
+ strcpy(buf, c2);
+ strcat(buf, tabtocol(buf, 2));
+ strcat(buf, c3);
+ if (strchr(c3, ':') == 0)
+ strcat(buf, ":");
+ strcat(buf, c4);
+ if (!oldpubkeymode) {
+ if (strcmp(c1, AUTH_DES_AUTH_TYPE) == 0)
+ strcat(buf, ":0");
+ else {
+ char algbuf[MECH_MAXATNAME], *a;
+
+ strcpy(algbuf, c1);
+ if (a = strchr(algbuf, '-')) {
+ *a = ':';
+ strcat(buf, a);
+ } else
+ strcat(buf, ":0");
+ }
+ }
+ printf("%s\n", buf);
+ }
+ }
+}
+
+int
+genent_netid(line, cback, udata)
+ char *line;
+ int (*cback)();
+ void *udata;
+{
+ char buf[BUFSIZ+1], tmpbuf[BUFSIZ+1], cname[NIS_MAXNAMELEN];
+ char *t, *p;
+ nis_object eobj;
+ entry_col ecol[5];
+ int uid;
+ struct passwd *pwd;
+
+ /*
+ * don't clobber our argument
+ */
+ if (strlen(line) >= sizeof (buf)) {
+ strcpy(parse_err_msg, "line too long");
+ return (GENENT_PARSEERR);
+ }
+ strcpy(buf, line);
+
+ /*
+ * clear column data
+ */
+ memset((char *)ecol, 0, sizeof (ecol));
+
+ /*
+ * cname (col 0)
+ */
+ if ((t = strtok(buf, " \t")) == 0) {
+ strcpy(parse_err_msg, "no auth_name");
+ return (GENENT_PARSEERR);
+ }
+ if (strncmp(t, "unix.", 5)) {
+ strcpy(parse_err_msg, "bad cname");
+ return (GENENT_PARSEERR);
+ }
+ strcpy(tmpbuf, &(t[5]));
+ if ((t[5] == '@') || ((p = strchr(tmpbuf, '@')) == 0)) {
+ strcpy(parse_err_msg, "bad cname");
+ return (GENENT_PARSEERR);
+ }
+ *(p++) = 0;
+ if (isdigit(*tmpbuf)) {
+ nis_error st;
+ extern struct passwd *getpwuid_nisplus_master();
+
+ uid = atoi(tmpbuf);
+ /*
+ * don't generate entries for uids without passwd entries
+ * first try it in the regular way, and if that fails, then
+ * a lookup to the NIS+ Master server to be sure.
+ */
+ if (((pwd = getpwuid(uid)) == 0) &&
+ ((pwd = getpwuid_nisplus_master(nisdomain,
+ uid, &st)) == 0)) {
+ fprintf(stderr,
+ "can't map uid %d to username, skipping\n",
+ uid);
+ return (GENENT_OK);
+ }
+ strcpy(cname, pwd->pw_name);
+ } else
+ uid = 0;
+ /*
+ * don't generate LOCAL entries for root
+ */
+ if (uid == 0)
+ return (GENENT_OK);
+ if (*p != '.')
+ strcat(cname, ".");
+ strcat(cname, p);
+ if (cname[strlen(cname)-1] != '.')
+ strcat(cname, ".");
+ /*
+ * XXX complain if domain in netname not same as nisdomain?
+ */
+ ecol[0].ec_value.ec_value_val = cname;
+ ecol[0].ec_value.ec_value_len = strlen(cname)+1;
+
+ /*
+ * auth_name (col 2)
+ */
+ if ((t = strtok(NULL, " \t")) == 0) {
+ strcpy(parse_err_msg, "no public_data");
+ return (GENENT_PARSEERR);
+ }
+ if ((p = strchr(t, ':')) == 0) {
+ strcpy(parse_err_msg, "bad auth_name");
+ return (GENENT_PARSEERR);
+ }
+ *(p++) = 0;
+ ecol[2].ec_value.ec_value_val = t;
+ ecol[2].ec_value.ec_value_len = strlen(t)+1;
+
+ /*
+ * public_data (col 3)
+ */
+ ecol[3].ec_value.ec_value_val = p;
+ ecol[3].ec_value.ec_value_len = strlen(p)+1;
+
+ /*
+ * private_data (col 4)
+ */
+ ecol[4].ec_value.ec_value_val = 0;
+ ecol[4].ec_value.ec_value_len = 0;
+
+ /*
+ * auth_type (col 1)
+ */
+ ecol[1].ec_value.ec_value_val = "LOCAL";
+ ecol[1].ec_value.ec_value_len = 6;
+
+ /*
+ * build entry
+ */
+ eobj = nis_default_obj;
+ eobj.zo_data.zo_type = NIS_ENTRY_OBJ;
+ eobj.EN_data.en_type = ta_type;
+ eobj.EN_data.en_cols.en_cols_val = ecol;
+ eobj.EN_data.en_cols.en_cols_len = 5;
+
+ if (!cback)
+ cback = addentry;
+
+ if ((*cback)(ta_name, &eobj, udata, 0))
+ return (GENENT_CBERR);
+
+ return (GENENT_OK);
+}
+
+void
+dump_netid(res)
+ nis_result *res;
+{
+ int i, j;
+ char buf[BUFSIZ+1], *c0, *c1, *c2, *c3, *c4;
+
+ for (i = 0; i < res->objects.objects_len; i++) {
+ if ((c0 = OBJ_COL_VAL(i, 0)) && (c1 = OBJ_COL_VAL(i, 1)) &&
+ (c2 = OBJ_COL_VAL(i, 2)) && (c3 = OBJ_COL_VAL(i, 3))) {
+ nis_name objdom = nis_domain_of(c0);
+ size_t objdom_len = strlen(objdom);
+
+ if (objdom_len > 0) {
+ if (objdom[objdom_len - 1] == '.')
+ objdom[objdom_len - 1] = '\0';
+ }
+
+ sprintf(buf, "unix.%s@%s", c2, objdom);
+ strcat(buf, tabtocol(buf, 2));
+ strcat(buf, c2);
+ strcat(buf, ":");
+ strcat(buf, c3);
+ printf("%s\n", buf);
+ }
+ }
+}
+
+char *
+dbmniskey_netid(key)
+ datum key;
+{
+ static char buf[NIS_MAXNAMELEN];
+ int i;
+
+ strcpy(buf, "auth_name=xxx");
+
+ if (key.dsize < 7 || strncmp(key.dptr, "unix.", 5))
+ return (buf);
+ for (i = 5; i < key.dsize; i++)
+ if (key.dptr[i] == '@')
+ break;
+ if (i == 5 || i == key.dsize)
+ return (buf);
+ strncpy(&(buf[10]), &(key.dptr[5]), i-5);
+ buf[5+i] = 0;
+
+ return (buf);
+}
+
+datum
+nisdbmkey_netid(entry)
+ nis_object *entry;
+{
+ static char buf[BUFSIZ+1];
+ datum key;
+
+ sprintf(buf, "unix.%s@%s", NKEYVAL(2), nis_domain_of(NKEYVAL(0)));
+ buf[strlen(buf)-1] = 0;
+
+ key.dptr = buf;
+ key.dsize = strlen(buf);
+
+ return (key);
+}
+
+
+/*
+ * /etc/aliases
+ * nis+ table: (mail_aliases) alias, expansion, comments, options
+ *
+ * The handling of dbm keys and values for aliases is a little funny
+ * because the length field in a datum includes the terminating
+ * null (e.g., "name" would have length 5). The other types of dbm
+ * files do not include the terminating null.
+ */
+
+int
+genent_aliases(line, cback, udata)
+ char *line;
+ int (*cback)();
+ void *udata;
+{
+ char buf[BUFSIZ+1];
+ char *t;
+ nis_object eobj;
+ entry_col ecol[4];
+ char *alias, aname[NIS_MAXNAMELEN], key[NIS_MAXNAMELEN];
+
+ /*
+ * don't clobber our argument
+ */
+ if (strlen(line) >= sizeof (buf)) {
+ strcpy(parse_err_msg, "line too long");
+ return (GENENT_PARSEERR);
+ }
+ strcpy(buf, line);
+
+ /*
+ * clear column data
+ */
+ memset((char *)ecol, 0, sizeof (ecol));
+
+ /*
+ * alias(col 0)
+ */
+ if ((t = strtok_quotes(buf, " \t")) == 0) {
+ strcpy(parse_err_msg, "no expansion");
+ return (GENENT_PARSEERR);
+ }
+ ecol[0].ec_value.ec_value_val = t;
+ ecol[0].ec_value.ec_value_len = strlen(t)+1;
+ alias = t;
+
+ /*
+ * expansion (col 1)
+ */
+ if ((t = strtok_quotes(NULL, "")) == 0) {
+ strcpy(parse_err_msg, "no expansion");
+ return (GENENT_PARSEERR);
+ }
+ /*
+ * skip white space
+ */
+ t += strspn(t, " \t");
+ if (*t == 0) {
+ strcpy(parse_err_msg, "empty expansion");
+ return (GENENT_PARSEERR);
+ }
+ ecol[1].ec_value.ec_value_val = t;
+ ecol[1].ec_value.ec_value_len = strlen(t)+1;
+
+ /*
+ * comments (col 2)
+ */
+ ecol[2].ec_value.ec_value_val = 0;
+ ecol[2].ec_value.ec_value_len = 0;
+
+ /*
+ * options (col 3)
+ */
+ ecol[3].ec_value.ec_value_val = 0;
+ ecol[3].ec_value.ec_value_len = 0;
+
+ /*
+ * build entry
+ */
+ eobj = nis_default_obj;
+ eobj.zo_data.zo_type = NIS_ENTRY_OBJ;
+ eobj.EN_data.en_type = ta_type;
+ eobj.EN_data.en_cols.en_cols_val = ecol;
+ eobj.EN_data.en_cols.en_cols_len = 4;
+
+ if (!cback)
+ cback = addentry;
+
+ (void) __nis_quote_key(alias, key, sizeof (key));
+
+ /* specify entry by alias alone (ignore expansion) */
+ sprintf(aname, "[alias=%s],%s", key, ta_name);
+
+ if ((*cback)(aname, &eobj, udata, 0))
+ return (GENENT_CBERR);
+
+ return (GENENT_OK);
+}
+
+void
+dump_aliases(res)
+ nis_result *res;
+{
+ int i;
+ char *c0, *c1, *c2;
+
+ for (i = 0; i < res->objects.objects_len; i++) {
+ if ((c0 = OBJ_COL_VAL(i, 0)) && (c1 = OBJ_COL_VAL(i, 1))) {
+ if ((c2 = OBJ_COL_VAL(i, 2)) && !blankline(c2))
+ printf("# %s\n", c2);
+ printf("%s: %s\n", c0, c1);
+ }
+ }
+}
+
+datum
+nisdbmkey_aliases(entry)
+ nis_object *entry;
+{
+ static char buf[BUFSIZ+1];
+ datum key;
+
+ strcpy(buf, NKEYVAL(tt->niskeycol));
+
+ key.dptr = buf;
+ key.dsize = strlen(buf)+1;
+
+ return (key);
+}
+
+datum
+filedbmkey_aliases(line)
+ char *line;
+{
+ static char buf[BUFSIZ+1];
+ int i;
+ char *p;
+ datum key;
+
+ for (i = tt->filekeycol; i >= 0; i--) {
+ p = strpbrk_quotes(line, tt->filesep);
+ if (i > 0) {
+ if (p == 0) {
+ key.dptr = 0;
+ key.dsize = 0;
+ return (key);
+ }
+ line = p + strspn(p, tt->filesep);
+ }
+ }
+
+ if (p)
+ sprintf(buf, "%.*s", p-line, line);
+ else
+ strcpy(buf, line);
+
+ key.dptr = buf;
+ key.dsize = strlen(buf)+1;
+
+ return (key);
+}
+
+int
+filedbmline_aliases(line, etcf, lineno, loc)
+ struct line_buf *line;
+ FILE *etcf;
+ int *lineno;
+ struct file_loc *loc;
+{
+ char *p;
+ int c;
+ int len = 0;
+
+ loc->offset = ftell(etcf);
+ while (1) {
+ if (fget_line_at(line, len, etcf) == 0)
+ return (0);
+
+ if (lineno)
+ (*lineno)++;
+
+ len = strlen(line->str);
+ if (line->str[len-1] == '\n') {
+ line->str[len-1] = 0;
+ len -= 1;
+ }
+
+ /*
+ * A continuation line starts with a space or a tab.
+ * Continuation lines count even for commented lines.
+ */
+ if ((c = fgetc(etcf)) != EOF &&
+ ungetc(c, etcf) != EOF &&
+ (c == ' ' || c == '\t')) {
+ continue;
+ }
+
+ if (!blankline(line->str) && line->str[0] != '#')
+ break;
+
+ len = 0;
+ loc->offset = ftell(etcf);
+ }
+
+ if (p = strchr_quotes(line->str, ':'))
+ *p = ' ';
+
+ loc->size = len;
+ return (1);
+}
+
+/*
+ * Convert an alias line into key and content. Just like filetodbm_keyvalue
+ * but the key and data includes the terminating null.
+ */
+void
+filetodbm_aliases(line, key, content)
+ char *line;
+ datum *key;
+ datum *content;
+{
+ int i;
+ char *p;
+
+ for (i = tt->filekeycol; i >= 0; i--) {
+ p = strpbrk_quotes(line, tt->filesep);
+ if (i > 0) {
+ if (p == 0) {
+ key->dptr = 0;
+ key->dsize = 0;
+ return;
+ }
+ line = p + strspn(p, tt->filesep);
+ }
+ }
+
+ if (p) {
+ *p++ = '\0'; /* null-terminate key */
+ key->dptr = line;
+ key->dsize = p-line; /* include terminating null */
+ line = p + strspn(p, tt->filesep);
+ content->dptr = line;
+ content->dsize = strlen(line) + 1;
+ } else {
+ key->dptr = line;
+ key->dsize = strlen(line) + 1;
+ content->dptr = "";
+ content->dsize = 0;
+ }
+}
+
+/*
+ * key-value
+ * nis+ table: key, value
+ *
+ */
+
+int
+genent_keyvalue(line, cback, udata)
+ char *line;
+ int (*cback)();
+ void *udata;
+{
+ char buf[BUFSIZ+1];
+ char *t;
+ nis_object eobj;
+ entry_col ecol[2];
+
+ /*
+ * don't clobber our argument
+ */
+ if (strlen(line) >= sizeof (buf)) {
+ strcpy(parse_err_msg, "line too long");
+ return (GENENT_PARSEERR);
+ }
+ strcpy(buf, line);
+
+ /*
+ * clear column data
+ */
+ memset((char *)ecol, 0, sizeof (ecol));
+
+ /*
+ * key(col 0)
+ */
+ if ((t = strtok(buf, " \t")) == 0) {
+ strcpy(parse_err_msg, "no value");
+ return (GENENT_PARSEERR);
+ }
+ ecol[0].ec_value.ec_value_val = t;
+ ecol[0].ec_value.ec_value_len = strlen(t)+1;
+
+ /*
+ * value (col 1)
+ */
+ if ((t = strtok(NULL, "")) == 0)
+ t = "";
+ else
+ t += strspn(t, " \t"); /* skip white space */
+ ecol[1].ec_value.ec_value_val = t;
+ ecol[1].ec_value.ec_value_len = strlen(t)+1;
+
+ /*
+ * build entry
+ */
+ eobj = nis_default_obj;
+ eobj.zo_data.zo_type = NIS_ENTRY_OBJ;
+ eobj.EN_data.en_type = ta_type;
+ eobj.EN_data.en_cols.en_cols_val = ecol;
+ eobj.EN_data.en_cols.en_cols_len = 2;
+
+ if (!cback)
+ cback = addentry;
+
+ if ((*cback)(ta_name, &eobj, udata, 0))
+ return (GENENT_CBERR);
+
+ return (GENENT_OK);
+}
+
+void
+dump_keyvalue(res)
+ nis_result *res;
+{
+ int i, j;
+ char buf[BUFSIZ+1], *c0, *c1;
+
+ for (i = 0; i < res->objects.objects_len; i++) {
+ if (c0 = OBJ_COL_VAL(i, 0)) {
+ strcpy(buf, c0);
+ if (c1 = OBJ_COL_VAL(i, 1)) {
+ strcat(buf, tabtocol(buf, 2));
+ strcat(buf, c1);
+ }
+ printf("%s\n", buf);
+ }
+ }
+}
+
+/*
+ * /etc/netgroup
+ * nis+ table: (netgroup_tbl) name, group, host, user, domain, comment
+ *
+ */
+
+int
+genent_netgroup(line, cback, udata)
+ char *line;
+ int (*cback)();
+ void *udata;
+{
+ char buf[BIGBUF+1]; /* netgroup entries tend to be big */
+ char *t, *p;
+ nis_object eobj;
+ entry_col ecol[6];
+
+ /*
+ * don't clobber our argument
+ */
+ if (strlen(line) >= sizeof (buf)) {
+ strcpy(parse_err_msg, "line too long");
+ return (GENENT_PARSEERR);
+ }
+ strcpy(buf, line);
+
+ /*
+ * clear column data
+ */
+ memset((char *)ecol, 0, sizeof (ecol));
+
+ /*
+ * comment (col 5)
+ */
+ t = strchr(buf, '#');
+ if (t) {
+ *t++ = 0;
+ ecol[5].ec_value.ec_value_val = t;
+ ecol[5].ec_value.ec_value_len = strlen(t)+1;
+ } else {
+ ecol[5].ec_value.ec_value_val = 0;
+ ecol[5].ec_value.ec_value_len = 0;
+ }
+
+ /*
+ * name(col 0)
+ */
+ if ((t = strtok(buf, " \t")) == 0) {
+ return (GENENT_OK); /* empty line */
+ }
+ ecol[0].ec_value.ec_value_val = t;
+ ecol[0].ec_value.ec_value_len = strlen(t)+1;
+
+ /*
+ * membership list
+ */
+ if ((p = strtok(NULL, "")) == 0)
+ p = "";
+
+ /*
+ * build entry
+ */
+ eobj = nis_default_obj;
+ eobj.zo_data.zo_type = NIS_ENTRY_OBJ;
+ eobj.EN_data.en_type = ta_type;
+ eobj.EN_data.en_cols.en_cols_val = ecol;
+ eobj.EN_data.en_cols.en_cols_len = 6;
+
+ if (!cback)
+ cback = addentry;
+
+ /*
+ * group (col 1)
+ * host (col 2)
+ * user (col 3)
+ * domain (col 4)
+ */
+ while (*p) {
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ if (*p == '\0')
+ break;
+
+ if (*p == '(') {
+ /*
+ * group (col 1)
+ */
+ ecol[1].ec_value.ec_value_val = 0;
+ ecol[1].ec_value.ec_value_len = 0;
+
+ /*
+ * host (col 2)
+ */
+ p++;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ t = p;
+ while (*p != ' ' && *p != '\t' && *p != ',')
+ if (*p == '\0' || *p == ')') {
+ strcpy(parse_err_msg, "bad host");
+ return (GENENT_PARSEERR);
+ }
+ else
+ p++;
+ *p++ = 0;
+ ecol[2].ec_value.ec_value_val = t;
+ ecol[2].ec_value.ec_value_len = strlen(t)+1;
+
+
+ /*
+ * user (col 3)
+ */
+ while (*p == ' ' || *p == '\t')
+ p++;
+ t = p;
+ while (*p != ' ' && *p != '\t' && *p != ',')
+ if (*p == '\0' || *p == ')') {
+ strcpy(parse_err_msg, "bad user");
+ return (GENENT_PARSEERR);
+ }
+ else
+ p++;
+ *p++ = 0;
+ ecol[3].ec_value.ec_value_val = t;
+ ecol[3].ec_value.ec_value_len = strlen(t)+1;
+
+ /*
+ * domain (col 4)
+ */
+ while (*p == ' ' || *p == '\t')
+ p++;
+ t = p;
+ while (*p != ' ' && *p != '\t' && *p != ')')
+ if (*p == '\0' || *p == ',') {
+ strcpy(parse_err_msg, "bad domain");
+ return (GENENT_PARSEERR);
+ }
+ else
+ p++;
+ *p++ = 0;
+ ecol[4].ec_value.ec_value_val = t;
+ ecol[4].ec_value.ec_value_len = strlen(t)+1;
+
+ } else {
+ /*
+ * host (col 2)
+ */
+ ecol[2].ec_value.ec_value_val = 0;
+ ecol[2].ec_value.ec_value_len = 0;
+
+ /*
+ * user (col 3)
+ */
+ ecol[3].ec_value.ec_value_val = 0;
+ ecol[3].ec_value.ec_value_len = 0;
+
+ /*
+ * domain (col 4)
+ */
+ ecol[4].ec_value.ec_value_val = 0;
+ ecol[4].ec_value.ec_value_len = 0;
+
+ /*
+ * group (col 1)
+ */
+ t = p;
+ while (*p != ' ' && *p != '\t' && *p != '\0')
+ p++;
+ if (*p)
+ *p++ = 0;
+ ecol[1].ec_value.ec_value_val = t;
+ ecol[1].ec_value.ec_value_len = strlen(t)+1;
+ }
+
+ /*
+ * XXX should avoid adding duplicates of first entry in
+ * order to keep from clobbering comment
+ */
+
+ if ((*cback)(ta_name, &eobj, udata, 0))
+ return (GENENT_CBERR);
+
+ /*
+ * only put comment in first entry
+ */
+ ecol[5].ec_value.ec_value_val = 0;
+ ecol[5].ec_value.ec_value_len = 0;
+
+ };
+
+ return (GENENT_OK);
+}
+
+
+/*
+ * There is no notion of a canonical entry for netgroup, so
+ * we can't tell be looking at an entry if it has already
+ * been handled. So, we keep a table of all of the netgroups
+ * we have done so far.
+ */
+
+#define HASH_SIZE 64
+
+struct hashent {
+ struct hashent *next;
+ char *name;
+};
+
+struct hashent *netgroup_table[64];
+
+int
+hash(s)
+ char *s;
+{
+ int retval = 0;
+
+ while (*s)
+ retval += *s++;
+ return (retval % HASH_SIZE);
+}
+
+int
+did_netgroup(s)
+ char *s;
+{
+ int n;
+ struct hashent *hp;
+
+ n = hash(s);
+ for (hp = netgroup_table[n]; hp; hp = hp->next) {
+ if (strcmp(s, hp->name) == 0) {
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+void
+mark_netgroup(s)
+ char *s;
+{
+ int n;
+ struct hashent *hp;
+
+ hp = (struct hashent *)malloc(sizeof (*hp));
+ if (hp == 0) {
+ fprintf(stderr, "mark_netgroup: out of memory\n");
+ exit(1);
+ }
+
+ hp->name = strdup(s);
+ if (hp->name == 0) {
+ fprintf(stderr, "mark_netgroup: out of memory\n");
+ exit(1);
+ }
+
+ n = hash(s);
+ hp->next = netgroup_table[n];
+ netgroup_table[n] = hp;
+}
+
+/*
+ * List all of the entries that match the name of the passed entry.
+ */
+nis_result *
+dump_match_netgroup(table, entry)
+ nis_name table;
+ nis_object *entry;
+{
+ nis_result *res = 0;
+ char *c0, *c1, *c2, *c3, *c4;
+ char srch[NIS_MAXNAMELEN];
+
+ c0 = KEYVAL(0);
+ c1 = KEYVAL(1);
+ c2 = KEYVAL(2);
+ c3 = KEYVAL(3);
+ c4 = KEYVAL(4);
+ if (c0 && *c0 && (c1 || (c3 && c4)) && ! did_netgroup(c0)) {
+ sprintf(srch, "[name=%s],%s", c0, table);
+ res = nis_list(srch, allres|master, 0, 0);
+ mark_netgroup(c0);
+ }
+
+ return (res);
+}
+
+void
+dump_netgroup(res)
+ nis_result *res;
+{
+ int i, j;
+ char *c0, *c1, *c2, *c3, *c4, *c5;
+ char *c0a, *c1a, *c2a, *c3a, *c4a;
+ static struct line_buf line;
+
+ for (i = 0; i < res->objects.objects_len; i++) {
+ if ((c0 = OBJ_COL_VAL(i, 0)) && *c0 &&
+ ((c1 = OBJ_COL_VAL(i, 1)) ||
+ ((c2 = OBJ_COL_VAL_NO_NULL(i, 2)) &&
+ (c3 = OBJ_COL_VAL(i, 3)) && (c4 = OBJ_COL_VAL(i, 4))))) {
+ print2buf_init(&line);
+ print2buf(&line, c0);
+ print2buf(&line, tabtocol(line.str, 2));
+ if (c1)
+ print2buf(&line, c1);
+ else {
+ print2buf(&line, "(");
+ print2buf(&line, c2);
+ print2buf(&line, ",");
+ print2buf(&line, c3);
+ print2buf(&line, ",");
+ print2buf(&line, c4);
+ print2buf(&line, ")");
+ }
+ c5 = OBJ_COL_VAL(i, 5);
+ for (j = i+1; j < res->objects.objects_len; j++) {
+ if ((c0a = OBJ_COL_VAL(j, 0)) && *c0a &&
+ ((c1a = OBJ_COL_VAL(j, 1)) ||
+ ((c2a = OBJ_COL_VAL_NO_NULL(j, 2)) &&
+ (c3a = OBJ_COL_VAL(j, 3)) &&
+ (c4a = OBJ_COL_VAL(j, 4)))) &&
+ strcmp(c0, c0a) == 0) {
+ print2buf(&line, " ");
+ if (c1a)
+ print2buf(&line, c1a);
+ else {
+ print2buf(&line, "(");
+ print2buf(&line, c2a);
+ print2buf(&line, ",");
+ print2buf(&line, c3a);
+ print2buf(&line, ",");
+ print2buf(&line, c4a);
+ print2buf(&line, ")");
+ }
+ if (!c5)
+ c5 = OBJ_COL_VAL(j, 5);
+ *c0a = 0;
+ }
+ }
+ if (c5 && !blankline(c5)) {
+ print2buf(&line, tabtocol(line.str, 6));
+ print2buf(&line, "#");
+ print2buf(&line, c5);
+ }
+ *c0 = 0;
+ printf("%s\n", line.str);
+ print2buf_destroy(&line);
+ }
+ }
+}
+
+void
+printfkeystr_netgroup(fstr, entry)
+ char *fstr;
+ nis_object *entry;
+{
+ char keystr[BUFSIZ];
+
+ if (NKEYVAL(1))
+ sprintf(keystr, "%s %s", NKEYVAL(0), NKEYVAL(1));
+ else
+ sprintf(keystr, "%s (%s,%s,%s)", NKEYVAL(0), NKEYVAL(2),
+ NKEYVAL(3), NKEYVAL(4));
+
+ printf(fstr, keystr);
+}
+
+/*
+ * /etc/timezone
+ * nis+ table: (timezone_tbl) name, tzone, comment
+ *
+ */
+
+int
+genent_timezone(line, cback, udata)
+ char *line;
+ int (*cback)();
+ void *udata;
+{
+ char buf[BUFSIZ+1];
+ char *t;
+ nis_object eobj;
+ entry_col ecol[3];
+ char *cname;
+
+ /*
+ * don't clobber our argument
+ */
+ if (strlen(line) >= sizeof (buf)) {
+ strcpy(parse_err_msg, "line too long");
+ return (GENENT_PARSEERR);
+ }
+ strcpy(buf, line);
+
+ /*
+ * clear column data
+ */
+ memset((char *)ecol, 0, sizeof (ecol));
+
+ /*
+ * comment (col 2)
+ */
+ t = strchr(buf, '#');
+ if (t) {
+ *t++ = 0;
+ ecol[2].ec_value.ec_value_val = t;
+ ecol[2].ec_value.ec_value_len = strlen(t)+1;
+ } else {
+ ecol[2].ec_value.ec_value_val = 0;
+ ecol[2].ec_value.ec_value_len = 0;
+ }
+
+ /*
+ * tzone (col 1)
+ */
+ if ((t = strtok(buf, " \t")) == 0) {
+ strcpy(parse_err_msg, "no name");
+ return (GENENT_PARSEERR);
+ }
+ ecol[1].ec_value.ec_value_val = t;
+ ecol[1].ec_value.ec_value_len = strlen(t)+1;
+
+ /*
+ * name(col 0)
+ */
+ if ((t = strtok(NULL, " \t")) == 0) {
+ strcpy(parse_err_msg, "no name");
+ return (GENENT_PARSEERR);
+ }
+ ecol[0].ec_value.ec_value_val = t;
+ ecol[0].ec_value.ec_value_len = strlen(t)+1;
+
+ /*
+ * build entry
+ */
+ eobj = nis_default_obj;
+ eobj.zo_data.zo_type = NIS_ENTRY_OBJ;
+ eobj.EN_data.en_type = ta_type;
+ eobj.EN_data.en_cols.en_cols_val = ecol;
+ eobj.EN_data.en_cols.en_cols_len = 3;
+
+ if (!cback)
+ cback = addentry;
+
+ if ((*cback)(ta_name, &eobj, udata, 0))
+ return (GENENT_CBERR);
+
+ return (GENENT_OK);
+}
+
+void
+dump_timezone(res)
+ nis_result *res;
+{
+ int i, j;
+ char buf[BUFSIZ+1], *c0, *c1, *c2;
+
+ for (i = 0; i < res->objects.objects_len; i++) {
+ if ((c0 = OBJ_COL_VAL(i, 0)) && (c1 = OBJ_COL_VAL(i, 1))) {
+ strcpy(buf, c1);
+ strcat(buf, tabtocol(buf, 2));
+ strcat(buf, c0);
+ if ((c2 = OBJ_COL_VAL(i, 2)) &&
+ !blankline(c2)) {
+ strcat(buf, tabtocol(buf, 6));
+ strcat(buf, "#");
+ strcat(buf, c2);
+ }
+ printf("%s\n", buf);
+ }
+ }
+}
+
+/*
+ * genent_attr:
+ * Generic function for generating entries for all of the *_attr databases.
+ */
+int
+genent_attr(line, cback, udata, database, ncol, cnames, nkeycol, keycol)
+ char *line;
+ int (*cback)();
+ void *udata;
+ char *database; /* name of the database */
+ int ncol; /* number of columns in the database */
+ char **cnames; /* array of column names */
+ int nkeycol; /* number of searchable columns that form the id */
+ int keycol[]; /* array containing indices of searchable columns */
+{
+ int i, j;
+ char buf[BUFSIZ+1];
+ char aname[NIS_MAXNAMELEN];
+ char *s;
+ char *sep = KV_TOKEN_DELIMIT;
+ char *lasts;
+ entry_col *ecol;
+ nis_object eobj;
+
+ /*
+ * don't clobber our argument
+ */
+ if (strlen(line) >= sizeof (buf)) {
+ strcpy(parse_err_msg, "line too long");
+ return (GENENT_PARSEERR);
+ }
+ strcpy(buf, line);
+
+ /*
+ * setup and clear column data
+ */
+ if ((ecol = (entry_col *)malloc(ncol * sizeof (entry_col))) == NULL)
+ return (GENENT_ERR);
+ memset((char *)ecol, 0, ncol * sizeof (ecol));
+
+ /* Split up columns */
+ i = 0;
+ while (i < ncol) {
+ s = _strtok_escape(i ? NULL : buf, sep, &lasts);
+ if (s == NULL) {
+ ecol[i].ec_value.ec_value_val = NULL;
+ ecol[i].ec_value.ec_value_len = 0;
+ } else {
+ ecol[i].ec_value.ec_value_val = s;
+ ecol[i].ec_value.ec_value_len = strlen(s)+1;
+ }
+ ecol[i].ec_flags = EN_MODIFIED;
+ i++;
+ }
+
+ /* build entry */
+ eobj = nis_default_obj;
+ eobj.zo_data.zo_type = ENTRY_OBJ;
+ eobj.EN_data.en_type = ta_type;
+ eobj.EN_data.en_cols.en_cols_val = ecol;
+ eobj.EN_data.en_cols.en_cols_len = ncol;
+
+ if (!cback)
+ cback = addentry;
+
+ /* specify entry by unique identifier */
+ memset(aname, 0, NIS_MAXNAMELEN * sizeof (char));
+ strcat(aname, "[");
+ for (i = 0; i < nkeycol; i++) {
+ j = keycol[i];
+ if (i)
+ strcat(aname, ",");
+ strcat(aname, cnames[j]);
+ strcat(aname, "=\"");
+ strcat(aname, ecol[j].ec_value.ec_value_val);
+ strcat(aname, "\"");
+ }
+ strcat(aname, "],");
+ strcat(aname, ta_name);
+
+ if ((*cback)(aname, &eobj, udata, 0))
+ return (GENENT_CBERR);
+
+ return (GENENT_OK);
+}
+
+void
+dump_attr(res, ncol)
+ nis_result *res;
+ int ncol;
+{
+ int i, j;
+ char *c;
+
+ for (i = 0; i < res->objects.objects_len; i++) {
+ if (c = OBJ_COL_VAL(i, 0)) {
+ printf("%s", c);
+ for (j = 1; j < ncol; j++) {
+ c = OBJ_COL_VAL(i, j);
+ printf(":%s", c ? c : null_string);
+ }
+ printf("\n");
+ }
+ }
+}
+
+int
+genent_auth_attr(line, cback, udata)
+ char *line;
+ int (*cback)();
+ void *udata;
+{
+ int keycol[AUTHATTR_DB_NKEYCOL] = {AUTHATTR_KEYCOL0};
+ char *cnames[AUTHATTR_DB_NCOL] = {
+ AUTHATTR_COL0_KW,
+ AUTHATTR_COL1_KW,
+ AUTHATTR_COL2_KW,
+ AUTHATTR_COL3_KW,
+ AUTHATTR_COL4_KW,
+ AUTHATTR_COL5_KW
+ };
+
+ return (genent_attr(line, cback, udata, NSS_DBNAM_AUTHATTR,
+ AUTHATTR_DB_NCOL, cnames, AUTHATTR_DB_NKEYCOL, keycol));
+}
+
+void
+dump_auth_attr(res)
+ nis_result *res;
+{
+ dump_attr(res, AUTHATTR_DB_NCOL);
+}
+
+int
+genent_exec_attr(line, cback, udata)
+ char *line;
+ int (*cback)();
+ void *udata;
+{
+ char *cnames[EXECATTR_DB_NCOL] = {
+ EXECATTR_COL0_KW,
+ EXECATTR_COL1_KW,
+ EXECATTR_COL2_KW,
+ EXECATTR_COL3_KW,
+ EXECATTR_COL4_KW,
+ EXECATTR_COL5_KW,
+ EXECATTR_COL6_KW
+ };
+
+ return (genent_attr(line, cback, udata, NSS_DBNAM_EXECATTR,
+ EXECATTR_DB_NCOL, cnames, EXECATTR_DB_NKEYCOL, exec_attr_keycol));
+}
+
+void
+dump_exec_attr(res)
+ nis_result *res;
+{
+ dump_attr(res, EXECATTR_DB_NCOL);
+}
+
+int
+genent_prof_attr(line, cback, udata)
+ char *line;
+ int (*cback)();
+ void *udata;
+{
+ int keycol[PROFATTR_DB_NKEYCOL] = {PROFATTR_KEYCOL0};
+ char *cnames[PROFATTR_DB_NCOL] = {
+ PROFATTR_COL0_KW,
+ PROFATTR_COL1_KW,
+ PROFATTR_COL2_KW,
+ PROFATTR_COL3_KW,
+ PROFATTR_COL4_KW
+ };
+
+ return (genent_attr(line, cback, udata, NSS_DBNAM_PROFATTR,
+ PROFATTR_DB_NCOL, cnames, PROFATTR_DB_NKEYCOL, keycol));
+}
+
+void
+dump_prof_attr(res)
+ nis_result *res;
+{
+ dump_attr(res, PROFATTR_DB_NCOL);
+}
+
+int
+genent_user_attr(line, cback, udata)
+ char *line;
+ int (*cback)();
+ void *udata;
+{
+ char *cnames[USERATTR_DB_NCOL] = {
+ USERATTR_COL0_KW,
+ USERATTR_COL1_KW,
+ USERATTR_COL2_KW,
+ USERATTR_COL3_KW,
+ USERATTR_COL4_KW,
+ };
+
+ return (genent_attr(line, cback, udata, NSS_DBNAM_USERATTR,
+ USERATTR_DB_NCOL, cnames, USERATTR_DB_NKEYCOL, user_attr_keycol));
+}
+
+void
+dump_user_attr(res)
+ nis_result *res;
+{
+ dump_attr(res, USERATTR_DB_NCOL);
+}
+
+int
+genent_audit_user(line, cback, udata)
+ char *line;
+ int (*cback)();
+ void *udata;
+{
+ int keycol[AUDITUSER_DB_NKEYCOL] = {AUDITUSER_KEYCOL0};
+ char *cnames[AUDITUSER_DB_NCOL] = {
+ AUDITUSER_COL0_KW,
+ AUDITUSER_COL1_KW,
+ AUDITUSER_COL2_KW
+ };
+
+ return (genent_attr(line, cback, udata, NSS_DBNAM_AUDITUSER,
+ AUDITUSER_DB_NCOL, cnames, AUDITUSER_DB_NKEYCOL, keycol));
+}
+
+void
+dump_audit_user(res)
+ nis_result *res;
+{
+ dump_attr(res, AUDITUSER_DB_NCOL);
+}
+
+struct ttypelist_t ttypelist[] = {
+ { "key-value", 0, 0, 0,
+ 0, 0, 0, " \t",
+ dbmniskey, nisdbmkey, filedbmkey, genent_keyvalue,
+ dump_keyvalue, 0,
+ filedbmline_comment, filetodbm_keyvalue, fetchdbm_addkey,
+ printfkeystr_0 },
+ { "aliases", "mail.aliases", "mail_aliases.org_dir", "mail_aliases",
+ 0, 0, 0, " \t",
+ dbmniskey, nisdbmkey_aliases, filedbmkey_aliases, genent_aliases,
+ dump_aliases, 0,
+ filedbmline_aliases, filetodbm_aliases, fetchdbm_addkey,
+ printfkeystr_0 },
+ { "bootparams", "bootparams", "bootparams.org_dir", "bootparams_tbl",
+ 0, 0, 0, " \t",
+ dbmniskey, nisdbmkey, filedbmkey, genent_keyvalue,
+ dump_keyvalue, 0,
+ filedbmline_comment, filetodbm_keyvalue, fetchdbm_addkey,
+ printfkeystr_0 },
+ { "ethers", "ethers.byaddr", "ethers.org_dir", "ethers_tbl",
+ 0, 0, 0, " \t",
+ dbmniskey, nisdbmkey, filedbmkey, genent_ethers,
+ dump_ethers, 0,
+ filedbmline_comment, filetodbm, fetchdbm,
+ printfkeystr_0 },
+ { "group", "group.byname", "group.org_dir", "group_tbl",
+ 0, 0, 0, ":",
+ dbmniskey, nisdbmkey, filedbmkey, genent_group,
+ dump_group, 0,
+ filedbmline_plus, filetodbm, fetchdbm,
+ printfkeystr_0 },
+ { "hosts", "hosts.byaddr", "hosts.org_dir", "hosts_tbl",
+ 0, 2, 0, " \t",
+ dbmniskey, nisdbmkey, filedbmkey, genent_hosts,
+ dump_hosts, dump_match_hosts,
+ filedbmline_comment, filetodbm, fetchdbm,
+ printfkeystr_cname },
+ { "ipnodes", "ipnodes.byaddr", "ipnodes.org_dir", "ipnodes_tbl",
+ 0, 2, 0, " \t",
+ dbmniskey, nisdbmkey, filedbmkey, genent_hosts,
+ dump_hosts, dump_match_hosts,
+ filedbmline_comment, filetodbm, fetchdbm,
+ printfkeystr_cname },
+ { "netgroup", "netgroup", "netgroup.org_dir", "netgroup_tbl",
+ 0, 0, 0, " \t",
+ dbmniskey, nisdbmkey, filedbmkey, genent_netgroup,
+ dump_netgroup, dump_match_netgroup,
+ filedbmline_comment, filetodbm_keyvalue, fetchdbm_addkey,
+ printfkeystr_netgroup },
+ { "netid", "netid.byname", "cred.org_dir", "cred_tbl",
+ "auth_type=LOCAL", 0, 0, " \t",
+ dbmniskey_netid, nisdbmkey_netid, filedbmkey_netid, genent_netid,
+ dump_netid, 0,
+ filedbmline_comment, filetodbm, fetchdbm_addkey,
+ printfkeystr_01 },
+ { "netmasks", "netmasks.byaddr", "netmasks.org_dir", "netmasks_tbl",
+ 0, 0, 0, " \t",
+ dbmniskey, nisdbmkey, filedbmkey, genent_netmasks,
+ dump_netmasks, 0,
+ filedbmline_comment, filetodbm_keyvalue, fetchdbm_addkey,
+ printfkeystr_0 },
+ { "networks", "networks.byname", "networks.org_dir", "networks_tbl",
+ 0, 0, 0, " \t",
+ dbmniskey, nisdbmkey, filedbmkey, genent_networks,
+ dump_networks, dump_match_cname,
+ filedbmline_comment, filetodbm, fetchdbm,
+ printfkeystr_cname },
+ { "passwd", "passwd.byname", "passwd.org_dir", "passwd_tbl",
+ 0, 0, 0, ":",
+ dbmniskey, nisdbmkey, filedbmkey, genent_passwd,
+ dump_passwd, 0,
+ filedbmline_plus, filetodbm, fetchdbm,
+ printfkeystr_0 },
+ { "protocols", "protocols.byname", "protocols.org_dir", "protocols_tbl",
+ 0, 0, 0, " \t",
+ dbmniskey, nisdbmkey, filedbmkey, genent_protocols,
+ dump_protocols, dump_match_cname,
+ filedbmline_comment, filetodbm, fetchdbm,
+ printfkeystr_cname },
+ {"publickey", "publickey.byname", "cred.org_dir", "cred_tbl",
+ "auth_type=DES", 2, 0, " \t",
+ dbmniskey_publickey, nisdbmkey_publickey, filedbmkey_publickey,
+ genent_publickey, dump_publickey, 0,
+ filedbmline_comment, filetodbm_keyvalue, fetchdbm_addkey,
+ printfkeystr_01 },
+ { "rpc", "rpc.bynumber", "rpc.org_dir", "rpc_tbl",
+ 0, 2, 1, " \t",
+ dbmniskey, nisdbmkey, filedbmkey, genent_rpc,
+ dump_rpc, dump_match_cname,
+ filedbmline_comment, filetodbm, fetchdbm,
+ printfkeystr_cname },
+ { "services", "services.byname", "services.org_dir", "services_tbl",
+ 0, -1, 1, " \t",
+ dbmniskey_services, nisdbmkey_services, filedbmkey, genent_services,
+ dump_services, dump_match_services,
+ filedbmline_comment, filetodbm, fetchdbm,
+ printfkeystr_services },
+#ifndef FOURDOTX
+ { "shadow", 0, "passwd.org_dir", "passwd_tbl",
+ 0, 0, 0, ":",
+ dbmniskey, nisdbmkey, filedbmkey, genent_shadow,
+ dump_shadow, 0,
+ filedbmline_plus, filetodbm, fetchdbm,
+ printfkeystr_0 },
+#endif
+ { "timezone", "timezone.byname", "timezone.org_dir", "timezone_tbl",
+ 0, 0, 1, " \t",
+ dbmniskey, nisdbmkey, filedbmkey, genent_timezone,
+ dump_timezone, 0,
+ filedbmline_comment, filetodbm, fetchdbm,
+ printfkeystr_0 },
+ { "auth_attr", "auth_attr.byname", "auth_attr.org_dir", "auth_attr_tbl",
+ 0, 0, 0, ":",
+ dbmniskey, nisdbmkey, filedbmkey, genent_auth_attr,
+ dump_auth_attr, 0,
+ filedbmline_comment, filetodbm, fetchdbm,
+ printfkeystr_0 },
+ { "exec_attr", "exec_attr.byname", "exec_attr.org_dir", "exec_attr_tbl",
+ 0, 0, 0, ":",
+ dbmniskey_attr, nisdbmkey_attr, filedbmkey_attr, genent_exec_attr,
+ dump_exec_attr, 0,
+ filedbmline_comment, filetodbm_attr, fetchdbm,
+ printfkeystr_0 },
+ { "prof_attr", "prof_attr.byname", "prof_attr.org_dir", "prof_attr_tbl",
+ 0, 0, 0, ":",
+ dbmniskey, nisdbmkey, filedbmkey, genent_prof_attr,
+ dump_prof_attr, 0,
+ filedbmline_comment, filetodbm, fetchdbm,
+ printfkeystr_0 },
+ { "user_attr", "user_attr.byname", "user_attr.org_dir", "user_attr_tbl",
+ 0, 0, 0, ":",
+ dbmniskey_attr, nisdbmkey_attr, filedbmkey_attr, genent_user_attr,
+ dump_user_attr, 0,
+ filedbmline_comment, filetodbm_attr, fetchdbm,
+ printfkeystr_0 },
+ { "audit_user", "audit_user.byname", "audit_user.org_dir",
+ "audit_user_tbl",
+ 0, 0, 0, ":",
+ dbmniskey, nisdbmkey, filedbmkey, genent_audit_user,
+ dump_audit_user, 0,
+ filedbmline_comment, filetodbm, fetchdbm,
+ printfkeystr_0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+};
+
+/*
+ * This routine dumps a table entry. If the dump_match routine
+ * is defined, then we call it to get all of the entries related
+ * to this one. For example, in the hosts table, we want all
+ * of the entries for a given address. If the dump_match routine
+ * returns 0, then the entry will be handled in conjuction with
+ * another entry and should be ignored now. If the dump_match
+ * routine is not defined, then we make a nis_result out of
+ * the entry and then dump it.
+ */
+int
+dumpcback(table, entry, udata)
+ nis_name table;
+ nis_object *entry;
+ void *udata;
+{
+ datum key, val;
+ char *line;
+ nis_result *res;
+ nis_result matchres;
+
+ if (tt->dump_match) {
+ res = tt->dump_match(table, entry);
+ if (res) {
+ tt->dump(res);
+ nis_freeresult(res);
+ }
+ } else {
+ matchres.status = NIS_SUCCESS;
+ matchres.objects.objects_len = 1;
+ matchres.objects.objects_val = entry;
+ tt->dump(&matchres);
+ }
+ return (0);
+}
+
+
+/*
+ * In "quick" mode, we ask the server for all of the entries for
+ * the table and then call the dump routine to merge entries and
+ * print them. This only works for small tables or on machines
+ * with a LOT of swap space. In "normal" mode, we go through
+ * the entries one by one, calling the dumpcback routine for
+ * each one. The dumpcback routine will find all of the entries
+ * related to the current one, merge them, and then print the
+ * result.
+ */
+void
+dumptable()
+{
+ int i, j;
+ char buf[BUFSIZ+1], srch[NIS_MAXNAMELEN], *c0, *c1, *c2, *c3, *c4;
+ nis_result *eres;
+
+ if ((strcmp("publickey", tt->ttype) == 0) && mechs)
+ sprintf(srch, "%s", ta_name);
+ else {
+ if (tt->clrsrch)
+ sprintf(srch, "[%s],%s", tt->clrsrch, ta_name);
+ else
+ sprintf(srch, "%s", ta_name);
+ }
+
+ if (flags & F_QUICK) {
+ eres = nis_list(srch, allres|master, 0, 0);
+ tt->dump(eres);
+ } else {
+ eres = nis_list(srch, allres|master, dumpcback, 0);
+
+ if (eres->status != NIS_CBRESULTS &&
+ eres->status != NIS_NOTFOUND) {
+ nis_perror(eres->status, "can't list table");
+ exit(1);
+ }
+ nis_freeresult(eres);
+ }
+}
+
+
+int lineno = 0;
+
+int
+putfile()
+{
+ struct line_buf line;
+ char *p;
+ struct file_loc loc;
+
+ line_buf_init(&line);
+ while (tt->filedbmline(&line, etcf, &lineno, &loc)) {
+
+ switch ((*(tt->genent))(line.str, 0, 0)) {
+ case GENENT_OK:
+ break;
+ case GENENT_PARSEERR:
+ fprintf(stderr, "parse error: %s (line %d)\n",
+ parse_err_msg, lineno);
+ exit_val = 2;
+ break;
+ default:
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+
+int
+isypsym(key)
+ datum *key;
+{
+ if (key->dptr && (key->dsize > 3) && !memcmp(key->dptr, "YP_", 3))
+ return (1);
+
+ return (0);
+}
+
+int
+putdbm()
+{
+ datum key, val;
+ char *line;
+
+ for (key = dbm_firstkey(dbmf); key.dptr; key = dbm_nextkey(dbmf)) {
+
+ if (isypsym(&key))
+ continue;
+
+ if (line = tt->fetchdbm(key)) {
+ switch ((*(tt->genent))(line, 0, 0)) {
+ case GENENT_OK:
+ break;
+ case GENENT_PARSEERR:
+ fprintf(stderr, "parse error: %s (key %.*s)\n",
+ parse_err_msg, key.dsize, key.dptr);
+ exit_val = 2;
+ break;
+ default:
+ return (1);
+ }
+ } else {
+ fprintf(stderr, "bad dbm file");
+ exit(1);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * __nis_form_keys: puts "," as key component separator, as required by
+ * nis_list etc.
+ * E.g.: "name=File System Mangement,policy=tsol,id=/usr/sbin/mount" is the
+ * corrent search key for exec_attr.
+ *
+ */
+void
+__nis_form_keys(char *keyname)
+{
+ char *p, *s;
+
+ s = keyname;
+ while (s) {
+ p = strpbrk(s, tt->filesep);
+ if (p == NULL)
+ break;
+ *p = ',';
+ s = ++p;
+ }
+}
+
+int
+addfile(merge)
+ int merge;
+{
+ struct line_buf line;
+ char **p;
+ datum key;
+ datum content;
+ datum tmp;
+ char *niskey, keyname[NIS_MAXNAMELEN], indexname[NIS_MAXNAMELEN];
+ nis_result *eres;
+ struct file_loc loc;
+
+ line_buf_init(&line);
+ while (tt->filedbmline(&line, etcf, &lineno, &loc)) {
+
+ key = tt->filedbmkey(line.str);
+ if (key.dptr == 0) {
+ fprintf(stderr,
+ "parse error: zero key length (line %d)\n", lineno);
+ continue;
+ }
+
+ /* if merging, add key/file_offset to dbm file */
+ if (merge) {
+ content.dsize = sizeof (loc);
+ content.dptr = (char *)&loc;
+ tmp = dbm_fetch(dbmf, key);
+ if (tmp.dptr == NULL) {
+ if (dbm_store(dbmf, key, content, 1) != 0) {
+ fprintf(stderr,
+ "problem storing %.*s %.*s\n",
+ key.dsize, key.dptr,
+ content.dsize, content.dptr);
+ dbm_close(dbmf);
+ return (1);
+ }
+ }
+ }
+ (void) __nis_quote_key(tt->dbmniskey(key), keyname,
+ sizeof (keyname));
+
+ /*
+ * Make sure "," is the separator for multi-component key. E.g.,
+ * "name=File System Mangement,policy=tsol,id=/usr/sbin/mount"
+ * is the correct search key for exec_attr.
+ *
+ * __nis_quote_key puts quotes around ",". Is case of multi-
+ * component key, "," is a separator and should not be in
+ * quotes (requirement of nis_list etc. Hence __nis_form_keys
+ * is called after __nis_quote_key.
+ */
+ (void) __nis_form_keys(keyname);
+
+ sprintf(indexname, "[%s],%s", keyname, ta_name);
+ eres = nis_list(indexname, fpath|MASTER_ONLY, 0, 0);
+ switch (eres->status) {
+ case NIS_BADATTRIBUTE:
+ fprintf(stderr, "parse error: bad key (line %d)\n",
+ lineno);
+ break;
+ case NIS_SUCCESS:
+ switch ((*(tt->genent))(line.str, 0, eres)) {
+ case GENENT_OK:
+ break;
+ case GENENT_PARSEERR:
+ fprintf(stderr, "parse error: %s (line %d)\n",
+ parse_err_msg, lineno);
+ exit_val = 2;
+ break;
+ default:
+ nis_freeresult(eres);
+ return (1);
+ }
+ break;
+ case NIS_NOTFOUND:
+ switch ((*(tt->genent))(line.str, 0, 0)) {
+ case GENENT_OK:
+ break;
+ case GENENT_PARSEERR:
+ fprintf(stderr, "parse error: %s (line %d)\n",
+ parse_err_msg, lineno);
+ exit_val = 2;
+ break;
+ default:
+ nis_freeresult(eres);
+ return (1);
+ }
+ break;
+ default:
+ nis_perror(eres->status, "can't list table");
+ exit(1);
+ }
+ nis_freeresult(eres);
+ }
+
+ return (0);
+}
+
+
+int
+adddbm()
+{
+ datum key, val;
+ char *line;
+ char *niskey, keyname[NIS_MAXNAMELEN], indexname[NIS_MAXNAMELEN];
+ nis_result *eres;
+
+ for (key = dbm_firstkey(dbmf); key.dptr; key = dbm_nextkey(dbmf)) {
+
+ if (isypsym(&key))
+ continue;
+
+ if (line = tt->fetchdbm(key)) {
+ (void) __nis_quote_key(tt->dbmniskey(key), keyname,
+ sizeof (keyname));
+ sprintf(indexname, "[%s],%s", keyname,
+ ta_name);
+ eres = nis_list(indexname, fpath|MASTER_ONLY, 0, 0);
+ switch (eres->status) {
+ case NIS_BADATTRIBUTE:
+ fprintf(stderr, "bad key (%.*s)\n", key.dsize,
+ key.dptr);
+ break;
+ case NIS_SUCCESS:
+ switch ((*(tt->genent))(line, 0, eres)) {
+ case GENENT_OK:
+ break;
+ case GENENT_PARSEERR:
+ fprintf(stderr,
+ "parse error: %s (key %.*s)\n",
+ parse_err_msg, key.dsize, key.dptr);
+ exit_val = 2;
+ break;
+ default:
+ nis_freeresult(eres);
+ return (1);
+ }
+ break;
+ case NIS_NOTFOUND:
+ switch ((*(tt->genent))(line, 0, 0)) {
+ case GENENT_OK:
+ break;
+ case GENENT_PARSEERR:
+ fprintf(stderr,
+ "parse error: %s (key %.*s)\n",
+ parse_err_msg, key.dsize, key.dptr);
+ exit_val = 2;
+ break;
+ default:
+ nis_freeresult(eres);
+ return (1);
+ }
+ break;
+ default:
+ nis_perror(eres->status, "can't list table");
+ exit(1);
+ }
+ nis_freeresult(eres);
+
+ } else {
+ fprintf(stderr, "bad dbm file");
+ exit(1);
+ }
+ }
+
+ return (0);
+}
+
+
+int
+deletecback(table, entry, udata)
+ nis_name table;
+ nis_object *entry;
+ void *udata;
+{
+ datum key;
+ char *line;
+ nis_result matchres;
+ static nis_name last_table = NULL;
+ static nis_object *last_entry = NULL;
+ nis_name tmp_table = table;
+ nis_object *tmp_entry;
+ int ret;
+
+ /*
+ * nis_list requires that the previous entry exists
+ * to return the next batch of entries. If we remove
+ * the last entry in a batch, we will not be able to
+ * get any more entries.
+ *
+ * To work around this, we will remember the last entry
+ * and only remove it after obtaining the next entry.
+ * deletecback must be called after nis_list completes
+ * to remove the last entry.
+ */
+
+ if (entry != NULL) {
+ key = tt->nisdbmkey(entry);
+
+ if (line = ((char *(*)())udata)(key)) {
+ matchres.objects.objects_len = 1;
+ matchres.objects.objects_val = entry;
+
+ switch ((*(tt->genent))(line, matchentry, &matchres)) {
+ case GENENT_CBERR:
+ return (0);
+ case GENENT_OK:
+ break;
+ case GENENT_PARSEERR:
+ fprintf(stderr,
+ "parse error (key %.*s)\n",
+ key.dsize, key.dptr);
+ exit_val = 2;
+ return (1);
+ default:
+ return (1);
+ }
+ }
+
+ tmp_entry = nis_clone_object(entry, 0);
+ if (tmp_entry == NULL) {
+ fprintf(stderr,
+ "Could not clone %s\n", NKEYVAL(0));
+ exit_val = 2;
+ return (0);
+ }
+ tmp_table = strdup(table);
+ if (tmp_table == NULL) {
+ fprintf(stderr, "deletecback: out of memory\n");
+ exit_val = 2;
+ return (0);
+ }
+ } else {
+ tmp_entry = NULL;
+ }
+
+ if (last_entry == NULL || last_table == NULL) {
+ last_table = tmp_table;
+ last_entry = tmp_entry;
+ return (0);
+ }
+
+ ret = removeentry(last_table, last_entry);
+
+ nis_destroy_object(last_entry);
+ free(last_table);
+
+ last_table = tmp_table;
+ last_entry = tmp_entry;
+
+ return (ret);
+}
+
+
+/*
+ * The deletecback routine is too slow for large files, so we create
+ * a dbm file and then use mergedbm instead. An exit routine will
+ * clean up the temporary files when we exit.
+ */
+int
+mergefile()
+{
+ char line[BUFSIZ+1], *p;
+ char lname[NIS_MAXNAMELEN];
+ datum key;
+ datum content;
+ datum tmp;
+ char *niskey, keyname[NIS_MAXNAMELEN];
+ nis_result *eres;
+ char *dbmfile;
+ FILE *fp;
+
+ /* create dbm file from text file */
+
+ dbmfile = tmpnam((char *)0);
+
+ strcpy(tmpdirbuf, dbmfile);
+ strcat(tmpdirbuf, ".dir");
+ strcpy(tmppagbuf, dbmfile);
+ strcat(tmppagbuf, ".pag");
+
+ fp = fopen(tmpdirbuf, "w");
+ if (fp == 0) {
+ fprintf(stderr, "can't open %s\n", tmpdirbuf);
+ return (1);
+ }
+ fclose(fp);
+ created_dir = 1;
+
+ fp = fopen(tmppagbuf, "w");
+ if (fp == 0) {
+ fprintf(stderr, "can't open %s\n", tmppagbuf);
+ return (1);
+ }
+ fclose(fp);
+ created_pag = 1;
+
+ if ((dbmf = dbm_open(dbmfile, O_RDWR, 0)) == 0) {
+ fprintf(stderr, "can't open dbmfile %s\n", dbmfile);
+ exit(1);
+ }
+
+ if (addfile(1))
+ return (1);
+
+ if (mechs && (strcmp(tt->ttype, "publickey") == 0))
+ sprintf(lname, "[],%s", ta_name);
+ else
+ sprintf(lname, "[%s],%s",
+ (tt->clrsrch)?tt->clrsrch:"", ta_name);
+ eres = nis_list(lname, MASTER_ONLY, deletecback,
+ (void*)(fetchfile));
+ (void) deletecback(NULL, NULL, NULL);
+ if (eres->status != NIS_CBRESULTS &&
+ eres->status != NIS_NOTFOUND) {
+ nis_perror(eres->status, "can't list table");
+ exit(1);
+ }
+
+ nis_freeresult(eres);
+
+ /* dbm files will be removed in cleanup, called at exit */
+ dbm_close(dbmf);
+
+ return (0);
+}
+
+int
+mergedbm()
+{
+ nis_result *eres;
+ char lname[NIS_MAXNAMELEN];
+
+ if ((strcmp(tt->ttype, "publickey") == 0) && mechs) {
+ fprintf(stderr,
+"Merging of extended Diffie-Hellman keys from NIS(YP) are not supported!\n");
+ exit(1);
+ }
+
+ if (adddbm())
+ return (1);
+
+ sprintf(lname, "[%s],%s",
+ (tt->clrsrch)?tt->clrsrch:"", ta_name);
+ eres = nis_list(lname, MASTER_ONLY, deletecback, (void*)(tt->fetchdbm));
+ (void) deletecback(NULL, NULL, NULL);
+ if (eres->status != NIS_CBRESULTS &&
+ eres->status != NIS_NOTFOUND) {
+ nis_perror(eres->status, "can't list table");
+ exit(1);
+ }
+
+ nis_freeresult(eres);
+ return (0);
+}
+
+void
+cleanup()
+{
+ if (created_dir)
+ unlink(tmpdirbuf);
+ if (created_pag)
+ unlink(tmppagbuf);
+}
+
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *defstr = 0, *ttype, *tname = 0, *ypdomain = 0, *ypmap = 0,
+ *etcfile = 0;
+ int c;
+ int op = OP_ADD;
+ nis_result *tres, *rres;
+ char rname[NIS_MAXNAMELEN];
+ char dbmfile[MAXPATHLEN];
+
+ /*
+ * We register an exit routine to clean up any temporary
+ * files.
+ */
+ atexit(cleanup);
+
+ mechs = __nis_get_mechanisms(FALSE);
+
+ while ((c = getopt(argc, argv, "D:PAMvapqrmdt:f:y:Y:o")) != -1) {
+ switch (c) {
+ case 'D':
+ defstr = optarg;
+ break;
+ case 'P':
+ fpath = FOLLOW_PATH;
+ break;
+ case 'A':
+ allres = ALL_RESULTS;
+ break;
+ case 'M':
+ master = MASTER_ONLY;
+ break;
+ case 'p':
+ flags |= F_PASSWD;
+ break;
+ case 'v':
+ flags |= F_VERBOSE;
+ break;
+ case 'a':
+ if (op)
+ usage();
+ op = OP_ADD;
+ break;
+ case 'r':
+ if (op)
+ usage();
+ op = OP_REPLACE;
+ break;
+ case 'm':
+ if (op)
+ usage();
+ op = OP_MERGE;
+ break;
+ case 'd':
+ if (op)
+ usage();
+ op = OP_DUMP;
+ break;
+ case 'q':
+ flags |= F_QUICK;
+ break;
+ case 't':
+ tname = optarg;
+ break;
+ case 'f':
+ if (ypmap || ypdomain)
+ usage();
+ etcfile = optarg;
+ break;
+ case 'y':
+ if (etcfile)
+ usage();
+ ypdomain = optarg;
+ break;
+ case 'Y':
+ if (etcfile)
+ usage();
+ ypmap = optarg;
+ break;
+ case 'o':
+ oldpubkeymode++;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (ypmap && !ypdomain)
+ usage();
+
+ if ((op == OP_MERGE) && !ypdomain && !etcfile)
+ usage();
+
+ if (argc - optind < 1)
+ usage();
+
+ ttype = argv[optind++];
+
+ for (tt = ttypelist; tt->ttype; tt++)
+ if (strcmp(tt->ttype, ttype) == 0)
+ break;
+ if (tt->ttype == 0) {
+ fprintf(stderr, "type %s not supported; supported types are:\n",
+ ttype);
+ for (tt = ttypelist; tt->ttype; tt++)
+ fprintf(stderr, "\t%s\n", tt->ttype);
+ exit(1);
+ }
+
+ if (ypdomain && !ypmap && !(tt->ypmap)) {
+ fprintf(stderr, "yp map not known; specify with -Y\n");
+ exit(1);
+ }
+
+ if (!tname && !(tt->nistbl)) {
+ fprintf(stderr, "nis table not known; specify with -t\n");
+ exit(1);
+ }
+
+ if (argc - optind > 0) {
+ nisdomain = argv[optind++];
+ if (nisdomain[strlen(nisdomain)-1] != '.') {
+ fprintf(stderr,
+ "nisdomain must be fully qualified.\n");
+ exit(1);
+ }
+ } else
+ nisdomain = nis_local_directory();
+
+ if (argc - optind > 0)
+ usage();
+
+ if (!tname)
+ tname = tt->nistbl;
+
+ if (tname[strlen(tname)-1] != '.')
+ sprintf(ta_name, "%s.%s", tname, nisdomain);
+ else
+ strcpy(ta_name, tname);
+
+ tres = nis_lookup(ta_name, MASTER_ONLY|FOLLOW_LINKS);
+ if (tres->status != NIS_SUCCESS) {
+ nis_perror(tres->status, ta_name);
+ exit(1);
+ }
+ ta_obj = tres->objects.objects_val;
+
+ if (ta_obj->zo_data.zo_type != NIS_TABLE_OBJ) {
+ fprintf(stderr, "%s is not a table!\n", ta_name);
+ exit(1);
+ }
+
+ /*
+ * Check type of table
+ */
+ if (tt->ta_type) {
+ if (strcmp(ta_obj->zo_data.objdata_u.ta_data.ta_type,
+ tt->ta_type)) {
+ fprintf(stderr, "%s is not of type %s!\n",
+ ta_name, tt->ta_type);
+ exit(1);
+ }
+ } else {
+ /*
+ * key-value requires two column table
+ */
+ if (ta_obj->zo_data.objdata_u.ta_data.ta_cols.ta_cols_len
+ != 2) {
+ fprintf(stderr, "%s is not a two column table!\n",
+ ta_name);
+ exit(1);
+ }
+ }
+ strcpy(ta_type, ta_obj->zo_data.objdata_u.ta_data.ta_type);
+
+ if (op == OP_DUMP) {
+ master = MASTER_ONLY;
+ dumptable();
+ exit(exit_val);
+ }
+
+ if (!nis_defaults_init(defstr))
+ exit(1);
+
+ if (nis_default_obj.zo_ttl > ta_obj->zo_ttl)
+ nis_default_obj.zo_ttl = ta_obj->zo_ttl;
+
+ if (ypdomain) {
+ sprintf(dbmfile, "/var/yp/%s/%s", ypdomain,
+ (ypmap)?ypmap:tt->ypmap);
+ if ((dbmf = dbm_open(dbmfile, O_RDONLY, 0)) == 0) {
+ fprintf(stderr, "can't open dbmfile %s\n", dbmfile);
+ exit(1);
+ }
+ } else if (etcfile) {
+ if ((etcf = fopen(etcfile, "r")) == 0) {
+ fprintf(stderr, "can't open file %s\n", etcfile);
+ exit(1);
+ }
+ } else {
+ etcfile = "stdin";
+ etcf = stdin;
+ }
+
+ if (flags & F_VERBOSE)
+ switch (op) {
+ case OP_MERGE:
+ printf("merging %s into table %s\n",
+ (ypdomain)?dbmfile:etcfile, ta_name);
+ break;
+ case OP_REPLACE:
+ case OP_ADD:
+ printf("adding %s to table %s\n",
+ (ypdomain)?dbmfile:etcfile, ta_name);
+ break;
+ }
+
+ switch (op) {
+ case OP_REPLACE:
+ /*
+ * Handle special case of publickey and RPCSEC_GSS
+ * being configured on this system.
+ */
+ if ((strcmp(tt->ttype, "publickey") == 0) && mechs) {
+ int mcount = 0;
+ int verbose = flags & F_VERBOSE;
+
+ while (mechs[mcount]) {
+ char authtype[MECH_MAXATNAME+1];
+
+ if (__nis_mechalias2authtype(
+ mechs[mcount]->alias,
+ authtype,
+ MECH_MAXATNAME)) {
+ if (verbose)
+ printf(
+ "clearing table (auth_type=%s) %s\n",
+ authtype, ta_name);
+
+ sprintf(rname, "[auth_type=%s],%s",
+ authtype, ta_name);
+ rres = nis_remove_entry(rname, 0,
+ REM_MULTIPLE);
+ switch (rres->status) {
+ case NIS_NOTFOUND:
+ case NIS_SUCCESS:
+ break;
+ default:
+ nis_perror(rres->status,
+ "can't clear table");
+ exit(1);
+ }
+ nis_freeresult(rres);
+ }
+ mcount++;
+ }
+ } else {
+ if (flags & F_VERBOSE) {
+ if (tt->clrsrch)
+ printf("clearing table (%s) %s\n",
+ tt->clrsrch, ta_name);
+ else
+ printf("clearing table %s\n", ta_name);
+ }
+
+ sprintf(rname, "[%s],%s",
+ (tt->clrsrch)?tt->clrsrch:"", ta_name);
+ rres = nis_remove_entry(rname, 0, REM_MULTIPLE);
+ switch (rres->status) {
+ case NIS_NOTFOUND:
+ case NIS_SUCCESS:
+ break;
+ default:
+ nis_perror(rres->status, "can't clear table");
+ exit(1);
+ }
+ nis_freeresult(rres);
+ }
+
+ if (ypdomain)
+ putdbm();
+ else
+ putfile();
+ break;
+
+ case OP_ADD:
+ if (ypdomain)
+ adddbm();
+ else
+ addfile(0);
+ break;
+
+ case OP_MERGE:
+ if (ypdomain)
+ mergedbm();
+ else
+ mergefile();
+ break;
+ }
+
+ if (flags & F_VERBOSE) {
+ printf("%d entries added/updated\n", nent_add);
+ if (op == OP_MERGE)
+ printf("%d entries removed\n", nent_del);
+ }
+
+ exit(exit_val);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisauthconf.c b/usr/src/cmd/rpcsvc/nis/utils/nisauthconf.c
new file mode 100644
index 0000000000..08f759eba0
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nisauthconf.c
@@ -0,0 +1,223 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * nisauthconf.c
+ *
+ * Configure NIS+ to use RPCSEC_GSS
+ *
+ * Copyright (c) 1997,1998 Sun Microsystems, Inc.
+ * All Rights Reserved.
+ *
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <libgen.h>
+#include <rpcsvc/nis_dhext.h>
+
+static struct mech_t {
+ char *mechname;
+ char *keylen;
+ char *algtype;
+ char *alias;
+ char *additional;
+} mechs[] = {
+ "diffie_hellman_1024_0", "1024", "0", "dh1024-0", "default integrity",
+ "diffie_hellman_640_0", "640", "0", "dh640-0", "default integrity",
+ "-", "-", "-", "des", "# AUTH_DES",
+ NULL, NULL, NULL, NULL, NULL
+};
+
+static void
+preamble(FILE *f)
+{
+ (void) fprintf(f, "# DO NOT EDIT FILE, AUTOMATICALLY GENERATED\n");
+ (void) fprintf(f, "# \n");
+ (void) fprintf(f,
+ "# The format of this file may change or it may be removed\n");
+ (void) fprintf(f, "# in future versions of Solaris.\n# \n");
+}
+
+static void
+printconf()
+{
+ int i = 0;
+ mechanism_t **mechlist;
+
+ if (mechlist = __nis_get_mechanisms(FALSE)) {
+ while (mechlist[i]) {
+ (void) printf("%s", mechlist[i]->alias);
+
+ if (mechlist[++i])
+ (void) printf(", ");
+ else
+ (void) printf("\n");
+ }
+ } else
+ (void) printf("des\n");
+ exit(0);
+}
+
+
+#define DESMECH1 "-"
+#define DESMECH2 "des"
+#define DESMECH3 "192"
+#define DESMECH4 0
+#define HEADING1 "GSS Mechanism Name"
+#define HEADING2 "Alias"
+#define HEADING3 "Bit Size"
+#define HEADING4 "Algorithm Type"
+#define HEADFMT "%s%*c%s%*c%s%*c%s\n"
+#define LINEFMT "%s%*c%s%*c%s%*c%d\n"
+#define GENSPC(a, b) b, (a - strlen(b)) + 2, ' '
+
+static void
+listmechs()
+{
+ int sc[3], i;
+ char tmpstr[1024];
+ mechanism_t **mechlist;
+
+ sc[0] = strlen(HEADING1);
+ sc[1] = strlen(HEADING2);
+ sc[2] = strlen(HEADING3);
+
+ if ((mechlist = __nis_get_mechanisms(FALSE)) == NULL) {
+ mechlist = (mechanism_t **) malloc(sizeof (mechanism_t *) * 2);
+
+ mechlist[0] = (mechanism_t *) malloc(sizeof (mechanism_t));
+ mechlist[1] = NULL;
+
+ mechlist[0]->mechname = NULL;
+ mechlist[0]->alias = "des";
+ mechlist[0]->keylen = 192;
+ mechlist[0]->algtype = 0;
+ }
+
+ for (i = 0; mechlist[i]; i++) {
+ int m1 = 0, m2 = 0;
+
+ if (mechlist[i]->mechname) m1 = strlen(mechlist[i]->mechname);
+ if (mechlist[i]->alias) m2 = strlen(mechlist[i]->alias);
+
+ sc[0] = sc[0] > m1 ? sc[0] : m1;
+ sc[1] = sc[1] > m2 ? sc[1] : m2;
+ (void) snprintf(tmpstr, 1024, "%d", mechlist[i]->keylen);
+ sc[2] = sc[2] > strlen(tmpstr) ? sc[2] : strlen(tmpstr);
+ }
+
+ (void) printf(HEADFMT, GENSPC(sc[0], HEADING1),
+ GENSPC(sc[1], HEADING2),
+ GENSPC(sc[2], HEADING3), HEADING4);
+ for (i = 0; mechlist[i]; i++) {
+ (void) snprintf(tmpstr, 1024, "%d", mechlist[i]->keylen);
+ (void) printf(LINEFMT, GENSPC(sc[0],
+ mechlist[i]->mechname ?
+ mechlist[i]->mechname : "-"),
+ GENSPC(sc[1], mechlist[i]->alias ?
+ mechlist[i]->alias : "-"),
+ GENSPC(sc[2], tmpstr),
+ mechlist[i]->algtype);
+ }
+ exit(0);
+}
+
+
+static void
+usage(char *cmd)
+{
+ (void) fprintf(stderr, "usage:\n\t%s [-v] [mechanism, ...]\n", cmd);
+ exit(1);
+}
+
+
+void
+main(int argc, char **argv)
+{
+ int c, i, dolistmechs = 0;
+ char *cmd = basename(argv[0]);
+ FILE *f;
+
+ while ((c = getopt(argc, argv, "v")) != -1) {
+ switch (c) {
+ case 'v':
+ dolistmechs++;
+ break;
+ default:
+ usage(cmd);
+ }
+ }
+
+ if (dolistmechs)
+ listmechs();
+
+ if (argc < 2)
+ printconf();
+
+ if (argc == 2 && (strcmp(argv[1], NIS_SEC_CF_DES_ALIAS) == 0)) {
+ (void) unlink(NIS_SEC_CF_PATHNAME);
+ exit(0);
+ }
+
+ if (!(f = fopen(NIS_SEC_CF_PATHNAME, "w"))) {
+ (void) fprintf(stderr,
+ "Could not open %s for writing.\n",
+ NIS_SEC_CF_PATHNAME);
+ exit(1);
+ }
+
+ preamble(f);
+
+ for (i = 1; i < argc; i++) {
+ int j = 0;
+ int gotit = 0;
+
+ while (mechs[j].alias) {
+ if (!(strcmp(argv[i], mechs[j].alias))) {
+ gotit++;
+
+ (void) fprintf(f, "mech\t%s\t%s\t%s\t%s\t%s\n",
+ mechs[j].mechname,
+ mechs[j].keylen,
+ mechs[j].algtype,
+ mechs[j].alias,
+ mechs[j].additional);
+ (void) fflush(f);
+ break;
+ }
+ j++;
+ }
+
+ if (!gotit) {
+ (void) fprintf(stderr,
+ "%s: Mechanism, %s, not found!\n", cmd,
+ argv[i]);
+ (void) fflush(f);
+ (void) fclose(f);
+ exit(1);
+ }
+ }
+ (void) fclose(f);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisbackup.cc b/usr/src/cmd/rpcsvc/nis/utils/nisbackup.cc
new file mode 100644
index 0000000000..3fa1527179
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nisbackup.cc
@@ -0,0 +1,1159 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * nisbackup.cc
+ *
+ * Copyright (c) 1988-1997 Sun Microsystems Inc
+ * All Rights Reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * nisbackup.cc
+ *
+ * A utility for performing database backups.
+ */
+
+#include <stdio.h>
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include <wait.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/nis.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <errno.h>
+#include <rpcsvc/nis_db.h>
+#include <rpcsvc/nis_tags.h>
+#include "nis_bkrst.h"
+#include "../rpc.nisd/nis_proc.h"
+#include "../rpc.nisd/log.h"
+
+/*
+ * Global state variables.
+ */
+static bool_t verbiage = FALSE;
+static bool_t root_dir = FALSE;
+static bool_t file_cleanup = FALSE;
+static bool_t backup_preserved = FALSE;
+static bool_t server_ro = FALSE;
+static int flags = 0;
+static char *nis_local_dir = __nis_rpc_domain();
+static char *backupdir;
+static directory_obj slist;
+
+static nis_error set_ro(void);
+static void set_rw(void);
+static void abort_backup(char *);
+static void abort_handler(int);
+
+/*
+ * Needs to be cleaned up in nislog as well as here. nis_log_common.o
+ * is shared by rpc.nisd, nislog and nisbackup/nisrestore. map_log()
+ * calls abort_transaction, which is defined in nis_svc_log.c. We can't
+ * link with nis_log_svc, so we'll stub it here. It's not used, since
+ * we are not modifying the transaction log.
+ */
+int
+abort_transaction(int xid)
+{
+ xid = 0;
+ return (0);
+}
+
+static void
+init_signals()
+{
+ sigset(SIGHUP, abort_handler);
+ sigset(SIGINT, abort_handler);
+ sigset(SIGTERM, abort_handler);
+}
+
+static void
+abort_handler(int sig)
+{
+ sig = 0;
+ abort_backup(backupdir);
+ exit(1);
+}
+
+void
+usage()
+{
+ fprintf(stderr, "usage: nisbackup [ -v ] backup-dir directory...\n");
+ fprintf(stderr, " nisbackup [ -v ] -a backup-dir\n");
+ exit(1);
+}
+
+struct file_set {
+ char **fs;
+ u_int fssize;
+ u_int fslen;
+};
+
+void
+fspush(char *name, struct file_set *fset)
+{
+ if (fset == NULL) {
+ syslog(LOG_ERR, "fspush: Internal error");
+ exit(1);
+ }
+ if (fset->fs == 0)
+ if ((fset->fs = (char **) calloc(fset->fssize,
+ sizeof (char *))) == NULL) {
+ syslog(LOG_ERR, "Unable to calloc memory: %m");
+ exit(1);
+ }
+ if (fset->fslen == fset->fssize) {
+ fset->fssize += 512;
+ if ((fset->fs = (char **) realloc(fset->fs,
+ fset->fssize * sizeof (char *))) == NULL) {
+ syslog(LOG_ERR, "Unable to calloc memory: %m");
+ exit(1);
+ }
+ }
+ fset->fs[fset->fslen++] = name;
+}
+
+/*
+ * This routine sets the rpc.nisd into a read-only state, so that the
+ * nisbackup can take a snapshot of the database. We set a global (server_ro)
+ * to indicate if we're sucessful. set_rw() is called on a kill signal (^C)
+ * and we don't want to reset the server to read-write if we're being aborted
+ * due to the existance of another nisbackup in progress!
+ */
+static nis_error
+set_ro(void)
+{
+ nis_tag *res = NULL;
+ nis_error error = NIS_SUCCESS;
+ nis_tag tagctl;
+
+ tagctl.tag_val = "";
+ tagctl.tag_type = TAG_READONLY;
+ error = nis_servstate(slist.do_servers.do_servers_val,
+ &tagctl, 1, &res);
+ if (res != NULL) {
+ if (strlen(res->tag_val) != 0) {
+ error = NIS_TRYAGAIN;
+ } else {
+ if (error == NIS_SUCCESS)
+ server_ro = TRUE;
+ }
+ nis_freetags(res, 1);
+ }
+ return (error);
+}
+
+static void
+set_rw(void)
+{
+ nis_tag *res = NULL;
+ nis_error error;
+ nis_tag tagctl;
+
+ /*
+ * If we never set the server to read-only, we don't want to try
+ * to set it to read-write. This could cause another instance of
+ * nisbackup to fail.
+ */
+ if (server_ro == FALSE)
+ return;
+ tagctl.tag_val = "";
+ tagctl.tag_type = TAG_READWRITE;
+ error = nis_servstate(slist.do_servers.do_servers_val,
+ &tagctl, 1, &res);
+ if (error != NIS_SUCCESS) {
+ syslog(LOG_ERR,
+ "Unable to reset rpc.nisd to read/write, kill and restart! %s\n",
+ nis_sperrno(error));
+ }
+ if (res != NULL)
+ nis_freetags(res, 1);
+}
+
+#define PRESERVE 0
+#define RESTORE 1
+
+int
+_mv_dir(char * name, int arg)
+{
+ struct stat s;
+ pid_t child;
+ siginfo_t si;
+ char src[BUFSIZ], dst[BUFSIZ];
+
+ if (arg == PRESERVE) {
+ strcpy(src, name);
+ sprintf(dst, "%stmp", name);
+ } else {
+ sprintf(src, "%stmp", name);
+ strcpy(dst, name);
+ }
+ if (stat(src, &s) == -1) {
+ syslog(LOG_ERR,
+ "Directory %s does not exist: %m", src);
+ return (1);
+ }
+
+ switch (child = vfork()) {
+ case -1: /* error */
+ syslog(LOG_ERR, "Cannot move directory %s", name);
+ return (1);
+ case 0: /* child */
+ if (execl("/bin/mv", "mv", src, dst, NULL) < 0)
+ _exit(1);
+ _exit(0); /* Since this is the child proc, we'll exit */
+ default: /* parent, we'll wait for the cp to complete */
+ if (waitid(P_PID, child, &si, WEXITED) == -1) {
+ syslog(LOG_ERR, "Cannot move directory %s",
+ src);
+ return (1);
+ } else {
+ if (si.si_status != 0) {
+ syslog(LOG_ERR,
+ "Can't move directory: error = %d",
+ si.si_status);
+ return (1);
+ }
+ }
+ }
+ return (0);
+}
+
+int
+_rm_dir(char * name)
+{
+ struct stat s;
+ pid_t child;
+ siginfo_t si;
+
+ if (stat(name, &s) == -1) {
+ syslog(LOG_ERR,
+ "nisbackup: directory %s does not exist: %m",
+ name);
+ return (1);
+ }
+ switch (child = vfork()) {
+ case -1: /* error */
+ syslog(LOG_ERR, "Cannot remove files in %s", name);
+ return (1);
+ case 0: /* child */
+ if (execl("/bin/rm", "rm",
+ "-r",
+ name,
+ NULL) < 0)
+ _exit(1);
+ _exit(0); /* Since this is the child proc, we'll exit */
+ default: /* parent, we'll wait for the cp to complete */
+ if (waitid(P_PID, child, &si, WEXITED) == -1) {
+ syslog(LOG_ERR, "Cannot remove files in %s", name);
+ return (1);
+ } else {
+ if (si.si_status != 0) {
+ syslog(LOG_ERR,
+ "Can't remove files: error = %d",
+ si.si_status);
+ return (1);
+ }
+ }
+ }
+ return (0);
+}
+
+/*
+ * bool_t is_master_of()
+ *
+ * Boolean function to check if local host is the master for the object
+ * passed in nis_obj. If the passed in object is not fully qualified, then
+ * nis_getnames() is called to resolve the object's full name. The parameter,
+ * full_name, is used to return the fully qualified name (ignored if set to
+ * NULL). The serving list is returned in srv_list.
+ */
+
+bool_t
+is_master_of(char *nis_obj, char *full_name, directory_obj *srv_list)
+{
+
+ char myname[NIS_MAXNAMELEN];
+ nis_name *namelist;
+ nis_error err;
+ int i;
+
+ /*
+ * We will use __nis_CacheBind instead of nis_lookup() so that we
+ * can take advantage of the NIS_SHARED_DIRCACHE.
+ */
+ if (nis_obj[strlen(nis_obj) - 1] != '.') { /* Partially qualified */
+ namelist = nis_getnames(nis_obj);
+ for (i = 0; namelist[i]; i++) {
+#ifdef DEBUG
+printf("name: %s\n", namelist[i]);
+#endif
+ err = __nis_CacheBind(namelist[i], srv_list);
+ if (err == NIS_SUCCESS) {
+ if (full_name)
+ sprintf(full_name, "%s", namelist[i]);
+ break;
+ }
+ }
+ } else { /* Fully qualified */
+ err = __nis_CacheBind(nis_obj, srv_list);
+ if (err != NIS_SUCCESS) {
+ if (verbiage)
+ syslog(LOG_INFO,
+ "Unable to lookup %s: %s", nis_obj,
+ nis_sperrno(err));
+ return (0);
+ }
+ if (full_name)
+ sprintf(full_name, "%s", nis_obj);
+ }
+
+ if (err != NIS_SUCCESS) {
+ if (verbiage)
+ syslog(LOG_INFO,
+ "Unable to lookup %s: %s", nis_obj, nis_sperrno(err));
+ return (0);
+ }
+ /*
+ * If we can get a directory object, we check the master server name
+ * against the local host's name. This is the final result.
+ */
+ if (err == NIS_SUCCESS) {
+ sprintf(myname, "%s", nis_local_host());
+ if (strcasecmp(myname,
+ srv_list->do_servers.do_servers_val[0].name)
+ == 0) {
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * This is a link list of all the directories backed up on this server.
+ */
+struct nis_dir_list {
+ char *name;
+ struct file_set obj_fs;
+ struct nis_dir_list *next;
+};
+static struct nis_dir_list *dirlisthead = NULL;
+enum CACHE_OP {CACHE_INIT, CACHE_ADD, CACHE_SAVE, CACHE_FIND};
+/*
+ * dirobj_list() maintains list of directory objects that are backed up
+ * with the -a(ll) option of the nisbackup utility.
+ *
+ * The fclose() calls are checked for error returns, _except_ in the case
+ * where an error has already occured, and we're returning FALSE.
+ */
+struct nis_dir_list *
+dirobj_list(enum CACHE_OP op, char *argp)
+{
+ char filename[BUFSIZ], tmpname[BUFSIZ];
+ char buf[BUFSIZ];
+ FILE *fr, *fw;
+ char *name, *end;
+ int ss;
+ struct nis_dir_list *tmp = NULL;
+ struct nis_dir_list *dlp = NULL;
+ directory_obj sl;
+ struct stat st;
+
+ switch (op) {
+ case CACHE_INIT:
+ /* Initialize cache with the serving list, for -a(ll) */
+ strcpy(filename, nis_data("serving_list"));
+ fr = fopen(filename, "r");
+ if (fr == NULL) {
+ ss = stat(filename, &st);
+ if (ss == -1 && errno == ENOENT) {
+ syslog(LOG_ERR, "Error opening %s: %m",
+ filename);
+ return (NULL);
+ }
+ }
+ while (fgets(buf, BUFSIZ, fr)) {
+ name = buf;
+ while (isspace(*name))
+ name++;
+ end = name;
+ while (!isspace(*end))
+ end++;
+ *end = NULL;
+ tmp = (struct nis_dir_list *)
+ XMALLOC(sizeof (struct nis_dir_list));
+ if (tmp == NULL) {
+ fclose(fr);
+ syslog(LOG_ERR,
+ "Internal error, no memory: %m");
+ exit(1);
+ }
+ /*
+ * If we're the master, and it hasn't already been added...
+ */
+ if (is_master_of(name, tmpname, &sl)) {
+ for (dlp = dirlisthead; dlp; dlp = dlp->next)
+ if (strcasecmp(dlp->name,
+ (char *)tmpname) == 0) {
+ XFREE(tmp);
+ tmp = NULL;
+ break;
+ }
+ if (tmp == NULL)
+ continue;
+ if ((tmp->name = strdup(tmpname)) == NULL) {
+ XFREE(tmp);
+ fclose(fr);
+ syslog(LOG_ERR,
+ "Internal error, no memory: %m");
+ exit(1);
+ }
+ tmp->obj_fs.fssize = BUFSIZ;
+ tmp->obj_fs.fslen = 0;
+ tmp->obj_fs.fs = NULL;
+ tmp->next = dirlisthead;
+ dirlisthead = tmp;
+ } else {
+#ifdef DEBUG
+fprintf(stderr, "dirobj_list: not master for %s\n", name);
+#endif
+ XFREE(tmp);
+ }
+ }
+ if (fclose(fr) == EOF) {
+ syslog(LOG_ERR, "Error closing %s", filename);
+ exit(1);
+ }
+ return (dirlisthead);
+
+ case CACHE_ADD:
+ if (argp == NULL)
+ return (NULL);
+ /* Check whether directory already added */
+ for (tmp = dirlisthead; tmp; tmp = tmp->next)
+ if (strcasecmp(tmp->name, (char *)argp) == 0)
+ return (tmp);
+ /* Add it to the cache */
+ tmp = (struct nis_dir_list *)
+ XMALLOC(sizeof (struct nis_dir_list));
+ if (tmp == NULL) {
+ syslog(LOG_ERR, "Could not allocate memory: %m");
+ return (NULL);
+ }
+ if ((tmp->name = strdup((char *)argp)) == NULL) {
+ syslog(LOG_ERR, "Could not allocate memory: %m");
+ XFREE(tmp);
+ return (NULL);
+ }
+ tmp->obj_fs.fssize = BUFSIZ;
+ tmp->obj_fs.fslen = 0;
+ tmp->obj_fs.fs = NULL;
+ tmp->next = dirlisthead;
+ dirlisthead = tmp;
+#ifdef DEBUG
+ fprintf(stderr, "objdir_list: added : %s\n", tmp->name);
+#endif
+ return (tmp);
+
+ case CACHE_SAVE:
+ /*
+ * Save back'd up object(s) to a file. If argp set, then we're
+ * doing an individual object to the file:
+ * /backupdir/nis+object/backup_list. If argp is NULL, we're
+ * doing the whole cached list in /backupdir/backup_list.
+ */
+ if (argp == NULL)
+ return (NULL);
+ fw = fopen(argp, "w");
+ if (fw == NULL) {
+ ss = stat(argp, &st);
+ if (ss == -1 && errno == ENOENT) {
+ fw = fopen(argp, "w+");
+ }
+ if (fw == NULL) {
+ syslog(LOG_ERR,
+ "Could not open file %s for updating: %m",
+ argp);
+ return (NULL);
+ }
+ }
+ for (tmp = dirlisthead; tmp; tmp = tmp->next)
+ fprintf(fw, "%s\n", (char *)tmp->name);
+ if (fclose(fw) == EOF)
+ return (NULL);
+ return (dirlisthead);
+
+ case CACHE_FIND: /* Search for object in cache */
+ if (argp == NULL)
+ return (NULL);
+ for (tmp = dirlisthead; tmp; tmp = tmp->next)
+ if (strcasecmp(tmp->name, (char *)argp) == 0)
+ return (tmp);
+ return (NULL);
+
+ default:
+ return (NULL);
+ }
+}
+
+void
+abort_backup(char * backupdir)
+{
+ char buf[NIS_MAXNAMELEN];
+ struct stat s;
+ struct nis_dir_list *tmp = NULL;
+
+ if (!backup_preserved) {
+ /*
+ * If we didn't save an existing backup, then we didn't create anything
+ * either, so there's nothing to cleanup.
+ */
+ return;
+ }
+ if (verbiage)
+ syslog(LOG_WARNING,
+ "Aborting NIS+ directory backup. Cleaning up files.");
+ /*
+ * Check if backup/data directory exists. If yes, delete it.
+ */
+ for (tmp = dirlisthead; tmp; tmp = tmp->next) {
+ if (file_cleanup) {
+ sprintf(buf, "%s/%s", backupdir, tmp->name);
+ if (stat(buf, &s) != -1) {
+ if (_rm_dir(buf)) {
+ syslog(LOG_ERR,
+ "Unable to remove directory %s", buf);
+ }
+ } else {
+ if (errno != ENOENT) {
+ syslog(LOG_ERR,
+ "Unable to stat file system directory %s: %m",
+ buf);
+ }
+ }
+ }
+ if (backup_preserved) {
+ if (_mv_dir(buf, RESTORE)) {
+ syslog(LOG_ERR,
+ "Unable to restore old backup %s.tmp",
+ buf);
+ }
+ }
+ }
+}
+
+void
+_cleanup(char * backupdir)
+{
+ char buf[NIS_MAXNAMELEN];
+ struct stat s;
+ struct nis_dir_list *tmp = NULL;
+
+ /*
+ * Check if preserved backup directories exists. If yes, delete them.
+ */
+ for (tmp = dirlisthead; tmp; tmp = tmp->next) {
+ sprintf(buf, "%s/%stmp", backupdir, tmp->name);
+ if (stat(buf, &s) != -1) {
+ if (_rm_dir(buf)) {
+ syslog(LOG_ERR,
+ "Unable to remove old backup %s", buf);
+ }
+ }
+ }
+}
+
+bool_t
+copyfile(char *srcfile, char *destfile)
+{
+ int infd, outfd;
+ char buffer[4096];
+ ssize_t n;
+
+ if ((infd = open(srcfile, O_RDONLY)) == -1)
+ return (FALSE);
+ if ((outfd = open(destfile, O_WRONLY|O_CREAT, 0664)) == -1) {
+ close(infd);
+ return (FALSE);
+ }
+ while (n = read(infd, buffer, sizeof (buffer))) {
+ if (n == -1) {
+ close(infd);
+ close(outfd);
+ return (FALSE);
+ }
+ if (write(outfd, buffer, n) != n) {
+ close(infd);
+ close(outfd);
+ return (FALSE);
+ }
+ }
+ if (close(infd))
+ return (FALSE);
+ if (close(outfd))
+ return (FALSE);
+ return (TRUE);
+}
+
+
+bool_t
+backup(char * backup_path, struct nis_dir_list *dlp)
+{
+ char logfile[NIS_MAXNAMELEN];
+ char srcfile[NIS_MAXNAMELEN], destfile[NIS_MAXNAMELEN];
+ struct stat s;
+ char *fs_p;
+ char **fs;
+ u_int fslen;
+ int i;
+
+ if (verbiage)
+ syslog(LOG_INFO, "Copying files to backup partition: %s",
+ backup_path);
+ fs = dlp->obj_fs.fs;
+ fslen = dlp->obj_fs.fslen;
+ for (i = 0; i < fslen; ++i) {
+ sprintf(logfile, "%s.log", fs[i]);
+ fs_p = (fs[i]) + strlen(nis_data(NULL));
+ sprintf(srcfile, "%s", fs[i]);
+ sprintf(destfile, "%s%s", backup_path, fs_p);
+ if ((stat(logfile, &s) == -1) && errno == ENOENT) {
+ /*
+ * Simple copy of file.
+ */
+ if (copyfile(srcfile, destfile) == FALSE)
+ return (FALSE);
+ } else {
+ /*
+ * This loads the log file (if there is one) and copies all data
+ * to backup file. This esentially checkpoints the data file.
+ */
+ if (db_copy_file(srcfile, destfile) == 0)
+ return (FALSE);
+ }
+ }
+ /*
+ * Finally, some special case files that need to be included, but that
+ * aren't part of the data dictionary.
+ */
+ if ((root_dir) && (strcmp(dlp->name, nis_local_dir) == 0)) {
+ sprintf(srcfile, "%s", nis_data(ROOT_OBJ));
+ sprintf(destfile, "%s/%s", backup_path, ROOT_OBJ);
+ return (copyfile(srcfile, destfile));
+ }
+ return (TRUE);
+}
+
+
+/*
+ * bool_t is_this_a_dir()
+ *
+ * Boolean function to check if the object passed in is a directory object,
+ * as opposed to a table object. This routine uses nis_lookup(), in nis_db.c,
+ * to get a nis_object structure, which identifies the object type.
+ */
+
+bool_t
+is_this_a_dir(nis_name name)
+{
+ nis_result *res;
+ char fullname[NIS_MAXNAMELEN];
+
+ sprintf(fullname, "%s.%s", name, nis_local_dir);
+#ifdef DEBUG
+ fprintf(stderr, "is_this_a_dir: looking up %s\n", fullname);
+#endif
+ if ((res = nis_lookup(fullname, MASTER_ONLY+USE_DGRAM)) == NULL)
+ return (FALSE);
+ if (res->status != NIS_SUCCESS) {
+#ifdef DEBUG
+ syslog(LOG_INFO, "Unable to lookup: %s: %s", fullname,
+ nis_sperrno(res->status));
+#endif
+ return (FALSE);
+ }
+ if (res->status == NIS_SUCCESS) {
+#ifdef DEBUG
+ fprintf(stderr, "is_this_a_dir: %s found, type:%d\n", fullname,
+ res->objects.objects_val);
+#endif
+ if (__type_of(res->objects.objects_val) == NIS_DIRECTORY_OBJ)
+ return (TRUE);
+ else
+ return (FALSE);
+ }
+ return (FALSE);
+}
+
+void
+sanity_checks(char *backupdir)
+{
+ struct stat s;
+ char buf[NIS_MAXNAMELEN];
+ struct stat st_t;
+ nis_error error = NIS_SUCCESS;
+ struct nis_dir_list *tmp = NULL;
+
+ if ((backupdir) && (*backupdir != '/')) {
+ syslog(LOG_ERR, "%s is not a absolute path name.", backupdir);
+ exit(1);
+ }
+ if (stat(backupdir, &s) == -1) {
+ if (errno == ENOENT) {
+ syslog(LOG_ERR,
+ "Directory %s does not exist, please create.",
+ backupdir);
+ exit(1);
+ } else {
+ syslog(LOG_ERR,
+ "An error occured while accessing %s: %m", backupdir);
+ exit(1);
+ }
+ }
+
+ /*
+ * Are we backing up a root-domain ? Note, either the root object
+ * has been specified, or the -a(ll) option. If so, check for the
+ * existence of a root object.
+ */
+
+ sprintf(buf, "%s", nis_data(ROOT_OBJ));
+ if ((dirobj_list(CACHE_FIND, nis_local_dir)) ||
+ (flags & DOMAIN_ALL)) {
+ if (stat(buf, &st_t) == -1) {
+ if (errno == ENOENT)
+ root_dir = FALSE;
+ }
+ else
+ root_dir = TRUE;
+ }
+
+ /*
+ * We can initialize the object cache with the serving list if we
+ * are backing up -a(ll). If the directory object(s) to backup is
+ * specified (not -a(ll) option), add that in to initialize the cache.
+ */
+ if (flags & DOMAIN_ALL) {
+ if (dirobj_list(CACHE_INIT, NULL) == NULL) {
+ syslog(LOG_ERR,
+ "Could not initialize, is this a master server?");
+ exit(1);
+ }
+ /*
+ * The cache is initialized, now we need initialize the server
+ * list.
+ */
+ if (is_master_of(dirlisthead->name, NULL, &slist) == FALSE) {
+ syslog(LOG_ERR, "Not master for %s", dirlisthead->name);
+ exit(1);
+ }
+ }
+#ifdef DEBUG
+ for (tmp = dirlisthead; tmp; tmp = tmp->next)
+ fprintf(stderr, "%s\n", tmp->name);
+#endif
+
+ /*
+ * Backup dirs setup, initialize signal handlers, which will cleanup
+ * if nisbackup is interupted from this point.
+ */
+ init_signals();
+
+ /*
+ * Make sure we reset the read-write flag in the server upon exit.
+ * set_rw() checks server_ro == TRUE, to make sure we actually set
+ * it to read-only!
+ */
+ atexit(set_rw);
+
+ /*
+ * Set server state to read-only. This must be done to take a backup.
+ */
+ int slp_time = 2;
+ int retries = 0;
+ while (((error = set_ro()) != NIS_SUCCESS) &&
+ (retries++ < MAXRETRIES)) {
+ if (verbiage) {
+ syslog(LOG_INFO,
+ "Unable to set server to read-only: %s",
+ nis_sperrno(error));
+ syslog(LOG_INFO, "Retrying in %d seconds", slp_time);
+ }
+ sleep(slp_time);
+ slp_time = slp_time * 2;
+ }
+ if (error != NIS_SUCCESS) {
+ syslog(LOG_INFO,
+ "Unable to set server to read-only mode, exiting.");
+ abort_backup(backupdir);
+ exit(1);
+ }
+
+ /*
+ * Check if backup directory(ies) exists. If not, create. If they
+ * do exist (old backup), save them off, until new backup created.
+ * Upon successful creation of a new backup, remove the saved copy.
+ */
+ for (tmp = dirlisthead; tmp; tmp = tmp->next) {
+ sprintf(buf, "%s/%s", backupdir, tmp->name);
+ if (stat(buf, &s) == -1) {
+ if (errno == ENOENT) {
+ if (mkdir(buf, 0777)) {
+ syslog(LOG_ERR,
+ "Unable to create directory %s: %m",
+ buf);
+ exit(1);
+ }
+ } else {
+ syslog(LOG_ERR,
+ "Unable to stat file system directory %s: %m",
+ buf);
+ exit(1);
+ }
+ } else {
+ /*
+ * Move old backup to backup-dir.tmp. Once backup
+ * successful, backup-dir.tmp will be removed. In
+ * case of failure, we will not lose old backup.
+ */
+ if (_mv_dir(buf, PRESERVE)) {
+ syslog(LOG_ERR,
+ "Unable to save old backup %s", buf);
+ exit(1);
+ }
+ backup_preserved = TRUE;
+ if (mkdir(buf, 0777)) {
+ syslog(LOG_ERR,
+ "Unable to create directory %s: %m",
+ buf);
+ exit(1);
+ }
+ file_cleanup = TRUE;
+ }
+
+ /*
+ * Check for a "data" directory under each backup-dir.
+ */
+ sprintf(buf, "%s/%s", buf, NIS_DIR);
+ if (stat(buf, &s) == -1) {
+ if (errno == ENOENT) {
+ if (mkdir(buf, 0777)) {
+ syslog(LOG_ERR,
+ "Unable to create directory %s: %m",
+ buf);
+ abort_backup(backupdir);
+ exit(1);
+ }
+ } else {
+ syslog(LOG_ERR,
+ "Unable to stat directory %s: %m", buf);
+ abort_backup(backupdir);
+ exit(1);
+ }
+ }
+ }
+}
+
+void
+determine_file_set()
+{
+ char localdir[NIS_MAXNAMELEN];
+ char full_name[NIS_MAXNAMELEN];
+ char buf[NIS_MAXNAMELEN];
+ char *domainof;
+ DIR *dirptr;
+ struct dirent *dent;
+ struct nis_dir_list *tmp = NULL;
+
+
+ sprintf(localdir, "%s", nis_data(NULL));
+ if ((dirptr = opendir(localdir)) == NULL) {
+ syslog(LOG_ERR, "Unable to open %s: %m", localdir);
+ exit(1);
+ }
+
+/* This assumes that the dictionary has been initialized */
+
+ while ((dent = readdir(dirptr)) != NULL) {
+ if (strcmp(dent->d_name, ".") == 0)
+ continue; /* Ignore */
+ if (strcmp(dent->d_name, "..") == 0)
+ continue; /* Ignore */
+ if (strstr(dent->d_name, ".log"))
+ continue; /* Ignore log files */
+ if (strcmp(dent->d_name, ROOT_OBJ) == 0)
+ continue; /* Ignore root.object, we'll get it later */
+
+ sprintf(buf, "%s/%s", nis_data(NULL), dent->d_name);
+ sprintf(full_name, "%s.%s", dent->d_name, nis_local_dir);
+#ifdef DEBUG
+ printf("searching for: %s\n", buf);
+ printf("full name: %s\n", full_name);
+#endif
+/*
+ * Look up the file in the data dictionary. It could be a stray ufs
+ * file, that has nothing to do with NIS+.
+ */
+ if (db_in_dict_file(buf)) { /* In dictionary? */
+/*
+ * Check for the directory objects that are being backed up. Put them into
+ * their own fileset, instead of the directory object that they belong
+ * to. This insures that each object backup is autonomous, and can be restored
+ * separately. Is it a DIRECTORY? Is it in list of objects to backup? fspush it!
+ */
+ if (is_this_a_dir(dent->d_name)) {
+ if ((tmp = dirobj_list(CACHE_FIND, full_name))
+ == NULL)
+ continue;
+ } else {
+/*
+ * Determine the which directory object this object belongs to.
+ */
+ domainof = nis_domain_of(full_name);
+ if ((tmp = dirobj_list(CACHE_FIND,
+ domainof)) == NULL)
+ continue;
+ }
+ fspush(strdup(buf), &(tmp->obj_fs));
+ continue;
+ } /* In data dictionary */
+ } /* while (readdir) */
+ closedir(dirptr);
+}
+
+
+bool_t
+create_perdir_translog(char *newlog, struct nis_dir_list *dlp)
+{
+ directory_obj sl;
+ char fullname[NIS_MAXNAMELEN];
+ char *str_p = fullname;
+ uint32_t u_time;
+ uint32_t u_nl;
+ FILE *fw;
+ int ss;
+ struct stat st;
+ int i;
+
+ fw = fopen(newlog, "w");
+ if (fw == NULL) {
+ ss = stat(newlog, &st);
+ if (ss == -1 && errno == ENOENT)
+ if ((fw = fopen(newlog, "w+")) == NULL) {
+ syslog(LOG_ERR,
+ "Cannot open file %s for updating: %m", newlog);
+ return (FALSE);
+ }
+ }
+/*
+ * Write a backup revision number to the this file, so that nisrestore(1M)
+ * will know if it can understand this backup.
+ */
+ u_nl = htonl(BACKUPREV);
+ if (!fwrite(&u_nl, sizeof (u_nl), 1, fw)) {
+ syslog(LOG_ERR, "Cannot write rev # to file %s: %d",
+ newlog, ferror(fw));
+ return (FALSE);
+ }
+/*
+ * Find the last time stamp for the directory(ies) being modified.
+ */
+ if (is_master_of(dlp->name, str_p, &sl) == FALSE) {
+ syslog(LOG_ERR, "Sorry, %s is not the master for %s",
+ nis_local_host(), dlp->name);
+ return (FALSE);
+ }
+/*
+ * Since, we are running as root, the nis_local_principal() should be OK.
+ */
+ for (i = 0; i < MAXRETRIES; i++)
+ if ((u_time = nis_cptime(sl.do_servers.do_servers_val,
+ fullname)) != 0)
+ break;
+ if (u_time == 0) {
+ syslog(LOG_ERR, "Unable to get last update time for %s",
+ fullname);
+ return (FALSE);
+ }
+ u_nl = htonl(u_time);
+ if (!fwrite(&u_nl, sizeof (u_nl), 1, fw)) {
+ syslog(LOG_ERR, "Cannot write update to file %s: error %d",
+ newlog, ferror(fw));
+ return (FALSE);
+ }
+#ifdef DEBUG
+ fprintf(stderr, "create_perdir_translog: last update: %d\n", u_time);
+#endif
+ if (fclose(fw) == EOF) {
+ syslog(LOG_ERR, "Error closing file %s", newlog);
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+merge_backup_list()
+{
+ char buf[BUFSIZ];
+ char filename[BUFSIZ];
+ FILE *fr;
+ char *name, *end;
+ int ss;
+ struct stat st;
+
+ sprintf(filename, "%s/%s", backupdir, BACKUPLIST);
+ ss = stat(filename, &st);
+ if (ss == -1 && errno == ENOENT) {
+ /*
+ * If it doesn't exist, CACHE_SAVE will create it.
+ */
+ return ((dirobj_list(CACHE_SAVE, filename) == dirlisthead));
+ } else {
+ fr = fopen(filename, "r");
+ if (fr == NULL)
+ return (FALSE);
+ while (fgets(buf, BUFSIZ, fr)) {
+ name = buf;
+ while (isspace(*name))
+ name++;
+ end = name;
+ while (!isspace(*end))
+ end++;
+ *end = NULL;
+ if (dirobj_list(CACHE_ADD, name) == NULL) {
+ fclose(fr);
+ return (FALSE);
+ }
+ }
+ fclose(fr);
+ return ((dirobj_list(CACHE_SAVE, filename) == dirlisthead));
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ char nisdomain[NIS_MAXNAMELEN];
+ char buf[NIS_MAXNAMELEN];
+ int c;
+ bool_t dbstat;
+ char *tmp;
+ struct nis_dir_list *dl = NULL;
+
+ openlog("nisbackup", LOG_PID+LOG_NOWAIT, LOG_USER);
+ if (geteuid() != 0) {
+ syslog(LOG_ERR, "You must be root to run nisbackup.");
+ exit(1);
+ }
+ if (argc < 3)
+ usage();
+
+ while ((c = getopt(argc, argv, "av")) != -1) {
+ switch (c) {
+ case 'a' :
+ flags |= DOMAIN_ALL;
+ break;
+ case 'v' :
+ verbiage = TRUE;
+ break;
+ default:
+ usage();
+ }
+ }
+ if (flags & DOMAIN_ALL) {
+ if (argc - optind != 1)
+ usage();
+ backupdir = argv[optind++];
+ } else {
+ if (argc - optind < 2)
+ usage();
+ backupdir = argv[optind++];
+ /*
+ * Yes, I really mean "=", not "==". tmp is assigned in while.
+ */
+ while (tmp = argv[optind++]) {
+ memset(nisdomain, 0, sizeof (nisdomain));
+ if (is_master_of(tmp, nisdomain, &slist)) {
+ if (dirobj_list(CACHE_ADD, nisdomain)
+ == NULL) {
+ syslog(LOG_ERR,
+ "Internal error, unable to add to cache");
+ exit(1);
+ }
+ } else {
+ syslog(LOG_ERR, "Not master for %s", tmp);
+ exit(1);
+ }
+ }
+ }
+ sanity_checks(backupdir);
+ sprintf(buf, "%s.dict", nis_data(NULL));
+ dbstat = db_initialize(buf);
+ if (dbstat == 0) {
+ abort_backup(backupdir);
+ syslog(LOG_ERR, "Unable to initialize %s", buf);
+ exit(1);
+ }
+ determine_file_set();
+/*
+ * Loop through dirobj_list, creating the backup_path for each
+ * object in the cache, then db_extrace_dict_entries, backup,
+ * create_perdir_translog and write backup_list for each entry in the cache.
+ */
+ for (dl = dirlisthead; dl; dl = dl->next) {
+ sprintf(buf, "%s/%s/%s.dict", backupdir, dl->name, NIS_DIR);
+ if (!db_extract_dict_entries(buf, dl->obj_fs.fs,
+ dl->obj_fs.fslen)) {
+ abort_backup(backupdir);
+ syslog(LOG_ERR, "Unable to create %s.", buf);
+ exit(1);
+ }
+ sprintf(buf, "%s/%s/%s", backupdir, dl->name, NIS_DIR);
+ if (!backup(buf, dl)) {
+ abort_backup(backupdir);
+ syslog(LOG_ERR, "Unable to backup files to %s.", buf);
+ exit(1);
+ }
+ sprintf(buf, "%s/%s/%s", backupdir, dl->name, LASTUPDATE);
+ if (!create_perdir_translog(buf, dl)) {
+ abort_backup(backupdir);
+ syslog(LOG_ERR, "Unable to create %s.", buf);
+ exit(1);
+ }
+ }
+ if (!merge_backup_list()) {
+ abort_backup(backupdir);
+ syslog(LOG_ERR, "Unable to create %s.", BACKUPLIST);
+ exit(1);
+ }
+ _cleanup(backupdir);
+ exit(0);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/utils/niscat.c b/usr/src/cmd/rpcsvc/nis/utils/niscat.c
new file mode 100644
index 0000000000..ca46db0d77
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/niscat.c
@@ -0,0 +1,284 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * niscat.c
+ *
+ * Copyright 1988-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * niscat.c
+ *
+ * nis+ table cat utility
+ */
+
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/nis.h>
+#include <strings.h>
+#include <stdlib.h>
+
+extern int optind;
+extern char *optarg;
+
+
+#define BINARY_STR "*BINARY*"
+
+
+struct pl_data {
+ unsigned flags;
+ char ta_sep;
+};
+
+#define PL_BINARY 1
+
+int
+print_line(tab, ent, udata)
+ char *tab;
+ nis_object *ent;
+ void *udata;
+{
+ register entry_col *ec = ent->EN_data.en_cols.en_cols_val;
+ register int ncol = ent->EN_data.en_cols.en_cols_len;
+ register struct pl_data *d = (struct pl_data *)udata;
+ register int i;
+ int len;
+
+ for (i = 0; i < ncol; i++) {
+ if (i > 0)
+ printf("%c", d->ta_sep);
+ len = ec[i].ec_value.ec_value_len;
+ if (len != 0) {
+ if (ec[i].ec_flags & EN_BINARY) {
+ if (d->flags & PL_BINARY) {
+ fwrite(ec[i].ec_value.ec_value_val,
+ 1, len, stdout);
+ } else {
+ printf(BINARY_STR);
+ }
+ } else {
+ printf("%s", ec[i].ec_value.ec_value_val);
+ }
+ }
+ }
+ printf("\n");
+
+ return (0);
+}
+
+int
+print_object(tab, ent, udata)
+ char *tab;
+ nis_object *ent;
+ void *udata;
+{
+ nis_print_object(ent);
+ return (0);
+}
+
+
+#define F_HEADER 1
+#define F_OBJECT 2
+
+void
+usage()
+{
+ fprintf(stderr, "usage: niscat [-LAMhv] [-s sep] tablename ...\n");
+ fprintf(stderr, " niscat [-LPAM] -o name ...\n");
+ exit(1);
+}
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+ unsigned flags = 0;
+ char *name;
+ nis_result *tres, *eres;
+ char tname[NIS_MAXNAMELEN];
+ struct pl_data pld;
+ int ncol, i;
+ ulong_t flinks = 0, fpath = 0, allres = 0, master = 0;
+ int error = 0;
+ int bad_name;
+ ulong_t list_flags;
+ nis_object *obj;
+
+ /*
+ * By default, don't print binary data.
+ */
+ pld.flags = 0;
+
+ pld.ta_sep = '\0';
+ while ((c = getopt(argc, argv, "LPAMohvs:")) != -1) {
+ switch (c) {
+ case 'L' :
+ flinks = FOLLOW_LINKS;
+ break;
+ case 'P':
+ fpath = FOLLOW_PATH;
+ break;
+ case 'A':
+ allres = ALL_RESULTS;
+ break;
+ case 'M':
+ master = MASTER_ONLY;
+ break;
+ case 'h':
+ flags |= F_HEADER;
+ break;
+ case 'v':
+ pld.flags |= PL_BINARY;
+ break;
+ case 'o':
+ flags |= F_OBJECT;
+ break;
+ case 's':
+ if (strlen(optarg) != 1) {
+ fprintf(stderr,
+ "separator must be a single character\n");
+ exit(1);
+ }
+ pld.ta_sep = *optarg;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (optind == argc) /* no table name */
+ usage();
+
+ while (optind < argc) {
+ name = argv[optind++];
+
+ if (flags & F_OBJECT) {
+ if (*name == '[') {
+ list_flags = fpath|allres|master|
+ FOLLOW_LINKS|EXPAND_NAME;
+ tres = nis_list(name, list_flags,
+ print_object, 0);
+ if ((tres->status != NIS_CBRESULTS) &&
+ (tres->status != NIS_NOTFOUND)) {
+ nis_perror(tres->status, name);
+ error = 1;
+ goto loop;
+ }
+ } else {
+ list_flags = flinks|master|EXPAND_NAME;
+ tres = nis_lookup(name, list_flags);
+ if (tres->status != NIS_SUCCESS) {
+ nis_perror(tres->status, name);
+ error = 1;
+ goto loop;
+ }
+ nis_print_object(tres->objects.objects_val);
+ }
+ goto loop;
+ }
+
+ /*
+ * Get the table object using expand name magic.
+ */
+ tres = nis_lookup(name, master|FOLLOW_LINKS|EXPAND_NAME);
+ if (tres->status != NIS_SUCCESS) {
+ nis_perror(tres->status, name);
+ error = 1;
+ goto loop;
+ }
+
+ /*
+ * Construct the name for the table that we found.
+ */
+ bad_name = (snprintf(tname, sizeof (tname), "%s.",
+ tres->objects.objects_val[0].zo_name)
+ >= sizeof (tname));
+ if (!bad_name &&
+ *(tres->objects.objects_val[0].zo_domain) != '.')
+ bad_name = (strlcat(tname,
+ tres->objects.objects_val[0].zo_domain,
+ sizeof (tname)) >= sizeof (tname));
+
+ if (bad_name) {
+ nis_perror(NIS_BADNAME, "can't list table");
+ error = 1;
+ goto loop;
+ }
+
+ /*
+ * Make sure it's a table object.
+ */
+ if (tres->objects.objects_val[0].zo_data.zo_type !=
+ NIS_TABLE_OBJ) {
+ fprintf(stderr, "%s is not a table!\n", tname);
+ error = 1;
+ goto loop;
+ }
+
+ /*
+ * Use the table's separator character when printing entries.
+ * Unless one was specified w/ -s
+ */
+ if (pld.ta_sep == '\0') {
+ pld.ta_sep
+ = tres->objects.objects_val[0].TA_data.ta_sep;
+ }
+
+ /*
+ * Print column names if F_HEADER is set.
+ */
+ if (flags & F_HEADER) {
+ obj = &tres->objects.objects_val[0];
+ ncol = obj->TA_data.ta_cols.ta_cols_len;
+ c = pld.ta_sep;
+ printf("# ");
+ for (i = 0; i < ncol; i++) {
+ if (i > 0)
+ printf("%c", c);
+ printf("%s",
+ obj->TA_data.ta_cols.ta_cols_val[i].tc_name);
+ }
+ printf("\n");
+ }
+
+ /*
+ * Cat the table using a callback function.
+ */
+ list_flags = allres|master;
+ eres = nis_list(tname, list_flags, print_line, (void *)&(pld));
+ if ((eres->status != NIS_CBRESULTS) &&
+ (eres->status != NIS_NOTFOUND)) {
+ nis_perror(eres->status, "can't list table");
+ error = 1;
+ }
+ nis_freeresult(eres);
+
+ loop:
+ nis_freeresult(tres);
+ }
+
+ exit(error);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nischmod.c b/usr/src/cmd/rpcsvc/nis/utils/nischmod.c
new file mode 100644
index 0000000000..823d9e1d77
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nischmod.c
@@ -0,0 +1,339 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * nischmod.c
+ *
+ * Copyright 1988-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * nischmod.c
+ *
+ * nis+ object chmod/chown/chgrp/chttl utility
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/nis.h>
+#include <stdlib.h>
+
+extern int optind;
+extern char *optarg;
+
+extern bool_t nis_verifycred();
+
+#define CMD_CHMOD 0
+#define CMD_CHOWN 1
+#define CMD_CHGRP 2
+#define CMD_CHTTL 3
+
+int cmdnum;
+char *cmdname;
+
+void
+usage()
+{
+ char *str;
+
+ switch (cmdnum) {
+ case CMD_CHMOD:
+ str = "mode";
+ break;
+ case CMD_CHOWN:
+ str = "owner";
+ break;
+ case CMD_CHGRP:
+ str = "group";
+ break;
+ case CMD_CHTTL:
+ str = "ttl";
+ break;
+ }
+
+ fprintf(stderr, "usage: %s [-LPAf] %s name ...\n",
+ cmdname, str);
+ exit(1);
+}
+
+
+void
+change_object(obj, udata)
+ nis_object *obj;
+ void *udata;
+{
+ switch (cmdnum) {
+ case CMD_CHMOD:
+ parse_rights(&(obj->zo_access), (char *)udata);
+ break;
+ case CMD_CHOWN:
+ obj->zo_owner = (nis_name)udata;
+ break;
+ case CMD_CHGRP:
+ obj->zo_group = (nis_name)udata;
+ break;
+ case CMD_CHTTL:
+ obj->zo_ttl = *(ulong_t *)udata;
+ if (obj->zo_data.zo_type == NIS_DIRECTORY_OBJ)
+ obj->DI_data.do_ttl = *(ulong_t *)udata;
+ break;
+ }
+}
+
+
+static char force = 0;
+static int error = 0;
+
+static int
+change_entry(name, ent, udata)
+ nis_name name;
+ nis_object *ent;
+ void *udata;
+{
+ nis_object newobj;
+ nis_result *res;
+
+ newobj = *ent;
+ change_object(&newobj, udata);
+ res = nis_modify_entry(name, &newobj, MOD_SAMEOBJ);
+ if (res->status != NIS_SUCCESS) {
+ if (!force) {
+ nis_perror(res->status, "can't modify entry");
+ error = 1;
+ }
+ }
+ nis_freeresult(res);
+ return (0);
+}
+
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+ char **plist, **p;
+ ulong_t flinks = 0, fpath = 0, allres = 0;
+ char *name;
+ void *udata;
+ uint_t x;
+ int bad_name;
+ nis_result *ores, *mres;
+ char oname[NIS_MAXNAMELEN];
+ nis_object newobj;
+
+ if (cmdname = strrchr(argv[0], '/'))
+ cmdname++;
+ else
+ cmdname = argv[0];
+
+ if (strcmp(cmdname, "nischmod") == 0)
+ cmdnum = CMD_CHMOD;
+ else if (strcmp(cmdname, "nischown") == 0)
+ cmdnum = CMD_CHOWN;
+ else if (strcmp(cmdname, "nischgrp") == 0)
+ cmdnum = CMD_CHGRP;
+ else if (strcmp(cmdname, "nischttl") == 0)
+ cmdnum = CMD_CHTTL;
+ else {
+ fprintf(stderr, "%s: bad command.\n", cmdname);
+ exit(1);
+ }
+
+ while ((c = getopt(argc, argv, "LPAf")) != -1) {
+ switch (c) {
+ case 'L':
+ flinks = FOLLOW_LINKS;
+ break;
+ case 'P':
+ fpath = FOLLOW_PATH;
+ break;
+ case 'A':
+ allres = ALL_RESULTS;
+ break;
+ case 'f':
+ force = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (argc - optind < 2)
+ usage();
+
+ switch (cmdnum) {
+ case CMD_CHMOD:
+ /*
+ * attempt to parse the access rights first
+ */
+ udata = argv[optind++];
+ if (!parse_rights(&x, udata)) {
+ fprintf(stderr, "Can't parse access rights \"%s\"\n",
+ argv[optind-1]);
+ usage();
+ }
+ break;
+ case CMD_CHOWN:
+ /*
+ * get the principal name using psuedo expand name
+ * magic
+ */
+ udata = argv[optind++];
+ if ((plist = nis_getnames(udata)) == 0) {
+ /* Could be either no mem or more likely bad name */
+ nis_perror(NIS_BADNAME, udata);
+ exit(1);
+ }
+ for (p = plist; *p; p++) {
+ ulong_t flags;
+ /*
+ * First try to verify through regular means and only
+ * if that fails, then try the Master server.
+ */
+ flags = (ulong_t)(USE_DGRAM|FOLLOW_LINKS|FOLLOW_PATH);
+ if (nis_verifycred(*p, flags) ||
+ nis_verifycred(*p, (ulong_t)(flags | MASTER_ONLY)))
+ break;
+ }
+ if (*p == 0) {
+ if (!force) {
+ fprintf(stderr,
+ "%s: principal not found\n",
+ udata);
+ exit(1);
+ } else if (
+ ((char *)udata)[strlen((char *)udata)-1] != '.')
+ exit(0);
+ } else
+ udata = *p;
+ break;
+ case CMD_CHGRP:
+ /*
+ * get the group name using psuedo expand name magic
+ */
+ udata = argv[optind++];
+ if ((plist = nis_getnames(udata)) == 0) {
+ /* Could be either no mem or more likely bad name */
+ nis_perror(NIS_BADNAME, udata);
+ exit(1);
+ }
+ for (p = plist; *p; p++)
+ if (nis_verifygroup(*p) == NIS_SUCCESS)
+ break;
+ if (*p == 0) {
+ if (!force) {
+ fprintf(stderr,
+ "%s: group not found\n",
+ udata);
+ exit(1);
+ } else if (
+ ((char *)udata)[strlen((char *)udata)-1] != '.')
+ exit(0);
+ } else
+ udata = *p;
+ break;
+ case CMD_CHTTL:
+ if (!parse_time(&x, argv[optind++])) {
+ fprintf(stderr, "can't parse time \"%s\"\n",
+ argv[optind-1]);
+ usage();
+ }
+ udata = &x;
+ break;
+ }
+
+ while (argc > optind) {
+ name = argv[optind++];
+
+ /*
+ * Get the object using expand name magic.
+ */
+ if (*name == '[') {
+ ores = nis_list(name,
+ fpath|allres|FOLLOW_LINKS|MASTER_ONLY|EXPAND_NAME,
+ change_entry, udata);
+ if (ores->status != NIS_CBRESULTS &&
+ ores->status != NIS_NOTFOUND) {
+ if (!force) {
+ nis_perror(ores->status, name);
+ error = 1;
+ }
+ }
+ } else {
+ ores = nis_lookup(name, flinks|MASTER_ONLY|EXPAND_NAME);
+ if (ores->status != NIS_SUCCESS) {
+ if (!force) {
+ nis_perror(ores->status, name);
+ error = 1;
+ }
+ goto loop;
+ }
+
+ /*
+ * Construct the name for the object that we found.
+ */
+ bad_name = (snprintf(oname, sizeof (oname), "%s.",
+ ores->objects.objects_val[0].zo_name)
+ >= sizeof (oname));
+ if (!bad_name &&
+ *(ores->objects.objects_val[0].zo_domain) != '.')
+ bad_name = (strlcat(oname,
+ ores->objects.objects_val[0].zo_domain,
+ sizeof (oname)) >= sizeof (oname));
+
+ if (bad_name) {
+ if (!force) {
+ fprintf(stderr, "%s: ", oname);
+ nis_perror(NIS_BADNAME,
+ "can't modify object");
+ error = 1;
+ }
+ goto loop;
+ }
+
+ /*
+ * Modify the object.
+ */
+ newobj = ores->objects.objects_val[0];
+ change_object(&newobj, udata);
+ mres = nis_modify(oname, &newobj);
+ if (mres->status != NIS_SUCCESS) {
+ if (!force) {
+ fprintf(stderr, "%s: ", oname);
+ nis_perror(mres->status,
+ "can't modify object");
+ error = 1;
+ }
+ }
+ nis_freeresult(mres);
+ }
+
+ loop:
+ nis_freeresult(ores);
+ }
+
+ exit(error);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisdefaults.c b/usr/src/cmd/rpcsvc/nis/utils/nisdefaults.c
new file mode 100644
index 0000000000..bc90747d1b
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nisdefaults.c
@@ -0,0 +1,203 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * nisdefaults.c
+ *
+ * Copyright (c) 1988-1992 Sun Microsystems Inc
+ * All Rights Reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * nisdefaults.c
+ *
+ * A simple utility to tell you what the defaults are that will be
+ * plugged into object creation.
+ *
+ * Copyright (c) 1992 Sun Microsystems Inc.
+ * All rights reserved.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <rpcsvc/nis.h>
+
+extern int key_secretkey_is_set_g();
+
+usage(cmd)
+ char *cmd;
+{
+ fprintf(stderr, "usage: %s [-pdhgrtsav]\n", cmd);
+ fprintf(stderr, " p = default principal name.\n");
+ fprintf(stderr, " d = default domain name.\n");
+ fprintf(stderr, " h = default host name.\n");
+ fprintf(stderr, " g = default group name.\n");
+ fprintf(stderr, " r = default access rights.\n");
+ fprintf(stderr, " t = default time to live.\n");
+ fprintf(stderr, " s = default search path.\n");
+ fprintf(stderr, " a = all of the above (default).\n");
+ fprintf(stderr, " v = verbose.\n");
+
+ exit(1);
+}
+
+extern int optind;
+extern char *optarg;
+
+extern nis_object nis_default_obj;
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ nis_name *result;
+ int c;
+ u_long secs, days, hrs, mins;
+ int i, verbose = 1,
+ pa = 1, /* print everything */
+ ph = 0, /* print host */
+ pd = 0, /* print domain */
+ pp = 0, /* print principal */
+ pr = 0, /* print rights */
+ pt = 0, /* print ttl */
+ pg = 0, /* print group */
+ ps = 0; /* print search path */
+
+ while ((c = getopt(argc, argv, "hpartgdsv")) != -1) {
+ switch (c) {
+ case 'v' :
+ verbose = 50;
+ break;
+ case 'h' :
+ pa = 0;
+ ph++;
+ verbose--;
+ break;
+ case 'p' :
+ pa = 0;
+ verbose--;
+ pp++;
+ break;
+ case 'r' :
+ pa = 0;
+ verbose--;
+ pr++;
+ break;
+ case 't' :
+ pa = 0;
+ verbose--;
+ pt++;
+ break;
+ case 'g' :
+ pa = 0;
+ verbose--;
+ pg++;
+ break;
+ case 'd' :
+ pa = 0;
+ verbose--;
+ pd++;
+ break;
+ case 's' :
+ pa = 0;
+ verbose--;
+ ps++;
+ break;
+ case 'a' :
+ pa++;
+ verbose--;
+ break;
+ case '?' :
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ if (verbose < 0)
+ verbose = 0;
+ nis_defaults_init(NULL);
+ if (pa || pp) {
+ if (verbose)
+ printf("Principal Name : ");
+ printf("%s", nis_default_obj.zo_owner);
+ if (verbose && ! key_secretkey_is_set_g(0, 0))
+ printf(" (not authenticated)");
+ printf("\n");
+ }
+ if (pa || pd) {
+ if (verbose)
+ printf("Domain Name : ");
+ printf("%s\n", nis_local_directory());
+ }
+ if (pa || ph) {
+ if (verbose)
+ printf("Host Name : ");
+ printf("%s\n", nis_local_host());
+ }
+ if (pa || pg) {
+ if (verbose)
+ printf("Group Name : ");
+ if (strlen(nis_default_obj.zo_group) != 0)
+ printf("%s\n", nis_default_obj.zo_group);
+ else
+ printf("%s\n", nis_local_group());
+ }
+ if (pa || pr) {
+ if (verbose)
+ printf("Access Rights : ");
+ nis_print_rights(nis_default_obj.zo_access);
+ printf("\n");
+ }
+ if (pa || pt) {
+ if (verbose)
+ printf("Time to live : ");
+ secs = nis_default_obj.zo_ttl;
+ days = secs / 86400;
+ hrs = (secs - (days * 86400)) / 3600;
+ mins = (secs - (days * 86400) - (hrs * 3600)) / 60;
+ secs = secs % 60;
+ if (verbose) {
+ if (days)
+ printf("%dD, ", days);
+ printf("%02d:%02d:%02d\n", hrs, mins, secs);
+ } else
+ printf("%d\n", nis_default_obj.zo_ttl);
+ }
+ if (pa || ps) {
+ result = nis_getnames("foo");
+ if (verbose)
+ printf("Search Path : ");
+ if (result && result[0]) {
+ printf("%s\n", nis_domain_of(result[0]));
+ i = 1;
+ while (result[i]) {
+ if (verbose)
+ printf(" ");
+ printf("%s\n",
+ nis_domain_of(result[i++]));
+ }
+ } else
+ printf("**NONE**\n");
+ }
+ exit(0);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/utils/niserror.c b/usr/src/cmd/rpcsvc/nis/utils/niserror.c
new file mode 100644
index 0000000000..3eb1089ff4
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/niserror.c
@@ -0,0 +1,63 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * niserror.c
+ *
+ * Copyright (c) 1988-1992 Sun Microsystems Inc
+ * All Rights Reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * niserror.c
+ *
+ * This module prints the error message associated with an NIS+
+ * error code.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <rpcsvc/nis.h>
+
+usage()
+{
+ fprintf(stderr, "usage: niserror error-num\n");
+ exit(1);
+}
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ nis_error err;
+
+ if (argc != 2)
+ usage();
+
+ if (! isdigit(*argv[1]))
+ usage();
+
+ err = (nis_error) atoi(argv[1]);
+ printf("%s\n", nis_sperrno(err));
+ exit(0);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisgrep.c b/usr/src/cmd/rpcsvc/nis/utils/nisgrep.c
new file mode 100644
index 0000000000..b9d1dd0e41
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nisgrep.c
@@ -0,0 +1,376 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * nisgrep.c
+ *
+ * Copyright (c) 1988-1992 Sun Microsystems Inc
+ * All Rights Reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * nisgrep.c
+ *
+ * nis+ table grep utility
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/nis.h>
+#include <regex.h>
+
+extern int optind;
+extern char *optarg;
+
+extern char *nisname_index();
+
+
+#define BINARY_STR "*BINARY*"
+#define TABLE_COLS(tres) tres->objects.objects_val[0].TA_data.ta_cols
+
+#define EXIT_MATCH 0
+#define EXIT_NOMATCH 1
+#define EXIT_ERROR 2
+
+struct pl_data {
+ unsigned flags;
+ char ta_sep;
+ u_long nmatch;
+ regex_t *re_dfa;
+ int *dfa_set; /* dfa_set[i] set if re_dfa[i] is valid */
+};
+
+#define PL_BINARY 1
+#define PL_COUNT 2
+#define PL_OBJECT 4
+#define PL_NOCASE 8
+
+static
+char *
+strlower(s)
+ char *s;
+{
+ int i;
+ int len;
+ char *p;
+
+ len = strlen(s) + 1;
+ p = malloc(len);
+ if (p == NULL) {
+ fprintf(stderr, "No memory!\n");
+ exit(EXIT_ERROR);
+ }
+ /* this loop includes the terminating null */
+ for (i = 0; i < len; i++) {
+ if (isupper(s[i]))
+ p[i] = tolower(s[i]);
+ else
+ p[i] = s[i];
+ }
+
+ return (p);
+}
+
+
+int
+print_line(tab, ent, udata)
+ char *tab;
+ nis_object *ent;
+ void *udata;
+{
+ int len;
+ char *val;
+ int res;
+ register entry_col *ec = ent->EN_data.en_cols.en_cols_val;
+ register int ncol = ent->EN_data.en_cols.en_cols_len;
+ register struct pl_data *d = (struct pl_data *)udata;
+ register int i;
+
+ /*
+ * check for matches with all patterns
+ */
+ for (i = 0; i < ncol; i++)
+ if (d->dfa_set[i]) {
+ val = ec[i].ec_value.ec_value_val;
+ len = ec[i].ec_value.ec_value_len;
+ if (len == 0)
+ return (0);
+ if (val[len-1] != '\0')
+ return (0);
+
+ if (d->flags & PL_NOCASE) {
+ val = strlower(val);
+ res = regexec(&d->re_dfa[i], val, 0, 0, 0);
+ free((void *)val);
+ } else {
+ res = regexec(&d->re_dfa[i], val, 0, 0, 0);
+ }
+ switch (res) {
+ case REG_ENOSYS:
+ return (-1);
+ case REG_NOMATCH:
+ return (0);
+ }
+ }
+
+ d->nmatch++;
+ if (d->flags & PL_COUNT)
+ return (0);
+
+ if (d->flags & PL_OBJECT) {
+ nis_print_object(ent);
+ return (0);
+ }
+
+ for (i = 0; i < ncol; i++) {
+ if (i > 0)
+ printf("%c", d->ta_sep);
+ if (ec[i].ec_value.ec_value_len) {
+ if ((ec[i].ec_flags & EN_BINARY) &&
+ !(d->flags & PL_BINARY))
+ printf(BINARY_STR);
+ else
+ printf("%s", ec[i].ec_value.ec_value_val);
+ }
+ }
+ printf("\n");
+
+ return (0);
+}
+
+
+#define F_HEADER 1
+
+void
+usage()
+{
+ fprintf(stderr,
+ "usage: nisgrep [-AMchivo] [-s sep] keypat tablename\n");
+ fprintf(stderr,
+ " nisgrep [-AMchivo] [-s sep] colname=keypat ... tablename\n");
+ exit(EXIT_ERROR);
+}
+
+re_error(pattern, code, expr)
+ char *pattern;
+ int code;
+ regex_t *expr;
+{
+ char buf[80];
+
+ buf[0] = 0;
+ regerror(code, expr, buf, sizeof (buf));
+ fprintf(stderr,
+ "can't compile regular expression \"%s\": %s\n",
+ pattern, buf);
+}
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+ int st;
+ u_long allres = 0, master = 0;
+ unsigned flags = 0;
+ char *p;
+ int npat, ncol, i, j;
+ char **patstr;
+ char *name;
+ nis_result *tres, *eres;
+ char tname[NIS_MAXNAMELEN];
+ struct pl_data pld;
+ int re_flags = REG_EXTENDED|REG_NOSUB;
+
+ /*
+ * By default, don't print binary data to ttys.
+ */
+ pld.flags = (isatty(1))?0:PL_BINARY;
+
+ pld.ta_sep = '\0';
+ while ((c = getopt(argc, argv, "AMchivos:")) != -1) {
+ switch (c) {
+ case 'A':
+ allres = ALL_RESULTS;
+ break;
+ case 'M':
+ master = MASTER_ONLY;
+ break;
+ case 'c':
+ pld.flags |= PL_COUNT;
+ break;
+ case 'i':
+ pld.flags |= PL_NOCASE;
+ break;
+ case 'h':
+ flags |= F_HEADER;
+ break;
+ case 'v' :
+ pld.flags &= ~PL_BINARY;
+ break;
+ case 'o' :
+ pld.flags |= PL_OBJECT;
+ break;
+ case 's':
+ if (strlen(optarg) != 1) {
+ fprintf(stderr,
+ "separator must be a single character\n");
+ exit(1);
+ }
+ pld.ta_sep = *optarg;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if ((npat = argc - optind - 1) < 1)
+ usage();
+ if ((patstr = (char **)malloc(npat * sizeof (char *))) == 0) {
+ fprintf(stderr, "No memory!\n");
+ exit(EXIT_ERROR);
+ }
+ for (i = 0; i < npat; i++) {
+ if (pld.flags & PL_NOCASE)
+ patstr[i] = strlower(argv[optind++]);
+ else
+ patstr[i] = argv[optind++];
+ }
+ name = argv[optind++];
+
+ /*
+ * Get the table object using expand name magic.
+ */
+ tres = nis_lookup(name, master|FOLLOW_LINKS|EXPAND_NAME);
+ if (tres->status != NIS_SUCCESS) {
+ nis_perror(tres->status, name);
+ exit(EXIT_ERROR);
+ }
+
+ /*
+ * Construct the name for the table that we found.
+ */
+ sprintf(tname, "%s.", tres->objects.objects_val[0].zo_name);
+ if (*(tres->objects.objects_val[0].zo_domain) != '.')
+ strcat(tname, tres->objects.objects_val[0].zo_domain);
+
+ /*
+ * Make sure it's a table object.
+ */
+ if (tres->objects.objects_val[0].zo_data.zo_type != NIS_TABLE_OBJ) {
+ fprintf(stderr, "%s is not a table!\n", tname);
+ exit(EXIT_ERROR);
+ }
+
+ /*
+ * Compile the regular expressions.
+ */
+ ncol = TABLE_COLS(tres).ta_cols_len;
+
+ if ((pld.re_dfa = (regex_t *)malloc(ncol * sizeof (regex_t))) == 0) {
+ fprintf(stderr, "No memory!\n");
+ exit(EXIT_ERROR);
+ }
+ memset(pld.re_dfa, 0, ncol * sizeof (regex_t));
+
+ if ((pld.dfa_set = (int *)malloc(ncol * sizeof (int))) == 0) {
+ fprintf(stderr, "No memory!\n");
+ exit(EXIT_ERROR);
+ }
+ memset(pld.dfa_set, 0, ncol * sizeof (int));
+
+ /* XXX pat could contain '=' */
+ if ((npat == 1) && (nisname_index(patstr[0], '=') == 0)) {
+ if ((st = regcomp(&pld.re_dfa[0], patstr[0], re_flags)) != 0) {
+ re_error(patstr[0], st, &pld.re_dfa[0]);
+ exit(EXIT_ERROR);
+ }
+ pld.dfa_set[0] = 1;
+ } else {
+ for (i = 0; i < npat; i++) {
+ if ((p = nisname_index(patstr[i], '=')) == 0)
+ usage();
+ *(p++) = 0;
+ for (j = 0; j < ncol; j++)
+ if (TABLE_COLS(tres).ta_cols_val[j].tc_name &&
+ (strcmp(
+ TABLE_COLS(tres).ta_cols_val[j].tc_name,
+ patstr[i]) == 0))
+ break;
+ if (j == ncol) {
+ fprintf(stderr, "column not found: %s\n",
+ patstr[i]);
+ exit(EXIT_ERROR);
+ }
+ if ((st = regcomp(&pld.re_dfa[j], p, re_flags)) != 0) {
+ re_error(p, st, &pld.re_dfa[j]);
+ exit(EXIT_ERROR);
+ }
+ pld.dfa_set[j] = 1;
+ }
+ }
+
+ /*
+ * Use the table's separator character when printing entries.
+ * Unless one was specified w/ -s
+ */
+ if (pld.ta_sep == '\0') {
+ pld.ta_sep = tres->objects.objects_val[0].TA_data.ta_sep;
+ }
+
+ /*
+ * Print column names
+ */
+ if ((flags & F_HEADER) && !(pld.flags & (PL_COUNT|PL_OBJECT))) {
+ ncol = TABLE_COLS(tres).ta_cols_len;
+ c = pld.ta_sep;
+ printf("# ");
+ for (i = 0; i < ncol; i++) {
+ if (i > 0)
+ printf("%c", c);
+ printf("%s",
+ TABLE_COLS(tres).ta_cols_val[i].tc_name);
+ }
+ printf("\n");
+ }
+
+ /*
+ * Cat matching entries from the table using a callback function.
+ */
+ pld.nmatch = 0;
+ eres = nis_list(tname, allres|master, print_line, (void *)&(pld));
+ if (eres->status != NIS_CBRESULTS &&
+ eres->status != NIS_NOTFOUND) {
+ nis_perror(eres->status, "can't list table");
+ exit(EXIT_ERROR);
+ }
+ if (pld.flags & PL_COUNT)
+ printf("%d\n", pld.nmatch);
+
+ if (pld.nmatch)
+ exit(EXIT_MATCH);
+ else
+ exit(EXIT_NOMATCH);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisgrpadm.c b/usr/src/cmd/rpcsvc/nis/utils/nisgrpadm.c
new file mode 100644
index 0000000000..f64bfe3b95
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nisgrpadm.c
@@ -0,0 +1,442 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * nisgrpadm.c
+ *
+ * Copyright (c) 1988-1992 Sun Microsystems Inc
+ * All Rights Reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * nisgrpadm.c
+ *
+ * This program allows the system administrator to create and administer
+ * groups within the NIS+ namespace.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/nis.h>
+
+extern int optind, opterr;
+extern char *optarg;
+extern nis_object nis_default_obj;
+
+extern nis_name __nis_map_group_r();
+extern bool_t nis_verifycred();
+
+enum op_types {NONE, DELETE, CREATE, ADD, REMOVE, LIST, TEST};
+
+static u_long master = 0;
+
+bool_t
+nis_verifydomain(n)
+ nis_name n;
+{
+ nis_result *res;
+ int err;
+ char dname[NIS_MAXNAMELEN];
+
+ sprintf(dname, "cred.org_dir.%s", n);
+ res = nis_lookup(dname, master);
+ err = (res->status == NIS_SUCCESS);
+ nis_freeresult(res);
+ return (err);
+}
+
+
+
+/*
+ * temp_nis_verifygroup(group)
+ *
+ * Verify the existence of the named group. This is a duplicate of
+ * nis_verifygroup API call.
+ * This duplication was necessitated by the fact that nis_verifygroup does not
+ * accept a 'flags' argument to force it to go to the master server.
+ * Refer to bugid 1092089 and rfe 1102245.
+ *
+ */
+nis_error
+temp_nis_verifygroup(group)
+ nis_name group; /* NIS group name */
+{
+ nis_name grpname;
+ nis_result *res;
+ nis_error result;
+ char namebuf[NIS_MAXNAMELEN];
+
+ grpname = __nis_map_group_r(group, namebuf, sizeof (namebuf));
+ res = nis_lookup(grpname, master | FOLLOW_LINKS);
+ if ((res->status == NIS_SUCCESS) || (res->status == NIS_S_SUCCESS)) {
+ if (__type_of(res->objects.objects_val) == NIS_GROUP_OBJ)
+ result = NIS_SUCCESS;
+ else
+ result = NIS_BADOBJECT;
+ } else
+ result = res->status;
+ nis_freeresult(res);
+ return (result);
+}
+
+
+
+
+bool_t
+verify_principal(name)
+ nis_name name;
+{
+ u_long flags;
+
+ if (name[0] == '-')
+ name++;
+ if (name[0] == '@')
+ return (temp_nis_verifygroup(&name[1]) == NIS_SUCCESS);
+ if (name[0] == '*') {
+ if (name[1] == '.')
+ return (nis_verifydomain(&name[2]));
+ else
+ return (FALSE);
+ }
+ /*
+ * Only if a random NIS+ server does not know about this, do we
+ * force a connection to the Master server to avoid overloading it.
+ */
+ flags = (u_long)(USE_DGRAM | FOLLOW_LINKS | FOLLOW_PATH);
+ return (nis_verifycred(name, flags) ? TRUE :
+ nis_verifycred(name, (u_long)(flags | MASTER_ONLY)));
+}
+
+void
+usage()
+{
+ fprintf(stderr,
+ "usage: nisgrpadm -a | -r | -t [-s] group princpal ...\n");
+ fprintf(stderr,
+ " nisgrpadm -d | -l [-M] [-s] group\n");
+ fprintf(stderr,
+ " nisgrpadm -c [ -D defaults] [-M] [-s] group\n");
+ exit(1);
+}
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *defstr = 0;
+ enum op_types op = NONE;
+ int silent = 0;
+ int c;
+ nis_error s, pstatus = NIS_SUCCESS, ostatus = NIS_SUCCESS;
+ char *grpname = NULL, *princp = NULL;
+ nis_name *grplist, *plist = 0, *p;
+ char buf[NIS_MAXNAMELEN], *grponame;
+ nis_result *gres;
+ int grpnm, found, i;
+ nis_name *grpml;
+
+ while ((c = getopt(argc, argv, "artcdlsMD:")) != -1) {
+ switch (c) {
+ case 'D':
+ defstr = optarg;
+ break;
+ case 'M':
+ master = MASTER_ONLY;
+ break;
+ case 'd':
+ if (op != NONE) {
+ fprintf(stderr,
+ "c, d, a, r, t, and l options are mutually exclusive.\n");
+ usage();
+ }
+ op = DELETE;
+ break;
+ case 'c':
+ if (op != NONE) {
+ fprintf(stderr,
+ "c, d, a, r, t, and l options are mutually exclusive.\n");
+ usage();
+ }
+ op = CREATE;
+ break;
+ case 'a':
+ if (op != NONE) {
+ fprintf(stderr,
+ "c, d, a, r, t, and l options are mutually exclusive.\n");
+ usage();
+ }
+ op = ADD;
+ break;
+ case 'r':
+ if (op != NONE) {
+ fprintf(stderr,
+ "c, d, a, r, t, and l options are mutually exclusive.\n");
+ usage();
+ }
+ op = REMOVE;
+ break;
+ case 't':
+ if (op != NONE) {
+ fprintf(stderr,
+ "c, d, a, r, t, and l options are mutually exclusive.\n");
+ usage();
+ }
+ op = TEST;
+ break;
+ case 'l':
+ if (op != NONE) {
+ fprintf(stderr,
+ "c, d, a, r, t, and l options are mutually exclusive.\n");
+ usage();
+ }
+ op = LIST;
+ break;
+ case 's':
+ silent = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (op == NONE)
+ usage();
+
+ if (optind == argc) {
+ fprintf(stderr, "Missing group name.\n");
+ usage();
+ }
+ grpname = argv[optind++];
+
+ if (!nis_defaults_init(defstr))
+ exit(1);
+ if ((op == CREATE) || (op == DELETE)) {
+ if (grpname[strlen(grpname)-1] != '.') {
+ fprintf(stderr,
+ "Group name must be fully qualified.\n");
+ exit(NIS_BADNAME);
+ }
+ } else {
+ /*
+ * Get the group name using psuedo expand name magic
+ */
+ if ((grplist = nis_getnames(grpname)) == 0) {
+ nis_perror(NIS_NOMEMORY, "nisgrpadm");
+ exit(NIS_NOMEMORY);
+ }
+ for (p = grplist; *p; p++)
+ if (temp_nis_verifygroup(*p) == NIS_SUCCESS)
+ break;
+ if (*p == 0) {
+ if (!silent)
+ fprintf(stderr,
+ "Group \"%s\" not found.\n",
+ grpname);
+ exit(NIS_NOTFOUND);
+ }
+ grpname = *p;
+ }
+
+ switch (op) {
+ case DELETE:
+ s = nis_destroygroup(grpname);
+ if (!silent) {
+ if (s == NIS_SUCCESS)
+ printf("Group \"%s\" destroyed.\n",
+ grpname);
+ else {
+ fprintf(stderr,
+ "Unable to destroy group \"%s\": %s\n",
+ grpname,
+ nis_sperrno(s));
+ }
+ }
+ exit(s);
+
+ case CREATE:
+ s = __nis_creategroup_obj(grpname, 0, &nis_default_obj);
+ if (!silent) {
+ if (s == NIS_SUCCESS)
+ printf("Group \"%s\" created.\n",
+ grpname);
+ else {
+ fprintf(stderr,
+ "Unable to create group \"%s\": %s\n",
+ grpname,
+ nis_sperrno(s));
+ }
+ }
+ exit(s);
+
+ case LIST:
+ if (!silent)
+ nis_print_group_entry(grpname);
+ exit(NIS_SUCCESS);
+ }
+
+ if (optind == argc) {
+ fprintf(stderr, "Missing principal name(s).\n");
+ usage();
+ }
+
+ /*
+ * Get the group object so we can do prefix matching on partial
+ * principal names. temp_nis_verifygroup() has already read and
+ * verified the group (too bad we have to look it up again) so
+ * this shouldn't fail.
+ */
+ if (op == REMOVE) {
+ grponame = __nis_map_group_r(grpname, buf, sizeof (buf));
+ gres = nis_lookup(grponame, master | FOLLOW_LINKS);
+ if (gres->status != NIS_SUCCESS) {
+ if (!silent)
+ nis_perror(gres->status,
+ "Can't read group object");
+ exit(gres->status);
+ }
+ grpnm = NIS_RES_OBJECT(gres)->GR_data.gr_members.gr_members_len;
+ grpml = NIS_RES_OBJECT(gres)->GR_data.gr_members.gr_members_val;
+ }
+
+ while (optind < argc) {
+
+ princp = argv[optind++];
+
+ if (op == REMOVE) {
+ /*
+ * If the principal name isn't fully qualified,
+ * look for a unique prefix match in the group
+ * membership list.
+ */
+ if (princp[strlen(princp)-1] != '.') {
+ for (found = -1, i = 0; i < grpnm; i++) {
+ if (strncasecmp(princp,
+ grpml[i],
+ strlen(princp)) == 0) {
+ if (found >= 0 && !silent) {
+ fprintf(stderr,
+ "Principal \"%s\" not unique, please use full name.\n",
+ princp);
+ found = -2;
+ break;
+ } else
+ found = i;
+ }
+ }
+ if (found < 0) {
+ pstatus = NIS_NOTFOUND;
+ if (found == -1 && !silent)
+ fprintf(stderr,
+ "Principal \"%s\" not found in group.\n",
+ princp);
+ continue;
+ }
+ princp = grpml[found];
+ }
+ } else {
+ /*
+ * Get the principal name using psuedo expand name
+ * magic
+ */
+ if (plist)
+ nis_freenames(plist);
+ if ((plist = nis_getnames(princp)) == 0) {
+ nis_perror(NIS_NOMEMORY, "nisgrpadm");
+ exit(NIS_NOMEMORY);
+ }
+ for (p = plist; *p; p++)
+ if (verify_principal(*p))
+ break;
+ if (*p == 0) {
+ pstatus = NIS_NOTFOUND;
+ if (!silent)
+ fprintf(stderr,
+ "Principal \"%s\" not found.\n",
+ princp);
+ continue;
+ }
+ princp = *p;
+ }
+
+ switch (op) {
+ case ADD:
+ s = nis_addmember(princp, grpname);
+ if (ostatus == NIS_SUCCESS)
+ ostatus = s;
+ if (!silent) {
+ if (s == NIS_SUCCESS)
+ printf(
+ "Added \"%s\" to group \"%s\".\n",
+ princp, grpname);
+ else {
+ fprintf(stderr,
+ "Unable to add \"%s\" to group \"%s\": %s\n",
+ princp, grpname,
+ nis_sperrno(s));
+ }
+ }
+ break;
+
+ case REMOVE:
+ s = nis_removemember(princp, grpname);
+ if (ostatus == NIS_SUCCESS)
+ ostatus = s;
+ if (!silent) {
+ if (s == NIS_SUCCESS)
+ printf(
+ "Removed \"%s\" from group \"%s\".\n",
+ princp, grpname);
+ else {
+ fprintf(stderr,
+ "Unable to remove \"%s\" from group \"%s\": %s\n",
+ princp, grpname,
+ nis_sperrno(s));
+ }
+ }
+ break;
+
+ case TEST:
+ s = nis_ismember(princp, grpname);
+ if (!s && ostatus == NIS_SUCCESS)
+ ostatus = NIS_NOTFOUND;
+ if (!silent) {
+ if (s)
+ printf(
+ "\"%s\" is a member of group \"%s\".\n",
+ princp, grpname);
+ else
+ printf(
+ "\"%s\" is not a member of group \"%s\".\n",
+ princp, grpname);
+ }
+ break;
+ }
+ }
+
+ if (ostatus == NIS_SUCCESS)
+ exit(pstatus);
+ else
+ exit(ostatus);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisln.c b/usr/src/cmd/rpcsvc/nis/utils/nisln.c
new file mode 100644
index 0000000000..718a91af70
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nisln.c
@@ -0,0 +1,181 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * nisln.c
+ *
+ * Copyright 1988-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * nisln.c
+ *
+ * nis+ link utility
+ */
+
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/nis.h>
+#include <stdlib.h>
+#include <strings.h>
+
+extern nis_object nis_default_obj;
+
+extern char *nisname_index();
+extern int nisname_split(char *, char *, char *, int);
+
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: nisln [-D defaults] [-L] name linkname\n");
+ exit(1);
+}
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+ char *defstr = 0;
+ ulong_t flinks = 0;
+ char *name, *lname;
+ nis_result *res, *ares;
+ nis_object *obj, lobj;
+ char oname[NIS_MAXNAMELEN], fbase[NIS_MAXNAMELEN];
+ char srch[NIS_MAXNAMELEN], base[NIS_MAXNAMELEN];
+ nis_error s;
+ ib_request ibr;
+ int bad_name;
+ int i, j;
+
+ while ((c = getopt(argc, argv, "D:L")) != -1) {
+ switch (c) {
+ case 'D':
+ defstr = optarg;
+ break;
+ case 'L':
+ flinks = FOLLOW_LINKS;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (argc - optind != 2)
+ usage();
+
+ name = argv[optind];
+ lname = argv[optind+1];
+
+ /*
+ * Get the object to link to.
+ */
+ if (nisname_split(name, base, srch, sizeof (base)) || base[0] == 0) {
+ nis_perror(NIS_BADNAME, name);
+ exit(1);
+ }
+ res = nis_lookup(base, flinks|MASTER_ONLY|EXPAND_NAME);
+ if (res->status != NIS_SUCCESS) {
+ nis_perror(res->status, base);
+ exit(1);
+ }
+ obj = res->objects.objects_val;
+
+ bad_name = (snprintf(fbase, sizeof (fbase), "%s.", obj->zo_name)
+ >= sizeof (fbase));
+ if (!bad_name && *(obj->zo_domain) != '.')
+ bad_name = (strlcat(fbase, obj->zo_domain, sizeof (fbase)) >=
+ sizeof (fbase));
+
+ if (bad_name) {
+ nis_perror(NIS_BADNAME, fbase);
+ exit(1);
+ }
+
+ if (obj->zo_data.zo_type == NIS_DIRECTORY_OBJ) {
+ fprintf(stderr, "\"%s\" is a directory!\n", fbase);
+ exit(1);
+ }
+
+ if (srch[0]) {
+ if (obj->zo_data.zo_type != NIS_TABLE_OBJ) {
+ fprintf(stderr, "\"%s\" is not a table!\n", fbase);
+ exit(1);
+ }
+
+ bad_name = (snprintf(oname, sizeof (oname), "%s,%s",
+ srch, fbase) >= sizeof (oname));
+
+ if (bad_name) {
+ nis_perror(NIS_BADNAME, oname);
+ exit(1);
+ }
+
+ s = nis_get_request(oname, 0, 0, &ibr);
+ if (s != NIS_SUCCESS) {
+ nis_perror(s, oname);
+ exit(1);
+ }
+
+ for (i = 0; i < ibr.ibr_srch.ibr_srch_len; i++) {
+ for (j = 0; j < obj->TA_data.ta_cols.ta_cols_len; j++)
+ if
+ (strcmp(ibr.ibr_srch.ibr_srch_val[i].zattr_ndx,
+ obj->TA_data.ta_cols.ta_cols_val[j].tc_name) == 0)
+ break;
+ if (j == obj->TA_data.ta_cols.ta_cols_len) {
+ nis_perror(NIS_BADATTRIBUTE, name);
+ exit(1);
+ }
+ }
+ } else {
+ memset(&ibr, 0, sizeof (ibr));
+ ibr.ibr_name = fbase;
+ }
+
+ if (!nis_defaults_init(defstr))
+ exit(1);
+
+ /*
+ * Construct link object.
+ */
+ lobj = nis_default_obj;
+ lobj.zo_data.zo_type = NIS_LINK_OBJ;
+ if (srch[0])
+ lobj.LI_data.li_rtype = NIS_ENTRY_OBJ;
+ else
+ lobj.LI_data.li_rtype = obj->zo_data.zo_type;
+ lobj.LI_data.li_attrs.li_attrs_len = ibr.ibr_srch.ibr_srch_len;
+ lobj.LI_data.li_attrs.li_attrs_val = ibr.ibr_srch.ibr_srch_val;
+ lobj.LI_data.li_name = ibr.ibr_name;
+
+ ares = nis_add(lname, &lobj);
+ if (ares->status != NIS_SUCCESS) {
+ nis_perror(ares->status, "can't add link");
+ exit(1);
+ }
+
+ exit(0);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisls.c b/usr/src/cmd/rpcsvc/nis/utils/nisls.c
new file mode 100644
index 0000000000..870a840478
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nisls.c
@@ -0,0 +1,269 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * nisls.c
+ *
+ * Copyright 1988-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * nisls.c
+ *
+ * Simple "ls" utility for nis directories.
+ */
+
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/nis.h>
+#include <stdlib.h>
+#include <strings.h>
+
+extern nis_result * __nis_list_localcb(nis_name, uint_t,
+ int (*)(nis_name, nis_object *, void *), void *);
+
+
+static char **nl = 0;
+static int nlsize = 1024, nllen = 0;
+
+static void
+nlpush(name)
+ char *name;
+{
+ if (nl == 0)
+ nl = (char **)malloc(nlsize * sizeof (char *));
+ if (nllen == nlsize) {
+ nlsize += 1024;
+ nl = (char **)realloc(nl, nlsize * sizeof (char *));
+ }
+ nl[nllen++] = name;
+}
+
+static char *
+nlpop()
+{
+ if (nllen == 0)
+ return (0);
+ return (nl[--nllen]);
+}
+
+
+#define F_LONG 1
+#define F_RECURSE 2
+#define F_MTIME 4
+#define F_GROUP 8
+#define F_DIR 16
+
+static int flags = 0;
+
+static int
+ls_obj(tab, ent, udata)
+ char *tab;
+ nis_object *ent;
+ void *udata;
+{
+ zotypes et;
+ char fname[NIS_MAXNAMELEN];
+ char *tms;
+
+ if (udata)
+ et = *(long *)(udata);
+ else
+ et = ntohl(*(long *)(ent->
+ EN_data.en_cols.en_cols_val[0].ec_value.ec_value_val));
+
+ (void) snprintf(fname, sizeof (fname), "%s.", ent->zo_name);
+ if (*(ent->zo_domain) != '.')
+ (void) strlcat(fname, ent->zo_domain, sizeof (fname));
+
+ if ((flags & F_RECURSE) && (et == NIS_DIRECTORY_OBJ) &&
+ !(flags & F_DIR))
+ nlpush(strdup(fname));
+
+ if (flags & F_LONG) {
+ switch (et) {
+ case NIS_BOGUS_OBJ:
+ printf("* ");
+ break;
+ case NIS_DIRECTORY_OBJ:
+ printf("D ");
+ break;
+ case NIS_GROUP_OBJ:
+ printf("G ");
+ break;
+ case NIS_TABLE_OBJ:
+ printf("T ");
+ break;
+ case NIS_ENTRY_OBJ:
+ printf("E ");
+ break;
+ case NIS_LINK_OBJ:
+ printf("L ");
+ break;
+ case NIS_PRIVATE_OBJ:
+ printf("P ");
+ break;
+ default:
+ printf("%d ", et);
+ break;
+ }
+ nis_print_rights(ent->zo_access);
+ if (flags & F_GROUP)
+ printf(" %s ", ent->zo_group);
+ else
+ printf(" %s ", ent->zo_owner);
+ if (flags & F_MTIME)
+ tms = ctime((time_t *)&(ent->zo_oid.mtime));
+ else
+ tms = ctime((time_t *)&(ent->zo_oid.ctime));
+ *(tms+strlen(tms)-1) = '\0';
+ printf("%s ", tms);
+ }
+
+ printf("%s\n", (tab)?ent->zo_name:fname);
+
+ return (0);
+}
+
+
+static void
+usage()
+{
+ printf("usage: nisls [-LMlmgdR] [name ...]\n");
+ exit(1);
+}
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+ ulong_t flinks = 0, master = 0;
+ nis_name *result;
+ char *name;
+ nis_result *res, *lres;
+ char fname[NIS_MAXNAMELEN];
+ int error = 0;
+
+ while ((c = getopt(argc, argv, "LMlmgdR")) != -1)
+ switch (c) {
+ case 'L':
+ flinks = FOLLOW_LINKS;
+ break;
+ case 'M':
+ master = MASTER_ONLY;
+ break;
+ case 'l':
+ flags |= F_LONG;
+ break;
+ case 'm':
+ flags |= F_MTIME;
+ break;
+ case 'g':
+ flags |= F_GROUP;
+ break;
+ case 'd':
+ flags |= F_DIR;
+ break;
+ case 'R':
+ flags |= F_RECURSE;
+ break;
+ default:
+ usage();
+ }
+
+ /*
+ * push all of the names to ls onto a stack (in reverse order)
+ */
+ if (argc - optind == 0) {
+ result = nis_getnames("foo");
+ nlpush(nis_domain_of(*result));
+ } else while (argc - optind > 0)
+ nlpush(argv[--argc]);
+
+ /*
+ * keep popping names off the stack and ls'ing them. if we're
+ * recursive, then subdirectories will get pushed on in ls_obj.
+ */
+ while (name = nlpop()) {
+ res = nis_lookup(name, flinks|master|EXPAND_NAME);
+ /*
+ * Hack to get around non replicated root object bug.
+ */
+ if (res->status == NIS_NOT_ME) {
+ printf("%s:\n", name);
+
+ lres = __nis_list_localcb(name, EXPAND_NAME|master,
+ ls_obj, 0);
+ if ((lres->status != NIS_CBRESULTS) &&
+ (lres->status != NIS_NOTFOUND)) {
+ nis_perror(lres->status,
+ "can't list directory");
+ error = 1;
+ }
+ nis_freeresult(lres);
+
+ if (nllen)
+ printf("\n");
+ goto loop;
+
+ } else if (res->status != NIS_SUCCESS) {
+ nis_perror(res->status, name);
+ error = 1;
+ goto loop;
+ }
+
+ if ((res->objects.objects_val[0].zo_data.zo_type !=
+ NIS_DIRECTORY_OBJ) || (flags & F_DIR)) {
+ ls_obj(0, res->objects.objects_val,
+ &(res->objects.objects_val[0].zo_data.zo_type));
+ goto loop;
+ }
+
+ (void) snprintf(fname, sizeof (fname), "%s.",
+ res->objects.objects_val[0].zo_name);
+ if (*(res->objects.objects_val[0].zo_domain) != '.')
+ (void) strlcat(fname,
+ res->objects.objects_val[0].zo_domain,
+ sizeof (fname));
+
+ printf("%s:\n", fname);
+
+ lres = __nis_list_localcb(fname, master, ls_obj, 0);
+ if ((lres->status != NIS_CBRESULTS) &&
+ (lres->status != NIS_NOTFOUND)) {
+ nis_perror(lres->status, "can't list directory");
+ error = 1;
+ }
+ nis_freeresult(lres);
+
+ if (nllen)
+ printf("\n");
+
+ loop:
+ nis_freeresult(res);
+ }
+
+ exit(error);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nismatch.c b/usr/src/cmd/rpcsvc/nis/utils/nismatch.c
new file mode 100644
index 0000000000..56a0327184
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nismatch.c
@@ -0,0 +1,334 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * nismatch.c
+ *
+ * Copyright 1988-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * nismatch.c
+ *
+ * nis+ table match utility
+ */
+
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/nis.h>
+#include <strings.h>
+#include <stdlib.h>
+
+extern char *nisname_index();
+extern nis_result * __nis_list_localcb(nis_name, uint_t,
+ int (*)(nis_name, nis_object *, void *), void *);
+extern int nisname_split(char *, char *, char *, int);
+
+
+#define BINARY_STR "*BINARY*"
+
+
+struct pl_data {
+ unsigned flags;
+ char ta_sep;
+ ulong_t nmatch;
+};
+
+#define PL_BINARY 1
+#define PL_COUNT 2
+#define PL_OBJECT 4
+
+int
+print_line(tab, ent, udata)
+ char *tab;
+ nis_object *ent;
+ void *udata;
+{
+ register entry_col *ec = ent->EN_data.en_cols.en_cols_val;
+ register int ncol = ent->EN_data.en_cols.en_cols_len;
+ register struct pl_data *d = (struct pl_data *)udata;
+ register int i;
+ int len;
+
+ d->nmatch++;
+ if (d->flags & PL_COUNT)
+ return (0);
+
+ if (d->flags & PL_OBJECT) {
+ nis_print_object(ent);
+ return (0);
+ }
+
+ for (i = 0; i < ncol; i++) {
+ if (i > 0)
+ printf("%c", d->ta_sep);
+ len = ec[i].ec_value.ec_value_len;
+ if (len != 0) {
+ if (ec[i].ec_flags & EN_BINARY) {
+ if (d->flags & PL_BINARY) {
+ fwrite(ec[i].ec_value.ec_value_val,
+ 1, len, stdout);
+ } else {
+ printf(BINARY_STR);
+ }
+ } else {
+ printf("%s", ec[i].ec_value.ec_value_val);
+ }
+ }
+ }
+ printf("\n");
+
+ return (0);
+}
+
+
+#define EXIT_MATCH 0
+#define EXIT_NOMATCH 1
+#define EXIT_ERROR 2
+
+#define F_HEADER 1
+
+void
+usage()
+{
+ fprintf(stderr,
+ "usage: nismatch [-PAMchvo] [-s sep] key tablename\n");
+ fprintf(stderr,
+ " nismatch [-PAMchvo] [-s sep] colname=key ... tablename\n");
+ fprintf(stderr,
+ " nismatch [-PAMchvo] [-s sep] indexedname\n");
+ exit(EXIT_ERROR);
+}
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+ ulong_t fpath = 0, allres = 0, master = 0;
+ unsigned flags = 0;
+ char *p, *name;
+ nis_result *tres, *eres;
+ nis_object *tobj;
+ char index[NIS_MAXNAMELEN],
+ spred[NIS_MAXNAMELEN],
+ tname[NIS_MAXNAMELEN],
+ sname[NIS_MAXNAMELEN];
+ struct pl_data pld;
+ int ncol, i;
+ table_col *tcol;
+
+ /*
+ * By default, don't print binary data to ttys.
+ */
+ pld.flags = (isatty(1))?0:PL_BINARY;
+
+ pld.ta_sep = '\0';
+ while ((c = getopt(argc, argv, "PAMchvos:")) != -1) {
+ switch (c) {
+ case 'P':
+ fpath = FOLLOW_PATH;
+ break;
+ case 'A':
+ allres = ALL_RESULTS;
+ break;
+ case 'M':
+ master = MASTER_ONLY;
+ break;
+ case 'c':
+ pld.flags |= PL_COUNT;
+ break;
+ case 'h':
+ flags |= F_HEADER;
+ break;
+ case 'v' :
+ pld.flags |= PL_BINARY;
+ break;
+ case 'o' :
+ pld.flags |= PL_OBJECT;
+ break;
+ case 's':
+ if (strlen(optarg) != 1) {
+ fprintf(stderr,
+ "separator must be a single character\n");
+ exit(1);
+ }
+ pld.ta_sep = *optarg;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (argc - optind < 1)
+ usage();
+
+ /*
+ * build up the search criteria in sname, if a token is
+ * just "hanging" it is assumed to be a value for the
+ * first column. In that case keep it in spred until we
+ * can find the column name from the lookup.
+ */
+ sname[0] = 0;
+ spred[0] = 0;
+ while (optind < (argc - 1)) {
+ if (sname[0] == 0)
+ strcat(sname, "[");
+ p = argv[optind++];
+ if (strchr(p, '=')) {
+ if ((strlcat(sname, p, sizeof (sname)) >=
+ sizeof (sname)) ||
+ (strlcat(sname, ",", sizeof (sname)) >=
+ sizeof (sname))) {
+ (void) fprintf(stderr,
+ "search pattern too big\n");
+ exit(EXIT_ERROR);
+ }
+ } else {
+ /* if no "=" sign assume it it implicit */
+ if (spred[0]) {
+ fprintf(stderr,
+ "only one implicit column name allowed.\n");
+ usage(argv[0]);
+ }
+ if (strlcat(spred, p, sizeof (spred)) >=
+ sizeof (spred)) {
+ nis_perror(NIS_BADNAME, p);
+ exit(EXIT_ERROR);
+ }
+ }
+ }
+
+ /* This should be the last argument in the list */
+ name = argv[optind];
+
+ /* Split the name into the indexed part and the table part */
+ if (nisname_split(name, tname, index, sizeof (tname))) {
+ nis_perror(NIS_BADNAME, name);
+ exit(EXIT_ERROR);
+ }
+ if (index[0] && sname[0]) {
+ fprintf(stderr,
+ "use either an indexed name or the col=key form.\n");
+ usage();
+ }
+
+ /*
+ * Get the table object using expand name magic.
+ */
+ tres = nis_lookup(tname, master|FOLLOW_LINKS|EXPAND_NAME);
+ if (tres->status != NIS_SUCCESS) {
+ nis_perror(tres->status, tname);
+ exit(EXIT_ERROR);
+ }
+
+ /*
+ * Make sure it's a table object.
+ */
+ tobj = tres->objects.objects_val;
+ (void) snprintf(tname, sizeof (tname), "%s.%s", tobj->zo_name,
+ tobj->zo_domain);
+ if (__type_of(tobj) != NIS_TABLE_OBJ) {
+ fprintf(stderr, "\"%s\" is not a table!\n", tname);
+ exit(EXIT_ERROR);
+ }
+ ncol = tobj->TA_data.ta_cols.ta_cols_len;
+ tcol = tobj->TA_data.ta_cols.ta_cols_val;
+
+ /*
+ * Finish the construction of the search criteria if necessary.
+ * At this point we've got three candidates for the search
+ * criteria :
+ * index - contains indexed name portion on table
+ * spred - contains "hanging" value, implicitly for the
+ * first column.
+ * sname - name=value pairs that were specified on the
+ * command line.
+ * tname - the name of our table.
+ * NB: If sname || spred have something in them then index is available
+ * otherwise we use what is in index.
+ */
+ if (spred[0]) {
+ for (i = 0; i < ncol; i++)
+ if (tcol[i].tc_flags & TA_SEARCHABLE)
+ break;
+ if (i == ncol) {
+ fprintf(stderr, "%s: No searchable columns!\n", tname);
+ exit(1);
+ }
+ (void) snprintf(index, sizeof (index), "%s=\"%s\"]",
+ tcol[i].tc_name, spred);
+ (void) strlcat(sname, index, sizeof (sname));
+ (void) strlcpy(index, sname, sizeof (index));
+ } else if (sname[0]) {
+ sname[strlen(sname) - 1] = 0;
+ (void) snprintf(index, sizeof (index), "%s]", sname);
+ }
+ /*
+ * Construct the search criteria for the table that we found.
+ */
+ (void) strlcat(index, ",", sizeof (index));
+ (void) strlcat(index, tname, sizeof (index));
+
+ /*
+ * Use the table's separator character when printing entries.
+ * Unless one was specified w/ -s
+ */
+ if (pld.ta_sep == '\0') {
+ pld.ta_sep = tobj->TA_data.ta_sep;
+ }
+
+ /*
+ * Print column names
+ */
+ if ((flags & F_HEADER) && !(pld.flags & (PL_COUNT|PL_OBJECT))) {
+ c = pld.ta_sep;
+ printf("# ");
+ for (i = 0; i < ncol; i++) {
+ if (i > 0)
+ printf("%c", c);
+ printf("%s", tcol[i].tc_name);
+ }
+ printf("\n");
+ }
+
+ /*
+ * Cat matching entries from the table using a callback function.
+ */
+ pld.nmatch = 0;
+ eres = __nis_list_localcb(index, fpath|allres|master,
+ print_line, (void *)&(pld));
+ if ((eres->status != NIS_CBRESULTS) &&
+ (eres->status != NIS_NOTFOUND)) {
+ nis_perror(eres->status, "can't list table");
+ exit(EXIT_ERROR);
+ }
+ if (pld.flags & PL_COUNT)
+ printf("%d\n", pld.nmatch);
+
+ if (pld.nmatch)
+ exit(EXIT_MATCH);
+ else
+ exit(EXIT_NOMATCH);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nismkdir.c b/usr/src/cmd/rpcsvc/nis/utils/nismkdir.c
new file mode 100644
index 0000000000..f7dcb5e8b7
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nismkdir.c
@@ -0,0 +1,508 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 1988-1995,2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * nismkdir.c
+ *
+ * NIS+ directory and server create utility
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/nis.h>
+#include <netdb.h>
+#include <netdir.h>
+#include <netconfig.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <rpc/key_prot.h>
+#include <rpcsvc/nis_dhext.h>
+
+#define MAX_REPLICA 64
+char *defstr = 0;
+int errcode = 0;
+
+extern int optind;
+extern char *optarg;
+
+extern nis_object nis_default_obj;
+
+void
+make_directory(char *name)
+{
+ nis_result *res, *ares;
+ nis_object *obj;
+ char *p, lname[NIS_MAXNAMELEN], *dname;
+ nis_error s;
+
+ /*
+ * Break name into leaf and domain components.
+ */
+ if ((p = nis_leaf_of(name)) == 0) {
+ nis_perror(NIS_BADNAME, name);
+ exit(1);
+ }
+ strcpy(lname, p);
+ dname = nis_domain_of(name);
+
+ /*
+ * Get the parent directory object.
+ */
+ res = nis_lookup(dname, MASTER_ONLY);
+ if (res->status != NIS_SUCCESS) {
+ nis_perror(res->status, dname);
+ exit(1);
+ }
+
+ if (!nis_defaults_init(defstr))
+ exit(1);
+
+ /*
+ * Turn the parent directory object into the
+ * sub-directory object. If we cared about memory
+ * leaks, we would save pointers to the fields that
+ * are being overwritten, and restore them and free
+ * the parent object when we are done.
+ */
+ obj = &(NIS_RES_OBJECT(res)[0]);
+ if (obj->zo_data.zo_type != NIS_DIRECTORY_OBJ) {
+ fprintf(stderr, "\"%s\": not a directory\n", dname);
+ exit(1);
+ }
+ obj->zo_owner = nis_default_obj.zo_owner;
+ obj->zo_group = nis_default_obj.zo_group;
+ obj->zo_access = nis_default_obj.zo_access;
+ obj->zo_ttl = nis_default_obj.zo_ttl;
+ obj->DI_data.do_name = name;
+
+ /*
+ * Make the directory and add it to the namespace.
+ */
+ ares = nis_add(name, obj);
+ if (ares->status != NIS_SUCCESS) {
+ nis_perror(ares->status, "can't add directory");
+ exit(1);
+ } else {
+ s = nis_mkdir(name,
+ &(obj->DI_data.do_servers.do_servers_val[0]));
+ if (s != NIS_SUCCESS) {
+ (void) nis_remove(name, 0);
+ nis_perror(s, "can't make directory");
+ exit(1);
+ }
+ }
+ nis_freeresult(ares);
+ /*
+ * do not free res because it contains pointers to structures
+ * it did not allocate.
+ */
+}
+
+
+static int
+verify_server(nis_name host, /* new server hostname */
+ nis_name dir_to_serve, /* directory to serve */
+ int to_be_master) /* will it be a master? */
+{
+
+ nis_error status; /* status of nis_stats() */
+ nis_name dom_of_host; /* domain name of server host */
+ nis_object *obj; /* generic NIS+ object variable */
+ nis_result *hres, *sres; /* status of nis_lookup() */
+ nis_server *servers; /* servers for host's domain */
+ nis_tag tags, *tagres; /* NIS+ tags and tags result */
+
+ /* Try to lookup directory to serve... */
+ sres = nis_lookup(dir_to_serve, 0);
+
+ /* If it doesn't exist, we can't have a (replica) server for it! */
+ if ((sres->status != NIS_SUCCESS) && !to_be_master) {
+ fprintf(stderr, "\"%s\": %s\n",
+ dir_to_serve, nis_sperrno(sres->status));
+ return (0);
+ }
+
+ /* It exists, but is it a directory object? */
+ if (sres->status == NIS_SUCCESS) {
+ obj = sres->objects.objects_val;
+ if (__type_of(obj) != NIS_DIRECTORY_OBJ) {
+ fprintf(stderr, "\"%s\": not a directory.\n",
+ dir_to_serve);
+ return (0);
+ }
+ }
+
+ nis_freeresult(sres);
+
+ /* Try to lookup domain of host who wants to be a server... */
+ dom_of_host = strdup(nis_domain_of(host));
+ hres = nis_lookup(dom_of_host, 0);
+
+ /* Can't get host domain, servers busy or domain name bogus... */
+ if (hres->status != NIS_SUCCESS) {
+ fprintf(stderr, "\"%s\": %s\n", dom_of_host,
+ nis_sperrno(hres->status));
+ return (0);
+ }
+
+ /* It exists, but is it a directory object? */
+ obj = hres->objects.objects_val;
+ if (__type_of(obj) != NIS_DIRECTORY_OBJ) {
+ fprintf(stderr, "\"%s\": not a directory.\n", dom_of_host);
+ return (0);
+ }
+
+ servers = obj->DI_data.do_servers.do_servers_val;
+
+ switch ((int)(nis_dir_cmp(dir_to_serve, dom_of_host))) {
+
+ case SAME_NAME:
+
+ tags.tag_type = TAG_ROOTSERVER;
+ tags.tag_val = "root server";
+
+ if (!servers[0].name) {
+ fprintf(stderr, "nismkdir: invalid directory object\n");
+ return (0);
+ }
+ status = nis_stats(&servers[0], &tags, 1, &tagres);
+ if (status != NIS_SUCCESS) {
+ fprintf(stderr,
+ "nismkdir: can't contact root server for \"%s\""
+ ": %s\n", servers[0].name, dom_of_host,
+ nis_sperrno(status));
+ return (0);
+ }
+ nis_freeresult(hres);
+
+ /* If the server-to-be is in the root domain, it can serve. */
+ if (strcmp(tagres[0].tag_val, "ON") == 0) {
+ return (1);
+ } else {
+ fprintf(stderr,
+ "nismkdir: host '%s' must be in a domain above '%s'\n",
+ host, dir_to_serve);
+ return (0);
+ }
+ case LOWER_NAME:
+ /* directory is below domain of host in question... okay */
+ return (1);
+
+ case HIGHER_NAME:
+ case NOT_SEQUENTIAL:
+ fprintf(stderr,
+ "nismkdir: host '%s' must be in a domain above '%s'\n",
+ host, dir_to_serve);
+ return (0);
+
+ case BAD_NAME:
+ default:
+ fprintf(stderr,
+ "nismkdir: cannot parse either or both of '%s' and '%s'\n",
+ dir_to_serve, dom_of_host);
+ return (0);
+ }
+}
+
+
+void
+make_directory_master(char *name, char *host)
+{
+ nis_result *res, *ares, *mres;
+ nis_object *obj, dobj;
+ nis_error s;
+ nis_server *serv, sserv, *newserv;
+ int nserv, i;
+
+ /*
+ * Does the directory already exist?
+ */
+ res = nis_lookup(name, MASTER_ONLY);
+ if (res->status == NIS_SUCCESS) {
+ obj = &(NIS_RES_OBJECT(res)[0]);
+ if (obj->zo_data.zo_type != NIS_DIRECTORY_OBJ) {
+ fprintf(stderr, "\"%s\": not a directory\n", name);
+ exit(1);
+ }
+ nserv = obj->DI_data.do_servers.do_servers_len;
+
+ newserv = __nis_host2nis_server_g(host, TRUE, TRUE, &errcode);
+ if (newserv == NULL) {
+ nis_perror(errcode, host);
+ exit(1);
+ }
+
+ if (nis_dir_cmp(obj->DI_data.do_servers.do_servers_val[0].name,
+ newserv->name) == SAME_NAME) {
+ fprintf(stderr,
+ "\"%s\" is already master for \"%s\"!\n",
+ host, name);
+ exit(1);
+ }
+
+ /*
+ * Verify server able to serve intended domain.
+ */
+ if (!verify_server(newserv->name, name, 1))
+ exit(1);
+
+ /*
+ * Add master to list of servers and demote current
+ * master to the role of a slave.
+ */
+ for (i = 1; i < nserv; i++)
+ if (nis_dir_cmp(
+ obj->DI_data.do_servers.do_servers_val[i].name,
+ newserv->name) == SAME_NAME)
+ break;
+ if (i < nserv) {
+ sserv =
+ obj->DI_data.do_servers.do_servers_val[i];
+ obj->DI_data.do_servers.do_servers_val[i] =
+ obj->DI_data.do_servers.do_servers_val[0];
+ obj->DI_data.do_servers.do_servers_val[0] =
+ sserv;
+ } else {
+ if ((serv = (nis_server*)malloc(
+ (nserv + 1)*sizeof (nis_server))) ==
+ 0) {
+ nis_perror(NIS_NOMEMORY,
+ "can't add master");
+ exit(1);
+ }
+
+ for (i = 0; i < nserv; i++)
+ serv[i+1] =
+ obj->DI_data.do_servers.do_servers_val[i];
+ serv[0] = *newserv;
+ obj->DI_data.do_servers.do_servers_len++;
+ obj->DI_data.do_servers.do_servers_val = serv;
+ }
+
+ mres = nis_modify(name, obj);
+ if (mres->status != NIS_SUCCESS) {
+ nis_perror(mres->status, "can't add master");
+ exit(1);
+ }
+ nis_freeresult(mres);
+ /*
+ * do not free res because it contains pointers to
+ * structures it did not allocate.
+ */
+ } else {
+ if (!nis_defaults_init(defstr))
+ exit(1);
+
+ /*
+ * Construct the directory object.
+ */
+ dobj = nis_default_obj;
+ dobj.zo_data.zo_type = NIS_DIRECTORY_OBJ;
+ dobj.DI_data.do_name = name;
+ dobj.DI_data.do_type = NIS;
+ dobj.DI_data.do_ttl = nis_default_obj.zo_ttl;
+ dobj.DI_data.do_servers.do_servers_len = 1;
+ dobj.DI_data.do_servers.do_servers_val =
+ __nis_host2nis_server_g(host, TRUE, TRUE, &errcode);
+ if (dobj.DI_data.do_servers.do_servers_val == NULL) {
+ nis_perror(errcode, host);
+ exit(1);
+ }
+ dobj.DI_data.do_armask.do_armask_len = 0;
+ dobj.DI_data.do_armask.do_armask_val = 0;
+
+ /*
+ * Make the directory and add it to the namespace.
+ */
+ ares = nis_add(name, &dobj);
+ if (ares->status != NIS_SUCCESS) {
+ nis_perror(ares->status, "can't add directory");
+ exit(1);
+ } else {
+ s = nis_mkdir(name,
+ &(dobj.DI_data.do_servers.do_servers_val[0]));
+ if (s != NIS_SUCCESS) {
+ (void) nis_remove(name, 0);
+ nis_perror(s, "can't make directory");
+ exit(1);
+ }
+ }
+ nis_freeresult(ares);
+ }
+}
+
+
+void
+make_directory_replica(char *name, char *host)
+{
+ nis_result *res, *mres;
+ nis_object *obj;
+ nis_server *serv, *newserv;
+ int nserv, i;
+
+ /*
+ * Get the directory object.
+ */
+ res = nis_lookup(name, MASTER_ONLY);
+ if (res->status != NIS_SUCCESS) {
+ nis_perror(res->status, name);
+ exit(1);
+ }
+ obj = &(NIS_RES_OBJECT(res)[0]);
+ if (obj->zo_data.zo_type != NIS_DIRECTORY_OBJ) {
+ fprintf(stderr, "\"%s\": not a directory\n", name);
+ exit(1);
+ }
+ nserv = obj->DI_data.do_servers.do_servers_len;
+
+ newserv = __nis_host2nis_server_g(host, TRUE, TRUE, &errcode);
+ if (newserv == NULL) {
+ nis_perror(errcode, host);
+ exit(1);
+ }
+
+ for (i = 0; i < nserv; i++)
+ if (nis_dir_cmp(
+ obj->DI_data.do_servers.do_servers_val[i].name,
+ newserv->name) == SAME_NAME)
+ break;
+ if (i < nserv) {
+ fprintf(stderr, "\"%s\" already serves \"%s\"!\n",
+ host, name);
+ exit(1);
+ }
+
+ /*
+ * Verify server able to serve intended domain
+ */
+ if (!verify_server(newserv->name, name, 0))
+ exit(1);
+
+ /*
+ * Add slave to the list of servers.
+ */
+ if ((serv = (nis_server*)malloc(
+ (nserv + 1)*sizeof (nis_server))) == 0) {
+ nis_perror(NIS_NOMEMORY, "can't add replica");
+ exit(1);
+ }
+ for (i = 0; i < nserv; i++)
+ serv[i] = obj->DI_data.do_servers.do_servers_val[i];
+ serv[i] = *newserv;
+ obj->DI_data.do_servers.do_servers_len++;
+ obj->DI_data.do_servers.do_servers_val = serv;
+
+ mres = nis_modify(name, obj);
+ if (mres->status != NIS_SUCCESS) {
+ nis_perror(mres->status, "can't add replica");
+ exit(1);
+ }
+ nis_freeresult(mres);
+ /*
+ * do not free res because it contains pointers to structures
+ * it did not allocate.
+ */
+}
+
+
+void
+usage()
+{
+ fprintf(stderr,
+ "usage: nismkdir [-D defaults] [-m hostname] [-s hostname] dirname\n");
+ exit(1);
+}
+
+
+void
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+ int i;
+ char *host;
+ char *name;
+ int update_master = 0;
+ char *replicas[MAX_REPLICA];
+ int nreplicas = 0;
+
+ while ((c = getopt(argc, argv, "D:m:s:")) != -1) {
+ switch (c) {
+ case 'D':
+ defstr = optarg;
+ break;
+ case 'm':
+ if (update_master) {
+ fprintf(stderr,
+ "only one master can be specified\n");
+ exit(1);
+ }
+ update_master = 1;
+ host = optarg;
+ break;
+ case 's':
+ if (nreplicas >= MAX_REPLICA) {
+ fprintf(stderr, "too many replicas\n");
+ exit(1);
+ }
+ replicas[nreplicas++] = optarg;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (argc - optind != 1)
+ usage();
+
+ name = argv[optind];
+ if (name[strlen(name)-1] != '.') {
+ fprintf(stderr, "dirname must be fully qualified.\n");
+ exit(1);
+ }
+
+ /*
+ * If no master or replica flag, just create directory.
+ * The directory will have the same replication as its parent
+ * directory.
+ */
+ if (! update_master && nreplicas == 0)
+ make_directory(name);
+ else {
+ if (update_master)
+ make_directory_master(name, host);
+
+ for (i = 0; i < nreplicas; i++)
+ make_directory_replica(name, replicas[i]);
+ }
+
+ exit(0);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nispath.c b/usr/src/cmd/rpcsvc/nis/utils/nispath.c
new file mode 100644
index 0000000000..08617bb938
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nispath.c
@@ -0,0 +1,90 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * nispath.c
+ *
+ * Copyright (c) 1988-1992 Sun Microsystems Inc
+ * All Rights Reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * nispath.c
+ *
+ * This little utility will print out the search path for a given
+ * NIS+ name.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <rpcsvc/nis.h>
+
+void
+usage(cmd)
+ char *cmd;
+{
+ fprintf(stderr, "usage : %s [-v] name\n", cmd);
+ exit(1);
+}
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ nis_name *result;
+ int i = 0;
+ char *name;
+ int verbose = 0;
+
+ if ((argc == 1) || (argc > 3))
+ usage(argv[0]);
+
+ if (argc == 3) {
+ if (strcmp(argv[1], "-v") == 0)
+ verbose = 1;
+ else
+ usage(argv[0]);
+ name = argv[2];
+ } else {
+ if (strcmp(argv[1], "-v") == 0)
+ usage(argv[0]);
+ name = argv[1];
+ }
+
+ result = nis_getnames(name);
+ if (verbose) {
+ printf("For NIS+ Name : \"%s\"\n", name);
+ printf("Search Path :\n");
+ }
+ if (! result) {
+ if (verbose)
+ printf("\t**NONE**\n");
+ exit(1);
+ } else
+ while (result[i]) {
+ if (verbose)
+ printf("\t");
+ printf("\"%s\"\n", result[i++]);
+ }
+ exit(0);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisprefadm.c b/usr/src/cmd/rpcsvc/nis/utils/nisprefadm.c
new file mode 100644
index 0000000000..fa7044a795
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nisprefadm.c
@@ -0,0 +1,1816 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 1996-1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <rpcsvc/nis.h>
+
+#define LOCAL_PREF "/var/nis/client_info"
+#define LOCAL_TMP "/var/nis/client_info.tmp"
+#define TABLE "client_info"
+#define TABLE_TYPE "client_info_tbl"
+#define NCOLS 4
+
+#define PREF_SRVR "pref_srvr"
+#define PREF_TYPE "pref_type"
+
+#define OP_TEST 1
+#define OP_FLUSH 2
+#define OP_GLOBAL 3
+#define OP_LOCAL 4
+#define OP_ADD 5
+#define OP_MODIFY 6
+#define OP_UPDATE 7
+#define OP_REMOVE 8
+#define OP_DELETE 9
+#define OP_LIST 10
+
+struct server_list {
+ char *client;
+ char *options;
+ char **list;
+ char **interface;
+ int *weight;
+ int count;
+ int alloc;
+ int ntoken;
+ char *tokens[20];
+ char *token_buff;
+};
+typedef struct server_list server_list;
+int debug = 0;
+int verbose = 0;
+int entry_found = 0;
+static int old_format = 0;
+
+void get_local_servers(server_list *, char *, int);
+void parse_preference(server_list *, char *, char *, char *);
+int parse_info(server_list *, char *, char **, char **);
+void parse_server(char *, char **, char **, int *);
+void server_list_init(server_list *);
+void server_list_clear(server_list *);
+void server_list_add(server_list *, char *);
+int server_list_remove(server_list *, char *);
+void server_list_print(server_list *, FILE *);
+void get_servers(server_list *, char *, char *, int);
+void remove_local_servers(server_list *);
+void remove_servers(server_list *, char *);
+void update_local_servers(server_list *);
+void update_servers(server_list *, char *);
+void create_table(char *, char *);
+void convert_old2new(server_list *, char *);
+int print_tokens(FILE *, server_list *, char *);
+void print_local_servers(server_list *, char *);
+void print_servers(server_list *, char *, char *);
+int flush_cache(char *);
+
+extern nis_object nis_default_obj;
+
+extern ulong_t __inet_get_addr(void *, int);
+extern void *__inet_get_local_interfaces();
+extern void __inet_free_local_interfaces(void *);
+extern char *__inet_get_networka(void *, int);
+extern int __inet_address_count(void *);
+
+
+void
+usage(char *s)
+{
+ fprintf(stderr, "\t%s -a {-L|-G} [-o <opt-string>] [-d domain] ", s);
+ fprintf(stderr, "[-C client] {<server-list>} ...\n");
+
+ fprintf(stderr, "\t%s -l {-L|-G} [-C client]\n", s);
+
+ fprintf(stderr, "\t%s -m {-L|-G} [-o <opt-string>] [-d domain] ", s);
+ fprintf(stderr, "[-C client] {<old-server>=<new-server>} ...\n");
+
+ fprintf(stderr, "\t%s -r {-L|-G} [-o <opt-string>] [-d domain] ", s);
+ fprintf(stderr, "[-C client] {<server-list>} ...\n");
+
+ fprintf(stderr, "\t%s -u {-L|-G} [-o <opt-string>] [-d domain] ", s);
+ fprintf(stderr, "[-C client] {<server-list>}\n");
+
+ fprintf(stderr, "\t%s -x {-L|-G} [-d domain] [-C client]\n", s);
+
+ fprintf(stderr, "\t%s -F\n", s);
+
+ exit(1);
+}
+
+main(int argc, char **argv)
+{
+ int c;
+ int op;
+ int subop;
+ int op_count = 0;
+ int subop_count = 0;
+ char *options = NULL;
+ char *domain = NULL;
+ char *client = NULL;
+ int create = 0;
+ server_list servers;
+
+ while ((c = getopt(argc, argv, "vZTFLGalmurxo:d:C:")) != EOF) {
+ switch (c) {
+ case 'T':
+ op_count++;
+ op = OP_TEST;
+ break;
+ case 'F':
+ if (geteuid() != (uid_t)0) {
+ fprintf(stderr,
+ "%s: -F option must be run as root.\n",
+ argv[0]);
+ exit(1);
+ }
+ op_count++;
+ op = OP_FLUSH;
+ break;
+ case 'L':
+ op_count++;
+ op = OP_LOCAL;
+ break;
+ case 'G':
+ op_count++;
+ op = OP_GLOBAL;
+ break;
+ case 'a':
+ subop_count++;
+ subop = OP_ADD;
+ create = 1;
+ break;
+ case 'l':
+ subop_count++;
+ subop = OP_LIST;
+ break;
+ case 'm':
+ subop_count++;
+ subop = OP_MODIFY;
+ break;
+ case 'u':
+ subop_count++;
+ subop = OP_UPDATE;
+ break;
+ case 'r':
+ subop_count++;
+ subop = OP_REMOVE;
+ break;
+ case 'x':
+ subop_count++;
+ subop = OP_DELETE;
+ break;
+ case 'o':
+ options = optarg;
+ break;
+ case 'd':
+ domain = optarg;
+ break;
+ case 'C':
+ client = optarg;
+ break;
+ case 'Z':
+ debug = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ if (op_count == 0) {
+ fprintf(stderr,
+ "%s: one of -L, -G, -T, or -F must be specified\n",
+ argv[0]);
+ usage(argv[0]);
+ } else if (op_count != 1) {
+ fprintf(stderr,
+ "%s: only one of -L, -G, -T, or -F may be specified\n",
+ argv[0]);
+ usage(argv[0]);
+ }
+ if (op == OP_LOCAL || op == OP_GLOBAL) {
+ if (subop_count == 0) {
+ fprintf(stderr,
+ "%s: one of -a, -l, -m, -r, or -x must be specified\n",
+ argv[0]);
+ usage(argv[0]);
+ } else if (subop_count != 1) {
+ fprintf(stderr,
+ "%s: only one of -a, -l, -m, -r, or -x may be specified\n",
+ argv[0]);
+ usage(argv[0]);
+ }
+ }
+
+ if (!nis_defaults_init(NULL))
+ exit(1);
+
+ if (op == OP_TEST) {
+ exit(0);
+ }
+
+ if (op == OP_FLUSH) {
+ exit(flush_cache(argv[0]));
+ }
+
+ /* OP_LOCAL or OP_GLOBAL */
+ if (subop == OP_LIST) {
+ /* listing the clint_info */
+ if (op == OP_LOCAL)
+ print_local_servers(&servers, client);
+ else {
+ if (domain == NULL)
+ domain = nis_local_directory();
+ print_servers(&servers, domain, client);
+ }
+ exit(0);
+ }
+ if (geteuid() != (uid_t)0) {
+ fprintf(stderr,
+ "%s: -L option must be run as root except to list "
+ "the preferred server information (the -l option)\n",
+ argv[0]);
+ exit(1);
+ }
+
+ if (client == NULL) {
+ client = (char *)malloc(256 * sizeof (char));
+ if (client == NULL) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ if (gethostname(client, 256)) {
+ perror("gethostname()");
+ exit(1);
+ }
+ }
+
+ if (op == OP_LOCAL)
+ get_local_servers(&servers, client, create);
+ else {
+ if (domain == NULL)
+ domain = nis_local_directory();
+ get_servers(&servers, domain, client, create);
+ }
+
+ if (client)
+ servers.client = client;
+ if (options) {
+ if (*options && (strcasecmp(options, "all") != 0) &&
+ (strcasecmp(options, "pref_only") != 0)) {
+ fprintf(stderr, "%s: invalid option specified.\n",
+ options);
+ fprintf(stderr,
+ "Valid options are \"all\" and \"pref_only\".\n");
+ exit(1);
+ }
+ servers.options = options;
+ }
+
+ if (subop == OP_ADD) {
+ for (; optind < argc; optind++) {
+ char *buf, *name, *p;
+ buf = strdup(argv[optind]);
+ name = buf;
+ do {
+ if (p = strchr(name, ','))
+ *p++ = '\0';
+ if (verbose)
+ printf("Adding server %s...\n",
+ name);
+ server_list_add(&servers, name);
+ name = p;
+ } while (p);
+ free(buf);
+ }
+ } else if (subop == OP_UPDATE) {
+ server_list_clear(&servers);
+ for (; optind < argc; optind++) {
+ char *buf, *name, *p;
+ buf = strdup(argv[optind]);
+ name = buf;
+ do {
+ if (p = strchr(name, ','))
+ *p++ = '\0';
+ if (verbose)
+ printf("Updating server %s...\n",
+ name);
+ server_list_add(&servers, name);
+ name = p;
+ } while (p);
+ free(buf);
+ }
+ } else if (subop == OP_MODIFY) {
+ for (; optind < argc; optind++) {
+ char *p, tmp[100];
+ strcpy(tmp, argv[optind]);
+ p = strchr(tmp, '=');
+ if (p == NULL) {
+ fprintf(stderr,
+ "%s: Ignored (invalid format)\n", tmp);
+ continue;
+ }
+ *p++ = '\0';
+ if (!server_list_remove(&servers, tmp))
+ fprintf(stderr, "%s: not found\n", tmp);
+ else {
+ if (verbose)
+ printf("Modifying server %s...\n", tmp);
+ server_list_add(&servers, p);
+ }
+ }
+ } else if (subop == OP_REMOVE) {
+ for (; optind < argc; optind++) {
+ char *buf, *name, *p;
+ buf = strdup(argv[optind]);
+ name = buf;
+ do {
+ if (p = strchr(name, ','))
+ *p++ = '\0';
+ if (verbose)
+ printf("Removing server %s...\n",
+ name);
+ (void) server_list_remove(&servers, name);
+ name = p;
+ } while (p);
+ free(buf);
+ }
+ }
+
+ if (subop == OP_DELETE) {
+ if (verbose)
+ printf("Deleting entry %s...\n", servers.client);
+ if (op == OP_LOCAL) {
+ remove_local_servers(&servers);
+ (void) flush_cache(argv[0]);
+ } else
+ remove_servers(&servers, domain);
+ } else {
+ if (op == OP_LOCAL) {
+ update_local_servers(&servers);
+ (void) flush_cache(argv[0]);
+ } else
+ update_servers(&servers, domain);
+ }
+ return (0);
+}
+
+
+void
+server_list_init(server_list *servers)
+{
+ int i;
+
+ servers->options = "";
+ servers->client = "";
+ servers->count = 0;
+ servers->alloc = 5;
+ servers->list = (char **)malloc(servers->alloc * sizeof (char *));
+ servers->interface = (char **)malloc(servers->alloc * sizeof (char *));
+ servers->weight = (int *)malloc(servers->alloc * sizeof (int));
+ if (servers->weight == NULL || servers->list == NULL) {
+ fprintf(stderr, "out of memory (1)\n");
+ exit(1);
+ }
+ for (i = 0; i < servers->count; i++) {
+ servers->interface[i] = NULL;
+ servers->list[i] = NULL;
+ servers->weight[i] = -1;
+ }
+ servers->ntoken = 0;
+ servers->token_buff = NULL;
+}
+
+void
+server_list_reinit(server_list *servers)
+{
+ server_list_clear(servers);
+ servers->options = "";
+ servers->client = "";
+}
+
+void
+server_list_clear(server_list *servers)
+{
+ int i;
+
+ for (i = 0; i < servers->count; i++) {
+ if (servers->list[i])
+ free(servers->list[i]);
+ servers->list[i] = NULL;
+ if (servers->interface[i])
+ free(servers->interface[i]);
+ servers->interface[i] = NULL;
+ servers->weight[i] = -1;
+ }
+ servers->count = 0;
+ if (servers->ntoken > 0) {
+ servers->ntoken = 0;
+ free(servers->token_buff);
+ servers->token_buff = NULL;
+ }
+}
+
+void
+server_list_add(server_list *servers, char *s)
+{
+ int i;
+ char *host, *interface;
+ int weight;
+
+ if (s == NULL || *s == NULL)
+ return;
+
+ if (debug)
+ printf("server_list_add: [%d] %s\n", servers->count, s);
+
+ parse_server(s, &host, &interface, &weight);
+
+ for (i = 0; i < servers->count; i++) {
+ if (strcasecmp(servers->list[i], host) == 0) {
+ if (interface) {
+ if (servers->interface[i] &&
+ strcasecmp(servers->interface[i],
+ interface) == 0) {
+ servers->weight[i] = weight;
+ return;
+ }
+ } else {
+ if (!servers->interface[i]) {
+ /*
+ * For exact match. Both interfaces
+ * must be NULL.
+ */
+ servers->weight[i] = weight;
+ return;
+ }
+ }
+ }
+ }
+
+ if (servers->count + 1 > servers->alloc) {
+ servers->alloc += 5;
+ servers->list = (char **)realloc((char *)servers->list,
+ servers->alloc * sizeof (char *));
+ servers->interface =
+ (char **)realloc((char *)servers->interface,
+ servers->alloc * sizeof (char *));
+
+ if (servers->list == NULL || servers->interface == NULL) {
+ fprintf(stderr, "out of memory (2)\n");
+ exit(1);
+ }
+ }
+ servers->list[servers->count] = host;
+ servers->interface[servers->count] = interface;
+ servers->weight[servers->count] = weight;
+ if (debug)
+ printf("server_list_add: count=%d l=<%s> i=<%s> w=%d\n",
+ servers->count, servers->list[servers->count],
+ (servers->interface[servers->count]) ?
+ servers->interface[servers->count] : "",
+ servers->weight[servers->count]);
+ servers->count += 1;
+}
+
+int
+server_list_remove(server_list *servers, char *s)
+{
+ int i;
+ char *host, *interface;
+ int weight;
+
+ if (s == NULL || *s == NULL)
+ return (0);
+
+ if (debug)
+ printf("server_list_remove: [%d] %s\n", servers->count, s);
+
+ parse_server(s, &host, &interface, &weight);
+
+ for (i = 0; i < servers->count; i++) {
+ if (strcasecmp(servers->list[i], host) == 0) {
+ /*
+ * If interface is not defined, then it will match
+ * the first client entry.
+ * If interface is defined, then it must match
+ * the interface in the server list.
+ */
+ if (interface) {
+ if (servers->interface[i] &&
+ strcasecmp(servers->interface[i],
+ interface) == 0)
+ break;
+ } else {
+ if (!servers->interface[i]) {
+ /*
+ * For exact match. Both interfaces
+ * must be NULL.
+ */
+ break;
+ }
+ }
+ }
+ }
+
+ if (i >= servers->count)
+ return (0); /* not in list */
+
+ if (servers->list[i]) {
+ free(servers->list[i]);
+ }
+ if (servers->interface[i]) {
+ free(servers->interface[i]);
+ }
+ servers->list[i] = NULL;
+ servers->interface[i] = NULL;
+ servers->weight[i] = -1;
+
+ for (; i < servers->count - 1; i++) {
+ servers->list[i] = servers->list[i+1];
+ servers->interface[i] = servers->interface[i+1];
+ servers->weight[i] = servers->weight[i+1];
+ }
+
+ servers->count -= 1;
+ if (debug)
+ printf("server_list_remove: removed =%s\n", host);
+ return (1);
+}
+
+void
+server_list_print(server_list *servers, FILE *fp)
+{
+ int i;
+ int len, l;
+
+ if (debug) {
+ printf("server_list_print: client=%s", servers->client);
+ }
+ if (servers->count == 0) {
+ char buf[50];
+ strcpy(buf, servers->client);
+ strcat(buf, "\t");
+ if (print_tokens(fp, servers, buf)) {
+ if (debug)
+ printf("\n");
+ fprintf(fp, "\n");
+ } else
+ fprintf(stderr,
+ "This became an empty entry. Removing it from the file\n");
+ return;
+ }
+
+ fprintf(fp, "%s", servers->client);
+ len = strlen(servers->client);
+ for (i = 0; i < servers->count; i++) {
+ if (i == 0) {
+ if (debug)
+ printf("\t%s=", PREF_SRVR);
+ fprintf(fp, "\t%s=", PREF_SRVR);
+ len += strlen(PREF_SRVR) + 1;
+ } else {
+ if (debug)
+ printf(",");
+ fprintf(fp, ",");
+ l = strlen(servers->list[i]) + 1;
+ if (servers->interface[i])
+ l += strlen(servers->interface[i]) + 2;
+ len += l;
+ if (len > 75) {
+ if (debug)
+ printf("\\\n\t");
+ fprintf(fp, "\\\n\t");
+ len = l;
+ }
+ }
+ if (debug)
+ printf("%s", servers->list[i]);
+ fprintf(fp, "%s", servers->list[i]);
+ if (servers->interface[i]) {
+ if (debug)
+ printf("/%s", servers->interface[i]);
+ fprintf(fp, "/%s", servers->interface[i]);
+ }
+ if (servers->weight[i] != -1) {
+ if (debug)
+ printf("(%d)", servers->weight[i]);
+ fprintf(fp, "(%d)", servers->weight[i]);
+ }
+ }
+ if (*servers->options) {
+ char *tab;
+ if (servers->count > 0)
+ tab = " ";
+ else
+ tab = "\t";
+ if (debug)
+ printf("%s%s=%s", tab, PREF_TYPE,
+ servers->options);
+ fprintf(fp, "%s%s=%s", tab, PREF_TYPE,
+ servers->options);
+ }
+ (void) print_tokens(fp, servers, " ");
+
+ if (debug)
+ printf("\n");
+ fprintf(fp, "\n");
+
+}
+
+
+void
+server_list_dump(char *value, FILE *fp)
+{
+ int len, n;
+ char *p, *pe;
+ char buf[100];
+
+ len = strlen(value);
+ if (debug)
+ printf("server_list_dump: [%d] <%s>\n", len, value);
+ p = value;
+ for (; len > 80; ) {
+ pe = strpbrk(p + 70, " ,\t");
+ n = pe - p + 1;
+ strncpy(buf, p, n);
+ buf[n] = '\0';
+ if (debug)
+ printf("%s\\\n\t", buf);
+ fprintf(fp, "%s\\\n\t", buf);
+ len -= n;
+ p = pe + 1;
+ }
+ if (debug)
+ printf("%s\n", p);
+ fprintf(fp, "%s\n", p);
+}
+
+
+
+void
+parse_server(char *value, char **server, char **interface, int *weight)
+{
+ char *p, *buf;
+ char *s = NULL, *i = NULL;
+ int w = -1;
+
+ *weight = -1;
+ *server = NULL;
+ *interface = NULL;
+
+ if (value == NULL || *value == '\0')
+ return;
+
+ buf = strdup(value);
+ s = buf;
+
+ while ((p = strpbrk(buf, "/(")) != NULL) {
+ switch (*p) {
+ case '/':
+ if (i != NULL) {
+ fprintf(stderr, "%s : invalid format\n",
+ value);
+ exit(1);
+ }
+ *p = '\0';
+ i = ++p;
+ if (debug)
+ printf("parse_server: interface=%s\n", i);
+ break;
+ case '(':
+ if (w != -1) {
+ fprintf(stderr, "%s : invalid format\n",
+ value);
+ exit(1);
+ }
+ *p = '\0';
+ w = atoi(++p);
+ p = strchr(p, ')');
+ if (debug)
+ printf("parse_server: weight=%d\n", w);
+ break;
+ }
+ buf = p;
+ }
+
+ *server = strdup(s);
+ if (*server == NULL) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ *weight = w;
+ if (i != NULL) {
+ /* check if the interface specified is a legal IP address */
+ struct in_addr in4;
+ struct in6_addr in6;
+ sa_family_t af;
+
+ af = strchr(i, ':') != 0 ? AF_INET6 : AF_INET;
+ if (inet_pton(af, i,
+ (af == AF_INET6) ? (void *)&in6 : (void *)&in4) != 1) {
+ fprintf(stderr, "%s: invalid interface.\n", i);
+ fprintf(stderr,
+ "This should be the internet address for the interface.\n");
+ exit(1);
+ }
+ *interface = strdup(i);
+ if (*interface == NULL) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ }
+
+ if (debug)
+ printf("parse_server: server=%s, weight=%d, interface=%s\n",
+ *server, *weight, (*interface)? *interface : "");
+}
+
+
+int
+break_tokens(server_list *servers, char *info)
+{
+ char *p;
+
+ if (debug)
+ printf("break_tokens: %s\n", info);
+
+ if (servers->ntoken != 0 && servers->token_buff) {
+ free(servers->token_buff);
+ servers->token_buff = NULL;
+ servers->ntoken = 0;
+ }
+ servers->token_buff = strdup(info);
+ p = servers->token_buff;
+ while (*p) {
+ /* skip spaces */
+ while (*p && isspace(*p))
+ p++;
+ if (*p == '\0')
+ break;
+ /* assign token */
+ servers->tokens[servers->ntoken] = p;
+
+ /* find the end of token */
+ while (*p && !isspace(*p))
+ p++;
+ if (*p != '\0') {
+ *p = '\0';
+ p++;
+ }
+ if (debug)
+ printf("break_tokens: [%d]=%s\n", servers->ntoken,
+ servers->tokens[servers->ntoken]);
+ servers->ntoken++;
+ }
+ return (servers->ntoken);
+}
+
+
+
+int
+print_tokens(FILE *fp, server_list * servers, char *h)
+{
+ int i;
+ int count = 0;
+ int first = 1;
+
+ for (i = 0; i < servers->ntoken; i++) {
+ if (servers->tokens[i][0] != '\0') {
+ if (debug)
+ printf("%s%s", (first) ? h : " ",
+ servers->tokens[i]);
+ fprintf(fp, "%s%s", (first) ? h : " ",
+ servers->tokens[i]);
+ first = 0;
+ count++;
+ }
+ }
+ return (count);
+}
+
+
+
+int
+parse_info(server_list *servers, char *info, char **hosts, char **options)
+{
+ char *p1 = NULL;
+ int i, n;
+
+ if ((info == NULL) || (*info == '\0')) {
+ *hosts = NULL;
+ *options = NULL;
+ return (1);
+ }
+
+ /* parse hosts */
+ n = break_tokens(servers, info);
+ for (i = 0; i < n; i++) {
+ if (debug)
+ printf("parse_info: servers->tokens[%d]=%s\n", i,
+ servers->tokens[i]);
+ if (strncmp(servers->tokens[i], PREF_SRVR,
+ strlen(PREF_SRVR)) == 0) {
+ /* preferred servers */
+ p1 = servers->tokens[i] + strlen(PREF_SRVR);
+ if (*p1 != '=' || *hosts != NULL)
+ /* invalid format */
+ return (0);
+ *hosts = strdup(p1 + 1);
+ if (*hosts == NULL) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ servers->tokens[i][0] = '\0';
+ if (debug)
+ printf("parse_info: hosts=%s\n", *hosts);
+ } else if (strncmp(servers->tokens[i], PREF_TYPE,
+ strlen(PREF_TYPE)) == 0) {
+ /* preferred types */
+ p1 = servers->tokens[i] + strlen(PREF_TYPE);
+ if (*p1 != '=' || *options != NULL)
+ /* invalid format */
+ return (0);
+ *options = strdup(p1 + 1);
+ if (*options == NULL) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ servers->tokens[i][0] = '\0';
+ if (*options[0] && (strcasecmp(*options, "all") != 0) &&
+ (strcasecmp(*options, "pref_only") != 0)) {
+ fprintf(stderr, "%s: invalid option.\n",
+ *options);
+ fprintf(stderr,
+ "Valid options are \"all\" and \"pref_only\".\n");
+ exit(1);
+ }
+ if (debug)
+ printf("parse_info: options=%s\n", *options);
+ }
+ }
+
+ return (1);
+}
+
+
+void
+parse_preference(server_list *servers, char *client, char *hosts, char *options)
+{
+ char *value;
+
+ /* parse client */
+ servers->client = client;
+ if (debug)
+ printf("parse_preference: hosts=%s\n", hosts);
+
+ /* parse hosts */
+ while (*hosts) {
+ value = hosts;
+ while (*hosts && !isspace(*hosts) && *hosts != ',')
+ hosts++;
+
+ if (hosts && *hosts)
+ *hosts++ = '\0';
+
+ while (hosts && *hosts == ',')
+ hosts++;
+
+ server_list_add(servers, value);
+ }
+
+ /* parse options */
+ servers->options = options;
+}
+
+
+char *
+get_line(FILE *fp)
+{
+ char *p;
+ int len, cont = 0;
+ char *value = NULL;
+ char buf[1024];
+
+ while ((p = fgets(buf, sizeof (buf), fp)) != NULL) {
+ cont = 0;
+ len = strlen(p);
+ if ((len - 1 >= 0) && (p[len - 1] == '\n'))
+ p[len - 1] = '\0';
+ if ((len - 2 >= 0) && (p[len - 2] == '\\')) {
+ cont = 1;
+ p[len - 2] = '\0';
+ }
+
+ if (value == NULL) {
+ value = strdup(p);
+ if (value == 0) {
+ fprintf(stderr, "out of memory (3)\n");
+ exit(1);
+ }
+ } else {
+ value = (char *)realloc(value,
+ strlen(value) + len + 2);
+ if (value == 0) {
+ fprintf(stderr, "out of memory (3)\n");
+ exit(1);
+ }
+ while (*p && isspace(*p))
+ p++;
+ strcat(value, p);
+ }
+ if (!cont)
+ break; /* complete line */
+ }
+ if (debug && value != NULL)
+ printf("get_line: text=%s\n", value);
+
+ return (value);
+}
+
+
+void
+get_local_servers(server_list *servers, char *target, int create)
+{
+ FILE *fp;
+ char *value = NULL;
+ char *client = NULL;
+ char *hosts = NULL;
+ char *option = NULL;
+ char *info = NULL;
+ char *p = NULL;
+
+ server_list_init(servers);
+ fp = fopen(LOCAL_PREF, "r");
+ if (fp == NULL) {
+ if (create) {
+ /*
+ * Allow it to continue...
+ * File will be created in update_local_servers()
+ */
+ return;
+ } else {
+ fprintf(stderr, "%s does not exist\n", LOCAL_PREF);
+ exit(1);
+ }
+ }
+
+ while ((value = get_line(fp)) != NULL) {
+ hosts = NULL;
+ option = NULL;
+ client = value;
+ p = strpbrk(value, " \t");
+ if (p == NULL) {
+ fprintf(stderr, "%s: %s\n", LOCAL_PREF, value);
+ fprintf(stderr, "Invalid format\n");
+ exit(1);
+ }
+
+ *p++ = '\0';
+ while (*p && isspace(*p))
+ p++;
+ info = p;
+ if (!parse_info(servers, info, &hosts, &option)) {
+ fprintf(stderr,
+ "Error found while parsing local file\n");
+ fprintf(stderr, "Please see manpage\n");
+ exit(1);
+ }
+
+ if (client == NULL)
+ client = "";
+ if (hosts == NULL)
+ hosts = "";
+ if (option == NULL)
+ option = "";
+
+ parse_preference(servers, client, hosts, option);
+
+ if (strcasecmp(client, target) == 0)
+ break;
+
+ server_list_reinit(servers);
+ free(value);
+ value = NULL;
+ if (*hosts == '\0')
+ free(hosts);
+ if (*option == '\0')
+ free(option);
+ }
+
+ fclose(fp);
+}
+
+void
+get_servers(server_list *servers, char *domain, char *client, int create)
+{
+ char *hosts = NULL;
+ char *options = NULL;
+ char name[NIS_MAXNAMELEN];
+ nis_result *res;
+ nis_object *obj;
+ ulong_t flags = FOLLOW_PATH|FOLLOW_LINKS|EXPAND_NAME;
+
+ entry_found = 0;
+ /* first check to see if table exists */
+ sprintf(name, "%s.org_dir.%s", TABLE, domain);
+ res = nis_lookup(name, flags);
+ if (res == 0) {
+ fprintf(stderr, "nis_lookup failed\n");
+ nis_perror(NIS_NOMEMORY, name);
+ exit(1);
+ }
+
+ server_list_init(servers);
+ if (res->status == NIS_NOTFOUND || res->status == NIS_NOSUCHTABLE) {
+ if (create) {
+ create_table(name, domain);
+ res->status = NIS_SUCCESS;
+ return;
+ } else {
+ nis_perror(res->status, "finding table");
+ exit(1);
+ }
+ }
+
+ obj = res->objects.objects_val;
+ if (obj->TA_data.ta_cols.ta_cols_len == 2)
+ old_format = 1; /* need old format for back compat */
+ else
+ old_format = 0;
+
+ if (old_format)
+ sprintf(name,
+ "[client=%s],%s.org_dir.%s", client, TABLE, domain);
+ else
+ sprintf(name,
+ "[client=%s,attr=%s],%s.org_dir.%s", client, PREF_SRVR,
+ TABLE, domain);
+ res = nis_list(name, flags, 0, 0);
+ if (res == 0) {
+ fprintf(stderr, "nis_list failed\n");
+ nis_perror(NIS_NOMEMORY, name);
+ exit(1);
+ }
+ if (res->status == NIS_NOTFOUND) {
+ return;
+ }
+
+ if (res->status != NIS_SUCCESS) {
+ nis_perror(res->status, name);
+ exit(1);
+ }
+
+ entry_found = 1;
+ obj = res->objects.objects_val;
+ client = ENTRY_VAL(obj, 0);
+ if (old_format) {
+ char *info = ENTRY_VAL(obj, 1);
+ if (!parse_info(servers, info, &hosts, &options)) {
+ printf(
+ "Error found while parsing the table information\n");
+ printf("Please see manpage.\n");
+ exit(1);
+ }
+ } else {
+ hosts = ENTRY_VAL(obj, 2);
+ options = ENTRY_VAL(obj, 3);
+ }
+
+ if (client == NULL)
+ client = "";
+ if (hosts == NULL)
+ hosts = "";
+ if (options == NULL)
+ options = "";
+
+ parse_preference(servers, client, hosts, options);
+}
+
+void
+remove_local_servers(server_list *target)
+{
+ FILE *fpin;
+ FILE *fpout;
+ char *value;
+ char *client;
+ char *p;
+
+ fpout = fopen(LOCAL_TMP, "w");
+ if (fpout == NULL) {
+ fprintf(stderr, "can't open %s for output\n", LOCAL_TMP);
+ exit(1);
+ }
+
+ fpin = fopen(LOCAL_PREF, "r");
+ if (fpin != NULL) {
+ while ((value = get_line(fpin)) != NULL) {
+ client = value;
+ p = strpbrk(value, " \t");
+ if (p == NULL) {
+ fprintf(stderr, "%s: %s\n", LOCAL_PREF, value);
+ fprintf(stderr, "Invalid format\n");
+ exit(1);
+ }
+ if (strncasecmp(client, target->client, p - value)
+ != 0) {
+ /* skip this line */
+ server_list_dump(value, fpout);
+ }
+ }
+ fclose(fpin);
+ } else {
+ /* local file does not exist */
+ fclose(fpout);
+ unlink(LOCAL_TMP);
+ fprintf(stderr, "%s does not exist\n", LOCAL_PREF);
+ exit(1);
+ }
+ if (fclose(fpout) != 0 || rename(LOCAL_TMP, LOCAL_PREF) == -1) {
+ unlink(LOCAL_TMP);
+ perror(LOCAL_TMP);
+ exit(1);
+ }
+}
+
+void
+remove_servers(server_list *servers, char *domain)
+{
+ char name[NIS_MAXNAMELEN];
+ nis_result *res;
+ ulong_t flags = FOLLOW_PATH|FOLLOW_LINKS|EXPAND_NAME;
+
+ if (debug)
+ printf("remove_servers: removing %s\n", servers->client);
+ if (old_format)
+ sprintf(name, "[client=%s],%s.org_dir.%s",
+ servers->client, TABLE, domain);
+ else
+ sprintf(name, "[client=%s,attr=%s],%s.org_dir.%s",
+ servers->client, PREF_SRVR, TABLE, domain);
+ res = nis_remove_entry(name, NULL, flags);
+ if (res == 0) {
+ fprintf(stderr, "nis_remove_entry failed\n");
+ nis_perror(NIS_NOMEMORY, name);
+ exit(1);
+ }
+ if (res->status == NIS_NOTFOUND) {
+ return;
+ }
+ if (res->status != NIS_SUCCESS) {
+ nis_perror(res->status, name);
+ exit(1);
+ }
+}
+
+void
+update_local_servers(server_list *target)
+{
+ FILE *fpin;
+ FILE *fpout;
+ char *value = NULL;
+ char *client;
+ char *p;
+
+ fpout = fopen(LOCAL_TMP, "w");
+ if (fpout == NULL) {
+ fprintf(stderr, "can't open %s for output\n", LOCAL_TMP);
+ exit(1);
+ }
+
+ fpin = fopen(LOCAL_PREF, "r");
+ if (fpin != NULL) {
+ while ((value = get_line(fpin)) != NULL) {
+ client = value;
+ p = strpbrk(value, " \t");
+ if (p == NULL) {
+ fprintf(stderr, "%s: %s\n", LOCAL_PREF, value);
+ fprintf(stderr, "Invalid format\n");
+ exit(1);
+ }
+ if (strncasecmp(client, target->client, p - value)
+ != 0) {
+ /* skip this line */
+ server_list_dump(value, fpout);
+ }
+ }
+ fclose(fpin);
+ }
+
+ server_list_print(target, fpout);
+
+ if (fclose(fpout) != 0 || rename(LOCAL_TMP, LOCAL_PREF) == -1) {
+ unlink(LOCAL_TMP);
+ perror(LOCAL_TMP);
+ exit(1);
+ }
+}
+
+void
+update_servers(server_list *servers, char *domain)
+{
+ int i;
+ int len = 0, bufsize = 1000;
+ char *buf, *optbuf;
+ entry_col cols[NCOLS];
+ nis_object eobj;
+ nis_result *res;
+ char table[NIS_MAXNAMELEN];
+ char tmp[10], lbuf[100];
+ int first = 1;
+
+ if (old_format)
+ convert_old2new(servers, domain);
+
+ buf = (char *)malloc(bufsize * sizeof (char));
+ if (buf == NULL) {
+ fprintf(stderr, "out of memory (4)\n");
+ exit(1);
+ }
+ optbuf = (char *)malloc(bufsize * sizeof (char));
+ if (optbuf == NULL) {
+ fprintf(stderr, "out of memory (4)\n");
+ exit(1);
+ }
+
+ buf[0] = optbuf[0] = '\0';
+ if (servers->count > 0) {
+ if (debug)
+ printf("update_servers:\n");
+ for (i = 0; i < servers->count; i++) {
+ if (i != 0) {
+ lbuf[0] = ',';
+ lbuf[1] = '\0';
+ } else
+ lbuf[0] = '\0';
+ if (debug)
+ printf("adding %d: %s\n", i, servers->list[i]);
+ strcat(lbuf, servers->list[i]);
+ if (servers->interface[i]) {
+ if (debug)
+ printf("/%s", servers->interface[i]);
+ strcat(lbuf, "/");
+ strcat(lbuf, servers->interface[i]);
+ }
+ if (servers->weight[i] != -1) {
+ if (debug)
+ printf("(%d)", servers->weight[i]);
+ sprintf(tmp, "(%d)", servers->weight[i]);
+ strcat(lbuf, tmp);
+ }
+ len += strlen(lbuf);
+ if (len > bufsize) {
+ bufsize += 1000;
+ buf = (char *)realloc(buf, bufsize *
+ sizeof (char));
+ }
+ strcat(buf, lbuf);
+ first = 0;
+ }
+ }
+
+ bufsize = 1000;
+ len = 0;
+ if (servers->options && *(servers->options)) {
+ lbuf[0] = '\0';
+ if (debug)
+ printf("update_servers: pref_type = %s\n",
+ servers->options);
+ strcat(lbuf, servers->options);
+ len += strlen(lbuf);
+ if (len > bufsize) {
+ bufsize += 50;
+ optbuf = (char *)realloc
+ (optbuf, bufsize * sizeof (char));
+ }
+ strcat(optbuf, lbuf);
+ first = 0;
+ }
+
+ if (strlen(buf) == 0) {
+ if (entry_found) {
+ fprintf(stderr,
+ "This became an empty entry. Removing it from the table\n");
+ remove_servers(servers, domain);
+ }
+ exit(0);
+ }
+
+ if (debug)
+ printf("info=%s, %s\n", buf, optbuf);
+
+ cols[0].ec_flags = 0;
+ cols[0].ec_value.ec_value_val = servers->client;
+ cols[0].ec_value.ec_value_len = strlen(servers->client) + 1;
+
+ cols[1].ec_flags = 0;
+ cols[1].ec_value.ec_value_val = PREF_SRVR;
+ cols[1].ec_value.ec_value_len = strlen(PREF_SRVR) + 1;
+
+ cols[2].ec_flags = 0;
+ cols[2].ec_value.ec_value_val = buf;
+ cols[2].ec_value.ec_value_len = strlen(buf) + 1;
+
+ cols[3].ec_flags = 0;
+ cols[3].ec_value.ec_value_val = optbuf;
+ cols[3].ec_value.ec_value_len = strlen(optbuf) + 1;
+
+ eobj = nis_default_obj;
+ eobj.zo_data.zo_type = ENTRY_OBJ;
+ eobj.EN_data.en_type = TABLE_TYPE;
+ eobj.EN_data.en_cols.en_cols_len = NCOLS;
+ eobj.EN_data.en_cols.en_cols_val = cols;
+
+ sprintf(table, "%s.org_dir.%s", TABLE, domain);
+ if (debug)
+ printf("calling nis_add_entry...");
+ res = nis_add_entry(table, &eobj, ADD_OVERWRITE);
+ if (debug)
+ printf("Done\n");
+ if (res == 0) {
+ fprintf(stderr, "nis_add_entry failed\n");
+ nis_perror(NIS_NOMEMORY, table);
+ exit(1);
+ } else if (res->status != NIS_SUCCESS) {
+ nis_perror(res->status, table);
+ exit(1);
+ }
+}
+
+/*
+ * table_col cols[] = {
+ * "client", TA_SEARCHABLE, 0,
+ * "servers", 0, 0,
+ * "options", 0, 0,
+ * };
+ */
+
+void
+create_table(char *name, char *domain)
+{
+ nis_object tobj;
+ nis_result *res;
+ int ncols = NCOLS;
+ table_col cols[NCOLS];
+ char gname[NIS_MAXNAMELEN];
+
+ if (debug)
+ printf("creating table\n");
+ cols[0].tc_name = "client";
+ cols[0].tc_flags = TA_SEARCHABLE | TA_CASE;
+ cols[0].tc_rights = nis_default_obj.zo_access;
+
+ cols[1].tc_name = "attr";
+ cols[1].tc_flags = TA_SEARCHABLE | TA_CASE;
+ cols[1].tc_rights = nis_default_obj.zo_access;
+
+ cols[2].tc_name = "info";
+ cols[2].tc_flags = 0;
+ cols[2].tc_rights = nis_default_obj.zo_access;
+
+ cols[3].tc_name = "flags";
+ cols[3].tc_flags = 0;
+ cols[3].tc_rights = nis_default_obj.zo_access;
+
+ tobj = nis_default_obj;
+ sprintf(gname, "admin.%s", domain);
+ tobj.zo_group = gname;
+ tobj.zo_access |= ((NIS_READ_ACC | NIS_MODIFY_ACC | NIS_CREATE_ACC |
+ NIS_DESTROY_ACC) << 8);
+ tobj.zo_data.zo_type = TABLE_OBJ;
+ tobj.TA_data.ta_type = TABLE_TYPE;
+ tobj.TA_data.ta_maxcol = ncols;
+ tobj.TA_data.ta_sep = ' ';
+ tobj.TA_data.ta_path = "";
+ tobj.TA_data.ta_cols.ta_cols_len = ncols;
+ tobj.TA_data.ta_cols.ta_cols_val = cols;
+ if (debug)
+ printf("calling nis_add %s\n", name);
+ res = nis_add(name, &tobj);
+ if (res == NULL) {
+ fprintf(stderr, "nis_add failed\n");
+ nis_perror(NIS_NOMEMORY, name);
+ exit(1);
+ }
+ if (res->status != NIS_SUCCESS) {
+ nis_perror(res->status, name);
+ exit(1);
+ }
+
+}
+
+void
+convert_old2new(server_list *servers, char *domain)
+{
+ nis_result *old_res, *res;
+ char name[NIS_MAXNAMELEN];
+ nis_object eobj;
+ int i;
+ ulong_t flags = FOLLOW_PATH|FOLLOW_LINKS|EXPAND_NAME;
+
+ if (debug)
+ printf("Converting table\n");
+ /* dump old table */
+ if (debug)
+ printf("dumping old table...");
+ sprintf(name, "%s.org_dir.%s", TABLE, domain);
+ old_res = nis_list(name, flags, 0, 0);
+ if (old_res == 0) {
+ fprintf(stderr, "failed to convert %s to new format\n",
+ TABLE);
+ fprintf(stderr, "while dumping table\n");
+ fprintf(stderr, "nis_list failed\n");
+ nis_perror(NIS_NOMEMORY, name);
+ exit(1);
+ }
+ if (old_res->status != NIS_SUCCESS) {
+ fprintf(stderr, "failed to convert %s to new format\n",
+ TABLE);
+ fprintf(stderr, "while dumping table\n");
+ nis_perror(old_res->status, name);
+ exit(1);
+ }
+ if (debug)
+ printf("done\n");
+
+ /* delete old table */
+ if (debug)
+ printf("cleaning out old table...");
+ res = nis_remove_entry(name, NULL, flags|REM_MULTIPLE);
+ if (res == 0) {
+ fprintf(stderr, "failed to convert %s to new format\n",
+ TABLE);
+ fprintf(stderr, "while cleaning out table\n");
+ fprintf(stderr, "nis_remove_entry failed\n");
+ nis_perror(NIS_NOMEMORY, name);
+ exit(1);
+ }
+ if (res->status != NIS_SUCCESS) {
+ fprintf(stderr, "failed to convert %s to new format\n",
+ TABLE);
+ fprintf(stderr, "while cleaning out table\n");
+ nis_perror(res->status, name);
+ exit(1);
+ }
+ if (debug)
+ printf("done\n");
+ if (debug)
+ printf("removing old table...");
+ res = nis_remove(name, NULL);
+ if (res == 0) {
+ fprintf(stderr, "failed to convert %s to new format\n",
+ TABLE);
+ fprintf(stderr, "while deleting table\n");
+ fprintf(stderr, "nis_remove failed\n");
+ nis_perror(NIS_NOMEMORY, name);
+ exit(1);
+ }
+ if (res->status != NIS_SUCCESS) {
+ fprintf(stderr, "failed to convert %s to new format\n",
+ TABLE);
+ fprintf(stderr, "while deleting table\n");
+ nis_perror(res->status, name);
+ exit(1);
+ }
+ if (debug)
+ printf("done\n");
+
+ /* create new table */
+ create_table(name, domain);
+
+ /* reload table with info from old table */
+ if (debug)
+ printf("reloading table in new format\n");
+ eobj = nis_default_obj;
+ eobj.zo_data.zo_type = ENTRY_OBJ;
+ eobj.EN_data.en_type = TABLE_TYPE;
+ eobj.EN_data.en_cols.en_cols_len = NCOLS;
+
+ for (i = 0; i < old_res->objects.objects_len; i++) {
+ nis_object *obj;
+ char *hosts, *options;
+ entry_col cols[NCOLS];
+
+ hosts = options = NULL;
+ obj = &(old_res->objects.objects_val[i]);
+ /* copy client column */
+ cols[0].ec_flags = 0;
+ cols[0].ec_value.ec_value_len = ENTRY_LEN(obj, 0);
+ cols[0].ec_value.ec_value_val = ENTRY_VAL(obj, 0);
+
+ /* fill in pref_srvr column */
+ cols[1].ec_flags = 0;
+ cols[1].ec_value.ec_value_val = PREF_SRVR;
+ cols[1].ec_value.ec_value_len = strlen(PREF_SRVR) + 1;
+
+ /* extract servers and options */
+ parse_info(servers, ENTRY_VAL(obj, 1), &hosts, &options);
+ if (hosts == NULL)
+ hosts = "";
+ if (options == NULL)
+ options = "";
+ if (debug) {
+ printf("convert_old2new: hosts = %s\n", hosts);
+ printf("convert_old2new: options = %s\n", options);
+ }
+
+ /* fill in servers column */
+ cols[2].ec_flags = 0;
+ cols[2].ec_value.ec_value_val = hosts;
+ cols[2].ec_value.ec_value_len = strlen(hosts) + 1;
+
+ /* fill in options column */
+ cols[3].ec_flags = 0;
+ cols[3].ec_value.ec_value_val = options;
+ cols[3].ec_value.ec_value_len = strlen(options) + 1;
+
+ /* add the new entry */
+ eobj.EN_data.en_cols.en_cols_val = cols;
+ if (debug)
+ printf("convert_old2new: calling nis_add_entry...");
+ res = nis_add_entry(name, &eobj, ADD_OVERWRITE);
+ if (debug)
+ printf("Done\n");
+ if (res == 0) {
+ fprintf(stderr, "failed to convert %s to new format\n",
+ TABLE);
+ fprintf(stderr, "while repopulating table\n");
+ fprintf(stderr, "nis_add_entry failed\n");
+ nis_perror(NIS_NOMEMORY, TABLE);
+ exit(1);
+ } else if (res->status != NIS_SUCCESS) {
+ fprintf(stderr, "failed to convert %s to new format\n",
+ TABLE);
+ fprintf(stderr, "while repopulating table\n");
+ nis_perror(res->status, TABLE);
+ exit(1);
+ }
+ }
+ nis_freeresult(old_res);
+ if (debug)
+ printf("finished convert_old2new\n");
+}
+
+void
+print_info(char *client, char *hosts, char *option)
+{
+ static first = 1;
+
+ if (!(client && *client && hosts && *hosts && option))
+ return;
+
+ if (first) {
+ fprintf(stdout, "client\tinformation\n");
+ fprintf(stdout, "------\t-----------\n");
+ first = 0;
+ }
+ fprintf(stdout, "%s\t%s=%s", client, PREF_SRVR, hosts);
+ if (*option)
+ fprintf(stdout, "\t%s=%s", PREF_TYPE, option);
+ fprintf(stdout, "\n");
+}
+
+void
+print_local_servers(server_list *servers, char *target)
+{
+ FILE *fp;
+ char *value = NULL;
+ char *client = NULL;
+ char *hosts = NULL;
+ char *option = NULL;
+ char *info = NULL;
+ char *p = NULL;
+
+ fp = fopen(LOCAL_PREF, "r");
+ if (fp == NULL)
+ return;
+
+ server_list_init(servers);
+ while ((value = get_line(fp)) != NULL) {
+ hosts = NULL;
+ option = NULL;
+ client = value;
+ p = strpbrk(value, " \t");
+ if (p == NULL) {
+ fprintf(stderr, "%s: %s\n", LOCAL_PREF, value);
+ fprintf(stderr, "Invalid format\n");
+ exit(1);
+ }
+
+ *p++ = '\0';
+ while (*p && isspace(*p))
+ p++;
+ info = p;
+ if (!parse_info(servers, info, &hosts, &option)) {
+ fprintf(stderr,
+ "Error found while parsing local file\n");
+ fprintf(stderr, "Please see manpage\n");
+ exit(1);
+ }
+
+ if (client == NULL)
+ client = "";
+ if (hosts == NULL)
+ hosts = "";
+ if (option == NULL)
+ option = "";
+
+ if (target && *target) {
+ if (strcasecmp(client, target) == 0) {
+ print_info(client, hosts, option);
+ fclose(fp);
+ return;
+ }
+ } else {
+ /* list all entries */
+ print_info(client, hosts, option);
+ }
+
+ server_list_reinit(servers);
+ free(value);
+ value = NULL;
+ if (*hosts == '\0')
+ free(hosts);
+ if (*option == '\0')
+ free(option);
+ }
+
+ fclose(fp);
+}
+
+
+int
+print_line(char *tab, nis_object *ent, void *udata)
+{
+ static first = 1;
+ char *hosts = NULL, *client = NULL, *options = NULL, *info = NULL;
+ server_list servers;
+
+ server_list_init(&servers);
+ client = ENTRY_VAL(ent, 0);
+ if (old_format) {
+ info = ENTRY_VAL(ent, 1);
+ if (!parse_info(&servers, info, &hosts, &options)) {
+ printf(
+ "Error found while parsing the table information\n");
+ printf("Please see manpage.\n");
+ exit(1);
+ }
+ } else {
+ hosts = ENTRY_VAL(ent, 2);
+ options = ENTRY_VAL(ent, 3);
+ }
+
+ if (!(client && *client && hosts && *hosts))
+ return (0);
+
+ if (first) {
+ fprintf(stdout, "client\t\tinformation\n");
+ fprintf(stdout, "------\t\t-----------\n");
+ first = 0;
+ }
+ fprintf(stdout, "%s\t%s=%s", client, PREF_SRVR, hosts);
+ if (options && *options)
+ fprintf(stdout, "\t%s=%s", PREF_TYPE, options);
+ fprintf(stdout, "\n");
+ return (0);
+}
+
+
+void
+print_servers(server_list *servers, char *domain, char *client)
+{
+ char *hosts = NULL;
+ char *options = NULL;
+ char *info = NULL;
+ char name[NIS_MAXNAMELEN];
+ nis_result *res;
+ nis_object *obj;
+ ulong_t flags = FOLLOW_PATH|FOLLOW_LINKS|EXPAND_NAME;
+
+ /* first check to see if table exists */
+ sprintf(name, "%s.org_dir.%s", TABLE, domain);
+ res = nis_lookup(name, flags);
+ if (res == 0 || res->status != NIS_SUCCESS)
+ return;
+
+ obj = res->objects.objects_val;
+ if (obj->TA_data.ta_cols.ta_cols_len == 2)
+ old_format = 1; /* need old format for back compat */
+ else
+ old_format = 0;
+
+ if (client && *client) {
+ server_list_init(servers);
+ /* specific client lookcup */
+ if (old_format)
+ sprintf(name, "[client=%s],%s.org_dir.%s",
+ client, TABLE, domain);
+ else
+ sprintf(name, "[client=%s,attr=%s],%s.org_dir.%s",
+ client, PREF_SRVR, TABLE, domain);
+ res = nis_list(name, flags, 0, 0);
+ if (res == 0) {
+ fprintf(stderr, "nis_list failed\n");
+ nis_perror(NIS_NOMEMORY, name);
+ exit(1);
+ }
+ if (res->status == NIS_NOTFOUND) {
+ return;
+ } else if (res->status != NIS_SUCCESS) {
+ nis_perror(res->status, name);
+ exit(1);
+ }
+
+ obj = res->objects.objects_val;
+ client = ENTRY_VAL(obj, 0);
+ if (old_format) {
+ info = ENTRY_VAL(obj, 1);
+ if (!parse_info(servers, info, &hosts, &options)) {
+ printf(
+ "Error found while parsing the table information\n");
+ printf("Please see manpage.\n");
+ exit(1);
+ }
+ } else {
+ hosts = ENTRY_VAL(obj, 2);
+ options = ENTRY_VAL(obj, 3);
+ }
+
+ if (client == NULL)
+ client = "";
+ if (hosts == NULL)
+ hosts = "";
+ if (options == NULL)
+ options = "";
+
+ print_info(client, hosts, options);
+ return;
+ }
+
+ /* list all entries */
+ sprintf(name, "%s.org_dir.%s", TABLE, domain);
+
+ res = nis_list(name, flags, print_line, 0);
+ if ((res->status != NIS_CBRESULTS) && (res->status != NIS_NOTFOUND)) {
+ nis_perror(res->status, "can't list table");
+ exit(1);
+ }
+
+}
+
+/*
+ * This routine sends a signal to the local Cache manager to flush the
+ * internal server list.
+ * Return values:
+ * 0: signal sent sucessfully
+ * 1: signal failed
+ */
+int
+flush_cache(char *prog)
+{
+
+ char *cmd = "kill -HUP `ps -e |"
+ " awk '{if ($4 == \"nis_cach\") printf(\"%d \", $1);}'`"
+ " 2> /dev/null";
+
+ if (system(cmd) != 0) {
+ /* error or nis_cachemgr not running */
+ fprintf(stderr,
+ "%s: Unable to flush the cache manager.", prog);
+ fprintf(stderr,
+ "\tThe cache manager is possibly down.");
+ return (1);
+ }
+ return (0);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisrestore.cc b/usr/src/cmd/rpcsvc/nis/utils/nisrestore.cc
new file mode 100644
index 0000000000..a95fb6cba3
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nisrestore.cc
@@ -0,0 +1,1301 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * nisrestore.cc
+ *
+ * Copyright (c) 1988-1998 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * nisrestore.cc
+ *
+ * A NIS+ utility for restoring archived databases, as well as an
+ * out-of-band resync mechanism.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <signal.h>
+#include <wait.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/nis.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <errno.h>
+#include <rpcsvc/nis_db.h>
+#include <rpcsvc/nis.h>
+#include "nis_bkrst.h"
+#include "../rpc.nisd/log.h"
+#include "../rpc.nisd/nis_proc.h"
+
+/*
+ * Global state variables.
+ */
+static bool_t force = FALSE;
+static bool_t aflg = FALSE;
+static bool_t tocflg = FALSE;
+static bool_t olddata = TRUE;
+static bool_t verbiage = FALSE;
+static nis_server *srv_ptr = NULL;
+static char *syncfile = "/var/nis/nisrestore.pid";
+static char *tempdir = "/var/nis/.nisrestore";
+static int fd_sync;
+
+static void abort_handler(int);
+
+/*
+ * Extern variables related to the trans.log header.
+ */
+extern log_hdr *__nis_log;
+extern int __nis_logfd;
+extern unsigned long __maxloglen, __loghiwater, __nis_logsize, __nis_filesize;
+extern "C" char *relative_name(char *);
+
+/*
+ * We are masquerading as the master server, so we can create and write
+ * to a temporary transaction file. This will get us around the
+ * CHILDPROC check.
+ */
+extern pid_t master_pid;
+
+/*
+ * This restoration utility assumes the backup was performed by the nisbackup
+ * utility and not via some other tools. Using nisbackup will validate the
+ * following assumptions being made during the restoration sequence:
+ *
+ * - the backup directory is organized on a per-directory basis.
+ * - there are no log files. No table logs and no dictionary log.
+ * (the backup utility _simulates_ a checkpoint)
+ * - the dictionary file has entries only for the table files relevant
+ * to the directory being restored. In other words, the complete
+ * dictionary file is merged without validating each entry.
+ * - the transaction log has only entry and that is the UPD_TIME_STAMP
+ * entry for the directory being restored.
+ *
+ */
+
+/*
+ * Needs to be cleaned up in nislog as well as here. nis_log_common.o
+ * is shared by rpc.nisd as well as nislog and nisbackup and rpc.nisd
+ * source already defines this.
+ */
+int
+abort_transaction(int xid)
+{
+ xid = 0;
+ return (0);
+}
+
+static void
+init_signals()
+{
+ sigset(SIGHUP, abort_handler);
+ sigset(SIGINT, abort_handler);
+ sigset(SIGTERM, abort_handler);
+}
+
+static void
+abort_handler(int sig)
+{
+ sig = 0;
+ exit(1);
+}
+
+/*
+ * clean_up() called atexit()
+ */
+static void
+clean_up()
+{
+ close(fd_sync);
+ unlink(syncfile);
+}
+
+void
+usage()
+{
+ fprintf(stderr, "usage: nisrestore [-fv] backup-dir directory...\n");
+ fprintf(stderr, " nisrestore [-fv] -a backup-dir\n");
+ fprintf(stderr, " nisrestore -t backup-dir\n");
+ exit(1);
+}
+
+/*
+ * This routine vforks "rm -r" to remove a non-empty directory, for cleanup.
+ */
+int
+_rm_dir(char * name)
+{
+ struct stat s;
+ pid_t child;
+ siginfo_t si;
+
+ if (stat(name, &s) == -1) {
+ fprintf(stderr,
+ "nisbackup: directory %s does not exist.", name);
+ return (1);
+ }
+ switch (child = vfork()) {
+ case -1: /* error */
+ fprintf(stderr, "Cannot remove files in %s", name);
+ return (1);
+ case 0: /* child */
+ if (execl("/bin/rm", "rm",
+ "-r",
+ name,
+ NULL) < 0)
+ _exit(1);
+ _exit(0); /* Since this is the child proc, we'll exit */
+ default: /* parent, we'll wait for the rm to complete */
+ if (waitid(P_PID, child, &si, WEXITED) == -1) {
+ fprintf(stderr, "Cannot remove files in %s", name);
+ return (1);
+ } else {
+ if (si.si_status != 0) {
+ fprintf(stderr,
+ "Can't remove files: error = %d",
+ si.si_status);
+ return (1);
+ }
+ }
+ }
+ return (0);
+}
+
+/*
+ * This routine takes the old name of the data file, a token to search for,
+ * and the replacement string to replace the token with. This will change
+ * names like org_dir.foo.bar to org_dir.foo (tok=.foo.bar, repl=.foo).
+ */
+char *
+change_name(char *filename, char *tok, char *repl)
+{
+ char *newname;
+ char *loc_end, *loc_beg;
+ char *s;
+
+ newname = (char *) calloc(1, sizeof (char) * strlen(filename) +
+ strlen(repl) - strlen(tok) + 1);
+ if (!newname) {
+ fprintf(stderr, "Unable to allocate memory.\n");
+ return (NULL);
+ }
+ if (strlen(tok) == 0) {
+ strcpy(newname, filename);
+ strcat(newname, repl);
+ } else {
+ if (s = strstr(filename, tok)) {
+ int sublen = s - filename;
+ memcpy(newname, filename, sublen);
+ strcat(newname, repl);
+ } else {
+ free(newname);
+ return (NULL);
+ }
+ }
+ return (newname);
+}
+
+/*
+ * This routine checks the data file names in the backup with their relative
+ * name on this server. If the master server that generated this backup lived
+ * in a different domain than this (replica) server, the files will need to
+ * be renamed and their names changed in the data dictionary.
+ */
+bool_t
+files_need_renaming(char * full_obj_name, char * backup_data_dir,
+ char ** oldsuf, char ** newsuf)
+{
+
+ char buf[BUFSIZ];
+ struct stat st;
+ char *domainof;
+ DIR *dirptr;
+ struct dirent *dent;
+ char *p;
+
+ *oldsuf = NULL;
+ *newsuf = NULL;
+ /*
+ * If we're restoring a root domain server, no renaming.
+ */
+ sprintf(buf, "%s/%s", backup_data_dir, ROOT_OBJ);
+ if (stat(buf, &st) == 0) {
+ return (FALSE);
+ }
+ /*
+ * The only way the localized name of the object is NULL is by
+ * using the -f(orce) option on a machine that's in the wrong
+ * domain for this object. All bets are off with the -f option. Punt.
+ */
+ if (nis_name_of(full_obj_name) == NULL)
+ return (FALSE);
+
+ /*
+ * If localized name of directory exist in backup dir, no renaming.
+ */
+ p = relative_name(full_obj_name);
+ sprintf(buf, "%s/%s", backup_data_dir, p);
+ if (stat(buf, &st) == 0) {
+ free((void *)p);
+ return (FALSE);
+ }
+
+ if ((dirptr = opendir(backup_data_dir)) == NULL) {
+ fprintf(stderr, "Unable to open %s: %m", backup_data_dir);
+ exit(1);
+ }
+ while ((dent = readdir(dirptr)) != NULL) {
+ if (strcmp(dent->d_name, ".") == 0)
+ continue; /* Ignore */
+ if (strcmp(dent->d_name, "..") == 0)
+ continue; /* Ignore */
+
+ /*
+ * Extrace the domain part of the entry, which will be the old suffix.
+ * Then get the domain part of the entry as it would be stored on this
+ * server (new suffix).
+ */
+ if (strcmp(nis_domain_of(dent->d_name), ".") == 0) {
+ *oldsuf = strdup("");
+ } else {
+ sprintf(buf, ".%s", nis_domain_of(dent->d_name));
+ *oldsuf = strdup(buf);
+ }
+
+ if (strcmp(nis_domain_of(p), ".") == 0) {
+ *newsuf = strdup("");
+ } else {
+ sprintf(buf, ".%s", nis_domain_of(p));
+ *newsuf = strdup(buf);
+ }
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+rename_data_files(char * oldsuf, char * newsuf)
+{
+
+ char buf[BUFSIZ];
+ char oldname[BUFSIZ];
+ char newname[BUFSIZ];
+ char *newtablename;
+ char dotlog[BUFSIZ];
+ DIR *dirptr;
+ struct dirent *dent;
+
+ sprintf(buf, "%s/data", tempdir);
+ if ((dirptr = opendir(buf)) == NULL) {
+ fprintf(stderr, "Unable to open %s: %m", buf);
+ return (FALSE);
+ }
+ while ((dent = readdir(dirptr)) != NULL) {
+ if (strcmp(dent->d_name, ".") == 0)
+ continue; /* Ignore */
+ if (strcmp(dent->d_name, "..") == 0)
+ continue; /* Ignore */
+ /*
+ * Take the leaf name of the table, append the new suffix, then rename
+ * the file. Attempt to unlink any .log file that may still be present.
+ */
+ if ((newtablename = change_name(dent->d_name, oldsuf, newsuf))
+ == FALSE) {
+ fprintf(stderr, "Unable to rename data files\n");
+ return (FALSE);
+ }
+ sprintf(newname, "/var/nis/data/%s", newtablename);
+ sprintf(oldname, "%s/%s", buf, dent->d_name);
+ rename(oldname, newname);
+ sprintf(dotlog, "%s.log", newname);
+ unlink(dotlog);
+ }
+ /* The temporary directory should be empty now, so we can remove it. */
+ if (_rm_dir(tempdir)) {
+ fprintf(stderr, "Unable to remove directory:%s", tempdir);
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+/*
+ * bool_t is_server_of()
+ *
+ * Boolean function to check if local host is a server for the object
+ * passed in nis_obj. If the passed in object is not fully qualified, then
+ * nis_getnames() is called to resolve the object's full name. The parameter,
+ * full_name, is used to return the fully qualified name (ignored if set to
+ * NULL). The nis_server struct is returned in *srv.
+ */
+bool_t
+is_server_of(char *nis_obj, char *full_name, nis_server **srv)
+{
+
+ char myname[NIS_MAXNAMELEN];
+ nis_name *namelist;
+ nis_error err;
+ int i;
+ directory_obj d_obj;
+
+ /*
+ * We will use __nis_CacheBind instead of nis_lookup() so that we
+ * can take advantage of the NIS_SHARED_DIRCACHE.
+ */
+ if (nis_obj[strlen(nis_obj) - 1] != '.') { /* Partially qualified */
+ namelist = nis_getnames(nis_obj);
+ for (i = 0; namelist[i]; i++) {
+#ifdef DEBUG
+printf("name: %s\n", namelist[i]);
+#endif
+ err = __nis_CacheBind(namelist[i], &d_obj);
+ if (err == NIS_SUCCESS) {
+ if (full_name)
+ sprintf(full_name, "%s", namelist[i]);
+ break;
+ }
+ }
+ } else { /* Fully qualified */
+ err = __nis_CacheBind(nis_obj, &d_obj);
+ if (err == NIS_SUCCESS)
+ if (full_name)
+ sprintf(full_name, "%s", nis_obj);
+ }
+ if (err != NIS_SUCCESS) {
+ fprintf(stderr, "Unable to lookup %s\n", nis_obj);
+#ifdef DEBUG
+fprintf(stderr, "Error code=%d\n", err);
+#endif
+ return (0);
+ }
+ /*
+ * If we can get a directory object, we check for membership.
+ */
+ if (err == NIS_SUCCESS) {
+ sprintf(myname, "%s", nis_local_host());
+ for (i = 0; i < d_obj.do_servers.do_servers_len; i++) {
+ if (strcasecmp(myname,
+ d_obj.do_servers.do_servers_val[i].name)
+ == 0) {
+ *srv = &(d_obj.do_servers.do_servers_val[i]);
+ return (1);
+ }
+ }
+ }
+ if (!force)
+ fprintf(stderr, "Not server for %s\n", nis_obj);
+ return (0);
+}
+
+/*
+ * This is a link list of the directories being restored on this server.
+ */
+struct nis_dir_list {
+ char *name;
+ uint32_t upd_time;
+ struct nis_dir_list *next;
+};
+static struct nis_dir_list *dirlisthead = NULL;
+enum CACHE_OP {CACHE_INIT, CACHE_ADD, CACHE_SAVE, CACHE_FIND, CACHE_DUMP};
+/*
+ * dirobj_list() maintains list of directory objects that are backed up
+ * with the -a(ll) option of the nisbackup utility.
+ */
+struct nis_dir_list *
+dirobj_list(enum CACHE_OP op, char *argp)
+{
+ char buf[BUFSIZ];
+ FILE *fr, *fw;
+ char *name, *end;
+ int ss;
+ struct nis_dir_list *tmp = NULL;
+ struct stat st;
+
+ switch (op) {
+ case CACHE_INIT:
+ /* Initialize cache with the file name passed in argp */
+ fr = fopen(argp, "r");
+ if (fr == NULL) {
+ return ((struct nis_dir_list *) NULL);
+ }
+ while (fgets(buf, BUFSIZ, fr)) {
+ name = buf;
+ while (isspace(*name))
+ name++;
+ end = name;
+ while (!isspace(*end))
+ end++;
+ *end = NULL;
+ tmp = (struct nis_dir_list *)
+ XMALLOC(sizeof (struct nis_dir_list));
+ if (tmp == NULL) {
+ fclose(fr);
+ return ((struct nis_dir_list *) NULL);
+ }
+ if ((tmp->name = strdup(name)) == NULL) {
+ free(tmp);
+ fclose(fr);
+ return ((struct nis_dir_list *) NULL);
+ }
+ tmp->next = dirlisthead;
+ dirlisthead = tmp;
+ }
+ fclose(fr);
+ return (dirlisthead);
+
+ case CACHE_ADD:
+ if (argp == NULL)
+ return ((struct nis_dir_list *) NULL);
+ /* Check whether already in cache */
+ for (tmp = dirlisthead; tmp; tmp = tmp->next)
+ if (strcasecmp(tmp->name, (char *)argp) == 0)
+ return (tmp);
+
+ /* Add it */
+ tmp = (struct nis_dir_list *)
+ malloc(sizeof (struct nis_dir_list));
+ if (tmp == NULL) {
+ return ((struct nis_dir_list *) NULL);
+ }
+ if ((tmp->name = strdup((char *)argp)) == NULL) {
+ free(tmp);
+ return ((struct nis_dir_list *) NULL);
+ }
+ tmp->next = dirlisthead;
+ dirlisthead = tmp;
+#ifdef DEBUG
+ fprintf(stderr, "objdir_list: added : %s\n", tmp->name);
+#endif
+ return (tmp);
+
+ case CACHE_SAVE: /* Save back'd up objects to a file */
+ if (argp == NULL)
+ return (NULL);
+ fw = fopen(argp, "w");
+ if (fw == NULL) {
+ ss = stat(argp, &st);
+ if (ss == -1 && errno == ENOENT) {
+ fw = fopen(argp, "w+");
+ }
+ if (fw == NULL) {
+ fprintf(stderr,
+ "Could not open file %s for updating\n",
+ argp);
+ return ((struct nis_dir_list *) NULL);
+ }
+ }
+ for (tmp = dirlisthead; tmp; tmp = tmp->next)
+ fprintf(fw, "%s\n", (char *)tmp->name);
+ if (fclose(fw) == EOF)
+ return (NULL);
+ return (dirlisthead);
+
+ case CACHE_FIND: /* Search for object in cache */
+ if (argp == NULL)
+ return (NULL);
+ for (tmp = dirlisthead; tmp; tmp = tmp->next)
+ if (strcasecmp(tmp->name, (char *)argp) == 0)
+ return (tmp);
+ return (NULL);
+
+ case CACHE_DUMP: /* Show 'em what we've got */
+ for (tmp = dirlisthead; tmp; tmp = tmp->next)
+ fprintf(stderr, "%s : %d\n",
+ tmp->name, tmp->upd_time);
+ return ((struct nis_dir_list *) NULL);
+
+ default:
+ return ((struct nis_dir_list *) NULL);
+ }
+}
+
+int
+merge_trans_log()
+{
+ log_upd *cur, *nxt;
+ u_long addr_p, upd_size;
+ int error;
+ int fd, num = 0;
+ int ret;
+ char backup[1024];
+ char buf[NIS_MAXNAMELEN];
+ ulong last_xid = 0;
+ struct nis_dir_list *tmp = NULL;
+
+ if (verbiage)
+ fprintf(stderr, "Merging the transaction log.\n");
+
+ /*
+ * Map in transaction log file.
+ */
+ sprintf(buf, "%s", LOG_FILE);
+ if (map_log(buf, FNISD)) {
+ fprintf(stderr,
+ "Unable to map transaction log : %s\n", buf);
+ return (0);
+ }
+
+ if (__nis_log->lh_state != LOG_STABLE) {
+ fprintf(stderr,
+ "Unable to merge transaction log, log unstable.\n");
+ return (0);
+ }
+
+ strcpy(backup, nis_data(BACKUP_LOG));
+
+ fd = open(backup, O_WRONLY+O_SYNC+O_CREAT+O_TRUNC, 0600);
+ if (fd == -1) {
+ fprintf(stderr,
+ "Unable to open backup log (%s).\n", backup);
+ return (0);
+ }
+
+ /*
+ * Make a backup of the log in two steps, write the size of the log
+ * and then a copy of the log.
+ */
+ __nis_logsize = (__nis_log->lh_tail) ? LOG_SIZE(__nis_log) :
+ sizeof (log_hdr);
+ if (write(fd, &__nis_logsize, sizeof (long)) != sizeof (long)) {
+ fprintf(stderr,
+ "Unable to merge transaction log, disk full.\n");
+ close(fd);
+ unlink(backup);
+ return (0);
+ }
+ /*
+ * Now set the state to RESYNC so if we have to recover and read
+ * this back in, and we get screwed while reading it, we won't
+ * get into major trouble. This still leaves open one window :
+ * a) Start checkpoint
+ * b) Successfully backup to BACKUP
+ * c) Set log state to CHECKPOINT
+ * c) crash.
+ * d) Reboot and start reading in the backup log
+ * e) crash.
+ * f) reboot and now the log appears to be resync'ing without
+ * all of its data.
+ */
+ __nis_log->lh_state = LOG_RESYNC;
+ if (write(fd, __nis_log, (size_t) __nis_logsize) != __nis_logsize) {
+ fprintf(stderr,
+ "Unable to merge transaction log, disk full.\n");
+ close(fd);
+ unlink(backup);
+ __nis_log->lh_state = LOG_STABLE;
+ sync_header();
+ return (0);
+ }
+ close(fd);
+
+ /* If we crash here we're ok since the log hasn't changed. */
+ __nis_log->lh_state = LOG_CHECKPOINT;
+ sync_header();
+
+ addr_p = (u_long)(__nis_log->lh_head);
+ for (cur = __nis_log->lh_head, num = 0; cur; cur = nxt) {
+ nxt = cur->lu_next;
+ for (tmp = dirlisthead; tmp; tmp = tmp->next) {
+ if (nis_dir_cmp(cur->lu_dirname, tmp->name) ==
+ SAME_NAME)
+ break;
+ }
+ if (tmp == NULL) {
+ upd_size = XID_SIZE(cur);
+ memmove((char *)addr_p, (char *)cur, (size_t) upd_size);
+ last_xid = cur->lu_xid;
+ addr_p += upd_size;
+ num++;
+ }
+ }
+
+ if (num == 0) {
+ /* Deleted all of the entries. */
+#ifdef DEBUG
+ fprintf(stderr,
+ "merge_trans_log: all entries removed.\n");
+#endif
+ __nis_log->lh_head = NULL;
+ __nis_log->lh_tail = NULL;
+ __nis_log->lh_num = 0;
+ sync_header();
+ ret = ftruncate(__nis_logfd, FILE_BLK_SZ);
+ if (ret == -1) {
+ fprintf(stderr,
+ "Cannot truncate transaction log file\n");
+ return (0);
+ }
+ __nis_filesize = FILE_BLK_SZ;
+ ret = (int)lseek(__nis_logfd, __nis_filesize, SEEK_SET);
+ if (ret == -1) {
+ fprintf(stderr,
+ "merge_trans_log: cannot increase transaction log file size\n");
+ return (0);
+ }
+ ret = (int) write(__nis_logfd, "+", 1);
+ if (ret != 1) {
+ fprintf(stderr,
+ "cannot write one character to transaction log file\n");
+ return (0);
+ }
+ __nis_logsize = sizeof (log_hdr);
+ if (msync((caddr_t)__nis_log,
+ (size_t) __nis_logsize, MS_SYNC)) {
+ perror("msync:");
+ fprintf(stderr, "unable to mysnc() LOG\n");
+ return (0);
+ }
+
+ } else {
+#ifdef DEBUG
+ fprintf(stderr,
+ "merge_trans_log: some entries removed.\n");
+#endif
+ __nis_log->lh_xid = last_xid;
+ __nis_log->lh_num = num;
+ sync_header();
+ error = __log_resync(__nis_log, FCHKPT);
+ if (error) {
+ fprintf(stderr,
+ "Transaction log merge failed, unable to resync.\n");
+ return (0);
+ }
+ }
+
+ /*
+ * Write back "last update" stamps for all of the directories
+ * we're restoring.
+ */
+ __nis_log->lh_state = LOG_STABLE;
+ sync_header();
+ for (tmp = dirlisthead; tmp; tmp = tmp->next)
+ make_stamp(tmp->name, (u_long) tmp->upd_time);
+ unlink(backup);
+ return (1);
+}
+
+int
+copydir(char *src, bool_t rename_files)
+{
+ char dest[1024];
+ struct stat s;
+ pid_t child;
+ siginfo_t si;
+
+ if (stat(src, &s) == -1) {
+ fprintf(stderr, "Directory %s does not exist.\n", src);
+ exit(1);
+ }
+ if (rename_files) {
+ /*
+ * We need to rename each file! We'll copy them into the temporary
+ * directory, to be renamed into the /var/nis/data directory in
+ * rename_data_files().
+ */
+ strcpy(dest, tempdir);
+ if (stat(dest, &s) == -1) {
+ if (errno == ENOENT) {
+ if (mkdir(dest, 0700)) {
+ fprintf(stderr,
+ "Unable to create NIS+ directory %s\n",
+ dest);
+ exit(1);
+ }
+ } else {
+ fprintf(stderr,
+ "Unable to stat NIS+ directory %s.\n", dest);
+ exit(1);
+ }
+ } else {
+ _rm_dir(dest);
+ if (mkdir(dest, 0700)) {
+ fprintf(stderr,
+ "Unable to create NIS+ directory %s\n",
+ dest);
+ exit(1);
+ }
+ }
+ } else {
+ strcpy(dest, "/var/nis");
+ }
+
+ switch (child = vfork()) {
+ case -1: /* error */
+ fprintf(stderr, "Can't copy backed up files\n");
+ exit(1);
+ case 0: /* child */
+ if (execl("/bin/cp", "cp", "-r", src, dest, NULL) < 0)
+ _exit(1);
+ _exit(0);
+ default: /* parent, we'll wait for the cp to complete */
+ if (waitid(P_PID, child, &si, WEXITED) == -1) {
+ fprintf(stderr, "Can't copy backed up files\n");
+ exit(1);
+ } else {
+ if (si.si_status != 0) {
+ fprintf(stderr,
+ "Can't copy backed up files: error = %d\n",
+ si.si_status);
+ exit(1);
+ }
+ }
+ }
+ return (1);
+}
+
+void
+sanity_checks(char *backupdir)
+{
+ struct stat s;
+ char buf[NIS_MAXNAMELEN];
+ char backup_path[NIS_MAXNAMELEN];
+ char trans_log[NIS_MAXNAMELEN];
+ struct nis_dir_list *tmp = NULL;
+ nis_tag *res = NULL;
+ nis_error error = NIS_SUCCESS;
+ nis_tag tagctl;
+ unsigned char u_nl[16];
+ uint32_t *unl_p;
+ FILE *fr = NULL;
+ char pid[16];
+ int fd_s;
+#ifdef DEBUG
+ struct timeval ctime;
+ int elapse;
+#endif
+
+ /*
+ * Create a synchronization file, to prevent multiple invocations
+ * of nisrestore from running simultaneously.
+ */
+ if ((fd_sync = open(syncfile, O_RDWR | O_CREAT, 0644)) == -1) {
+ fprintf(stderr,
+ "Unable to create pid file: %s\n", syncfile);
+ exit(1);
+ }
+ /*
+ * Lock without blocking. If we can't get it, another nisrestore
+ * must have it.
+ */
+ lseek(fd_sync, 0L, SEEK_SET);
+ if ((fd_s = lockf(fd_sync, F_TLOCK, 1)) == -1) {
+ close(fd_sync);
+ fprintf(stderr, "Unable to lock pid file: %s\n", syncfile);
+ fprintf(stderr, "Is nisrestore already running?\n");
+ exit(1);
+ }
+ /*
+ * We'll write our pid to the file, for good measure. Leave the
+ * fd_sync open for the duration of the restore.
+ */
+ sprintf(pid, "%d\n", getpid());
+ if (write(fd_sync, pid, strlen(pid) + 1) < 0) {
+ close(fd_sync);
+ fprintf(stderr, "Cannot write pid file: %s\n", syncfile);
+ exit(1);
+ }
+
+ /*
+ * Catch signals and clean up files before exit.
+ */
+ init_signals();
+
+ /*
+ * Clean up any files before we exit.
+ */
+ atexit(clean_up);
+
+ if (*backupdir != '/') {
+ fprintf(stderr,
+ "Backup directory %s is not a absolute path name.\n",
+ backupdir);
+ exit(1);
+ }
+
+ /*
+ * Check if backup directory exists.
+ */
+ if (stat(backupdir, &s) == -1) {
+ fprintf(stderr,
+ "Backup directory %s does not exist.\n",
+ backupdir);
+ exit(1);
+ }
+
+ if (aflg) {
+ sprintf(buf, "%s/%s", backupdir, BACKUPLIST);
+ if (dirobj_list(CACHE_INIT, buf) == NULL) {
+ fprintf(stderr,
+ "Unable to access backup list : %s\n", buf);
+ exit(1);
+ }
+ /*
+ * Sanity check: make sure that we serve the directory object(s)
+ * listed in the backup_list file. Note: backup_list is created by
+ * nisbackup(1), so we know the objects are fully qualified.
+ */
+ for (tmp = dirlisthead; tmp; tmp = tmp->next) {
+ if (!is_server_of(tmp->name, NULL, &srv_ptr) &&
+ !force) {
+ fprintf(stderr, "Use -f option to override.\n");
+ exit(1);
+ }
+
+ }
+ }
+
+ /*
+ * Check if the NIS+ server is running. Report an error if it is.
+ * If srv_ptr is not set, they must be using the -f(orce), so all
+ * bets are off. Punt
+ */
+ if (srv_ptr) {
+ tagctl.tag_val = "";
+ tagctl.tag_type = 0;
+#ifdef DEBUG
+ fprintf(stderr, "Server name: %s\n", srv_ptr->name);
+ gettimeofday(&ctime, 0);
+ elapse = ctime.tv_sec;
+#endif
+ if (verbiage)
+ fprintf(stderr,
+ "Verifying rpc.nisd process is not running\n");
+ error = nis_servstate(srv_ptr, &tagctl, 1, &res);
+ if (error == NIS_SUCCESS) {
+ fprintf(stderr,
+ "rpc.nisd is active, kill server before running nisrestore\n");
+ exit(1);
+ }
+#ifdef DEBUG
+ gettimeofday(&ctime, 0);
+ fprintf(stderr, "Server access: %d seconds\n",
+ ctime.tv_sec - elapse);
+#endif
+ if (res != NULL)
+ nis_freetags(res, 1);
+ }
+#ifdef DEBUG
+ else
+ fprintf(stderr, "No Server found.\n");
+#endif
+ /*
+ * Check if backup directory(ies) exist and are valid.
+ */
+ for (tmp = dirlisthead; tmp; tmp = tmp->next) {
+
+ sprintf(backup_path, "%s/%s/%s", backupdir, tmp->name, NIS_DIR);
+ /*
+ * Check for the "data" subdirectory in the backup.
+ */
+ if (stat(backup_path, &s) == -1) {
+ fprintf(stderr,
+ "%s not found.\n", backup_path);
+ exit(1);
+ }
+ /*
+ * Check for the existence of a dictionary file in the backup-dir.
+ */
+ sprintf(buf, "%s.dict", backup_path);
+ if (stat(buf, &s) == -1) {
+ fprintf(stderr,
+ "Dictionary file %s not found.\n", buf);
+ exit(1);
+ }
+
+ /*
+ * Check for the last update transaction file in the backup-dir.
+ */
+ sprintf(trans_log, "%s/%s/%s", backupdir, tmp->name,
+ LASTUPDATE);
+ if (stat(trans_log, &s) == -1) {
+ fprintf(stderr, "Transaction log %s not found in.\n",
+ trans_log);
+ exit(1);
+ }
+ /*
+ * Read in the last update file, and cache the time stamp.
+ */
+ if ((fr = fopen(trans_log, "r")) == NULL) {
+ fprintf(stderr, "Error accessing file %s: %d\n",
+ trans_log, errno);
+ exit(1);
+ }
+ /*
+ * Read in the backup revision number, and verify.
+ */
+ fread((void *) u_nl, sizeof (unsigned long), 1, fr);
+ unl_p = (uint32_t *) u_nl;
+ if (ntohl(*unl_p) != BACKUPREV) {
+ fprintf(stderr,
+ "Invalid backup version #%X, should be #%X\n",
+ ntohl(*unl_p), BACKUPREV);
+ exit(1);
+ }
+
+ fread((void *) u_nl, sizeof (uint32_t), 1, fr);
+ unl_p = (uint32_t *) u_nl;
+ tmp->upd_time = ntohl(*unl_p);
+#ifdef DEBUG
+fprintf(stderr, "sanity_checks: update time: %d\n", tmp->upd_time);
+#endif
+ if (fclose(fr) == EOF) {
+ fprintf(stderr, "Error closing file\n");
+ exit(1);
+ }
+ }
+ /*
+ * Check for the local "data" directory existing. If not, create it.
+ */
+ sprintf(buf, "%s", nis_data(NULL));
+ if (stat(buf, &s) == -1) {
+ if (errno == ENOENT) {
+ sprintf(buf, nis_data(NULL));
+ if (mkdir(buf, 0700)) {
+ fprintf(stderr,
+ "Unable to create NIS+ directory %s\n", buf);
+ exit(1);
+ }
+ olddata = FALSE;
+ } else {
+ fprintf(stderr,
+ "Unable to stat NIS+ directory %s.\n", buf);
+ exit(1);
+ }
+ }
+ strcat(buf, ".dict");
+ if (!db_initialize(buf)) {
+ fprintf(stderr,
+ "Unable to initialize data dictionary %s.", buf);
+ exit(1);
+ }
+}
+
+bool_t
+merge_serving_list()
+{
+ char buf[BUFSIZ];
+ char filename[BUFSIZ];
+ FILE *fr;
+ char *name, *end;
+ int ss;
+ struct stat st;
+
+ strcpy(filename, nis_data("serving_list"));
+ ss = stat(filename, &st);
+ if (ss == -1 && errno == ENOENT) {
+ /*
+ * If it doesn't exist, CACHE_SAVE will create it.
+ */
+ return ((dirobj_list(CACHE_SAVE, filename) == dirlisthead));
+ } else {
+ fr = fopen(filename, "r");
+ if (fr == NULL)
+ return (FALSE);
+ while (fgets(buf, BUFSIZ, fr)) {
+ name = buf;
+ while (isspace(*name))
+ name++;
+ end = name;
+ while (!isspace(*end))
+ end++;
+ *end = NULL;
+ if (dirobj_list(CACHE_ADD, name) == NULL) {
+ fclose(fr);
+ return (FALSE);
+ }
+ }
+ fclose(fr);
+ return ((dirobj_list(CACHE_SAVE, filename) == dirlisthead));
+ }
+}
+
+/*
+ * rm_logs()
+ *
+ * This routine checks for each database file restored, if a .log file
+ * exists. The .log files are removed from the /var/nis/data directory,
+ * as they will be invalid after the restore is performed. As an optimization,
+ * a flag is set in sanity_checks that indicates if the /var/nis/data
+ * directory has been created (new install) and therefore this routine is
+ * unnecessary.
+ */
+bool_t
+rm_logs(char *src)
+{
+ char localdir[NIS_MAXNAMELEN];
+ char buf[NIS_MAXNAMELEN];
+ char full_name[NIS_MAXNAMELEN];
+ char *domainof;
+ DIR *dirptr;
+ struct dirent *dent;
+ struct stat st;
+
+#ifdef DEBUG
+ printf("rm_logs: entry point\n");
+#endif
+ sprintf(localdir, "%s", nis_data(NULL));
+ if ((dirptr = opendir(src)) == NULL) {
+ fprintf(stderr, "Unable to open %s.\n", src);
+ return (FALSE);
+ }
+
+ while ((dent = readdir(dirptr)) != NULL) {
+ if (strcmp(dent->d_name, ".") == 0)
+ continue; /* Ignore */
+ if (strcmp(dent->d_name, "..") == 0)
+ continue; /* Ignore */
+ if (strcmp(dent->d_name, ROOT_OBJ) == 0)
+ continue; /* root.object doesn't have log file */
+
+ sprintf(buf, "%s/%s.log", localdir, dent->d_name);
+ sprintf(full_name, "%s.%s", dent->d_name,
+ nis_local_directory());
+#ifdef DEBUG
+ printf("rm_logs: stat'ng: %s\n", buf);
+#endif
+ if (stat(buf, &st) == -1)
+ if (errno == ENOENT)
+ continue;
+/*
+ * Determine if this .log file belongs to a directory object that has been
+ * restored.
+ */
+ if (!dirobj_list(CACHE_FIND, full_name)) {
+ domainof = nis_domain_of(full_name);
+ if (!dirobj_list(CACHE_FIND, domainof))
+ continue;
+ }
+/*
+ * It's a match, delete it!
+ */
+ if (unlink(buf)) {
+ fprintf(stderr, "Unable to remove %s.\n", buf);
+ return (FALSE);
+ }
+ } /* while (readdir) */
+ closedir(dirptr);
+ return (TRUE);
+}
+
+
+bool_t
+print_backuplist(char *backupdir)
+
+{
+ struct stat s;
+ char buf[NIS_MAXNAMELEN];
+ struct nis_dir_list *dl = NULL;
+
+ if (*backupdir != '/') {
+ fprintf(stderr,
+ "Backup directory %s is not a absolute path name.\n",
+ backupdir);
+ return (FALSE);
+ }
+
+ /*
+ * Check if backup directory exists.
+ */
+ if (stat(backupdir, &s) == -1) {
+ fprintf(stderr, "Backup directory %s does not exist.\n",
+ backupdir);
+ return (FALSE);
+ }
+
+ sprintf(buf, "%s/%s", backupdir, BACKUPLIST);
+ if (dirobj_list(CACHE_INIT, buf) == NULL) {
+ fprintf(stderr, "Unable to access backup list : %s\n", buf);
+ return (FALSE);
+ }
+ for (dl = dirlisthead; dl; dl = dl->next)
+ fprintf(stdout, "%s\n", dl->name);
+ return (TRUE);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *backupdir;
+ char nisdomain[NIS_MAXNAMELEN];
+ char newdict[NIS_MAXNAMELEN];
+ char buf[NIS_MAXNAMELEN];
+ int c;
+ db_status dbstat;
+ char *oldsuf = NULL;
+ char *newsuf = NULL;
+ char *tmp;
+ struct nis_dir_list *dl = NULL;
+ bool_t rename_files;
+
+ if (geteuid() != 0) {
+ fprintf(stderr, "You must be root to run nisrestore.\n");
+ exit(1);
+ }
+ if (argc < 3)
+ usage();
+
+ while ((c = getopt(argc, argv, "aftv")) != -1) {
+ switch (c) {
+ case 'a' :
+ aflg = TRUE;
+ break;
+ case 'f' :
+ force = TRUE;
+ break;
+ case 't' :
+ tocflg = TRUE;
+ break;
+ case 'v' :
+ verbiage = TRUE;
+ break;
+ default:
+ usage();
+ }
+ }
+ if (aflg || tocflg) {
+ if (argc - optind != 1)
+ usage();
+ backupdir = argv[optind++];
+ } else {
+ if (argc - optind < 2)
+ usage();
+ backupdir = argv[optind++];
+ /*
+ * Yes, I really mean "=", not "==". tmp is assigned in while.
+ */
+ while (tmp = argv[optind++]) {
+ memset(nisdomain, 0, sizeof (nisdomain));
+ if (!is_server_of(tmp, nisdomain, &srv_ptr) &&
+ !force) { /* Not server for object specd */
+ fprintf(stderr, "Use -f option to override.\n");
+ exit(1);
+ } else {
+ if (dirobj_list(CACHE_ADD,
+ ((strlen(nisdomain)) ? nisdomain:tmp))
+ == NULL) {
+ fprintf(stderr,
+ "Internal error, unable to add to cache\n");
+ exit(1);
+ }
+ }
+ }
+ }
+
+ if (tocflg) {
+ if (!print_backuplist(backupdir)) {
+ fprintf(stderr,
+ "Could not list objects in backup directory %s\n",
+ backupdir);
+ exit(1);
+ }
+ exit(0);
+ }
+ master_pid = getpid();
+ sanity_checks(backupdir);
+/*
+ * Loop through dirobj_list, merge in dictionary entries. After looping
+ * through, close out the data dictionary, merge_trans_log(), update
+ * the serving_list and were done!
+ */
+ for (dl = dirlisthead; dl; dl = dl->next) {
+ sprintf(newdict, "%s/%s/%s.dict", backupdir, dl->name, NIS_DIR);
+ /*
+ * Now, copy the contents of the backup directory to our local
+ * directory. Overwrite any local files if they exist. First we
+ * check to see if the files need to be renamed. If the master
+ * lives in a different domain than the server being restored,
+ * the database files have different names.
+ */
+ if (verbiage)
+ fprintf(stderr, "Restoring %s\n", dl->name);
+ sprintf(buf, "%s/%s/%s", backupdir, dl->name, NIS_DIR);
+ rename_files = files_need_renaming(dl->name, buf, &oldsuf,
+ &newsuf);
+ if (!copydir(buf, rename_files)) {
+ fprintf(stderr,
+ "Unable to copy file from backup directory %s.\n", buf);
+ db_abort_merge_dict();
+ exit(1);
+ }
+ if ((rename_files) && (rename_data_files(oldsuf, newsuf)
+ == FALSE)) {
+ fprintf(stderr,
+ "Unable to move data files into place.\n");
+ db_abort_merge_dict();
+ exit(1);
+ }
+ if (verbiage)
+ fprintf(stderr,
+ "Adding the %s objects into the data dictionary.\n",
+ dl->name);
+ if ((dbstat = db_begin_merge_dict(newdict, oldsuf, newsuf))
+ != DB_SUCCESS) {
+ fprintf(stderr, "Unable to merge dictionary %s: %s.\n",
+ newdict, db_perror(dbstat));
+ db_abort_merge_dict();
+ exit(1);
+ }
+ /*
+ * Since the backup doesn't have any .log files, any reminant
+ * .log's need to be removed. This is taken care of by
+ * rename_data_files() if rename_files is TRUE.
+ */
+ if ((olddata) && (!rename_files) && (!rm_logs(buf))) {
+ fprintf(stderr, "Error from rm_logs.\n");
+ db_abort_merge_dict();
+ exit(1);
+ }
+ free(oldsuf);
+ free(newsuf);
+ }
+ /*
+ * Now closeout the data dictionary.
+ */
+ if ((dbstat = db_end_merge_dict()) != DB_SUCCESS) {
+ fprintf(stderr, "Error from db_end_merge_dict: %d\n",
+ dbstat);
+ db_abort_merge_dict();
+ exit(1);
+ }
+ if (verbiage)
+ fprintf(stderr, "Updating the trans.log file.\n");
+ if (!merge_trans_log()) {
+ fprintf(stderr,
+ "Unable to merge backed up transaction log.\n");
+ exit(1);
+ }
+ if (!merge_serving_list()) {
+ fprintf(stderr, "Error from merge_serving_list.\n");
+ db_abort_merge_dict();
+ exit(1);
+ }
+ exit(0);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisrm.c b/usr/src/cmd/rpcsvc/nis/utils/nisrm.c
new file mode 100644
index 0000000000..22864ac6e5
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nisrm.c
@@ -0,0 +1,166 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * nisrm.c
+ *
+ * Copyright 1988-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * nisrm.c
+ *
+ * nis+ object removal utility
+ */
+
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/nis.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+extern nis_object nis_default_obj;
+
+extern char *nisname_index();
+
+
+void
+usage()
+{
+ fprintf(stderr, "usage: nisrm [-if] name ...\n");
+ exit(1);
+}
+
+void
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+ char ask_remove = 0, force_remove = 0;
+ ulong_t expand;
+ char *name;
+ int error = 0;
+ int bad_name;
+ nis_result *res, *rres;
+ nis_object *obj;
+ char fname[NIS_MAXNAMELEN], buf[BUFSIZ];
+
+ while ((c = getopt(argc, argv, "if")) != -1) {
+ switch (c) {
+ case 'i':
+ ask_remove = 1;
+ break;
+ case 'f' :
+ force_remove = 1;
+ break;
+ default :
+ usage();
+ }
+ }
+
+ if (optind == argc)
+ usage();
+
+ while (optind < argc) {
+ name = argv[optind++];
+
+ if (name[strlen(name)-1] != '.')
+ expand = EXPAND_NAME;
+ else
+ expand = 0;
+
+ /*
+ * Get the object to remove.
+ */
+ res = nis_lookup(name, expand|MASTER_ONLY);
+ if (res->status != NIS_SUCCESS) {
+ if (!force_remove) {
+ nis_perror(res->status, name);
+ error = 1;
+ }
+ goto loop;
+ }
+
+ bad_name = (snprintf(fname, sizeof (fname), "%s.",
+ res->objects.objects_val[0].zo_name)
+ >= sizeof (fname));
+ if (!bad_name &&
+ *(res->objects.objects_val[0].zo_domain) != '.')
+ bad_name = (strlcat(fname,
+ res->objects.objects_val[0].zo_domain,
+ sizeof (fname)) >= sizeof (fname));
+
+ if (bad_name) {
+ if (!force_remove) {
+ nis_perror(NIS_BADNAME, name);
+ error = 1;
+ }
+ goto loop;
+ }
+
+ if (res->objects.objects_val[0].zo_data.zo_type ==
+ NIS_DIRECTORY_OBJ) {
+ if (!force_remove) {
+ fprintf(stderr, "\"%s\" is a directory!\n",
+ fname);
+ error = 1;
+ }
+ goto loop;
+ }
+
+ if (ask_remove || expand) {
+ printf("remove %s? ", fname);
+ *buf = '\0';
+ (void) fgets(buf, sizeof (buf), stdin);
+ if (tolower(*buf) != 'y')
+ goto loop;
+ }
+
+ obj = res->objects.objects_val;
+ rres = nis_remove(fname, obj);
+ if ((rres->status == NIS_PERMISSION) && force_remove) {
+ obj->zo_access |= 0x08080808;
+ nis_freeresult(rres);
+ rres = nis_modify(fname, obj);
+ if (rres->status == NIS_SUCCESS) {
+ nis_freeresult(rres);
+ rres = nis_remove(fname, NULL);
+ }
+ }
+ if (rres->status != NIS_SUCCESS) {
+ if (!force_remove) {
+ nis_perror(rres->status, name);
+ error = 1;
+ }
+ }
+ nis_freeresult(rres);
+
+ loop:
+ nis_freeresult(res);
+ }
+
+ exit(error);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nisrmdir.c b/usr/src/cmd/rpcsvc/nis/utils/nisrmdir.c
new file mode 100644
index 0000000000..698bfb2f78
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nisrmdir.c
@@ -0,0 +1,433 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 1988-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * nisrmdir.c
+ *
+ * nis+ dir remove utility
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/nis.h>
+#include <netdb.h>
+#include <signal.h>
+#include <netdir.h>
+#include <netconfig.h>
+#include <sys/socket.h>
+#include <rpcsvc/nis.h>
+#include <rpcsvc/nis_dhext.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define ROOT_OBJ "root.object"
+extern nis_name __nis_local_root();
+
+char fname[NIS_MAXNAMELEN];
+nis_object *obj;
+int nserv;
+nis_server *servers;
+int errcode = 0;
+
+static
+int
+match_host(char *host, char *target)
+{
+ int len = strlen(host);
+
+ if (strncasecmp(host, target, len) == 0 &&
+ (target[len] == '.' || target[len] == '\0'))
+ return (1);
+
+ return (0);
+}
+
+void
+cleanup_rmdir()
+{
+ int ns, i;
+
+ /*
+ * put any servers that weren't removed (non-nil name) back into the
+ * directory.
+ */
+ for (ns = 0, i = 0; i < nserv; i++) {
+ if (servers[i].name) {
+ if (ns == i)
+ ns++;
+ else
+ servers[ns++] = servers[i];
+ }
+ }
+
+ if (ns) {
+ obj->DI_data.do_servers.do_servers_len = ns;
+ (void) nis_add(fname, obj);
+ }
+
+ exit(1);
+}
+
+nis_server sserv;
+int sservi;
+
+void
+cleanup_rmslave()
+{
+ nis_result *res;
+
+ /*
+ * put the slave back in the directory.
+ */
+ res = nis_lookup(fname, MASTER_ONLY);
+ if (res->status == NIS_SUCCESS) {
+ obj->zo_oid = NIS_RES_OBJECT(res)[0].zo_oid;
+ obj->DI_data.do_servers.do_servers_len++;
+ servers[sservi] = sserv;
+ (void) nis_modify(fname, obj);
+ }
+
+ exit(1);
+}
+
+/*
+ * remove_directory is a special rmdir that takes care of the root case.
+ * This is needed only for the -s option when the host or directory object
+ * specified in the removal no longer exists. Otherwise,
+ * the nis_remove operation on the directory object would result in
+ * the replicas receiving pings to remove the root object in the root case.
+ */
+nis_error
+remove_directory(nis_name directory, nis_server* server, int verbose)
+{
+ nis_error s = nis_rmdir(directory, server);
+
+ if (s != NIS_SUCCESS) {
+ if (verbose)
+ fprintf(stderr, "cannot remove replica \"%s\": %s.\n",
+ server->name, nis_sperrno(s));
+ return (s);
+ }
+ /* root replica: send ping to remove root object. */
+ if (nis_dir_cmp(__nis_local_root(), directory) == SAME_NAME) {
+ __nis_pingproc(server, ROOT_OBJ, time(0));
+ }
+
+ return (s);
+}
+
+#define OP_RMDIR 0
+#define OP_RMSLAVE 2
+
+void
+usage()
+{
+ fprintf(stderr, "usage: nisrmdir [-if] [-s hostname] dirname\n");
+ exit(1);
+}
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+ char ask_remove = 0, force_remove = 0;
+ int op = OP_RMDIR;
+ ulong_t expand;
+ char buf[BUFSIZ];
+ char *host = 0;
+ char *name;
+ nis_result *res, *rres, *mres;
+ nis_error s;
+ int i, nur, found;
+ int bad_name;
+ nis_server *sservp;
+
+ while ((c = getopt(argc, argv, "ifs:")) != -1) {
+ switch (c) {
+ case 'i':
+ ask_remove = 1;
+ break;
+ case 'f':
+ force_remove = 1;
+ break;
+ case 's':
+ op = OP_RMSLAVE;
+ host = optarg;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (argc - optind != 1)
+ usage();
+
+ name = argv[optind];
+
+ if (name[strlen(name)-1] != '.')
+ expand = EXPAND_NAME;
+ else
+ expand = 0;
+
+ /*
+ * Get the directory object.
+ */
+ res = nis_lookup(name, expand|MASTER_ONLY);
+ if (res->status != NIS_SUCCESS) {
+ if (force_remove) {
+ /*
+ * If specifying host, maybe trying to clean up
+ * directory that has already been removed.
+ */
+ if (host && (res->status == NIS_NOTFOUND ||
+ res->status == NIS_NOSUCHNAME)) {
+ sservp = __nis_host2nis_server_g(host,
+ FALSE, TRUE, &errcode);
+
+ if (sservp == NULL) {
+ nis_perror(errcode, host);
+ exit(1);
+ }
+
+ sserv = *sservp;
+ if (expand == 0)
+ bad_name = (strlcpy(fname, name,
+ sizeof (fname)) >=
+ sizeof (fname));
+ else
+ bad_name = (snprintf(fname,
+ sizeof (fname),
+ "%s.%s", name,
+ nis_local_directory()) >=
+ sizeof (fname));
+ if (bad_name) {
+ nis_perror(NIS_BADNAME, name);
+ exit(1);
+ }
+
+ s = remove_directory(fname, &sserv, 0);
+ }
+ exit(0);
+ }
+ nis_perror(res->status, name);
+ exit(1);
+ }
+
+ bad_name = (snprintf(fname, sizeof (fname), "%s.",
+ res->objects.objects_val[0].zo_name) >= sizeof (fname));
+ if (!bad_name && *(res->objects.objects_val[0].zo_domain) != '.')
+ bad_name = strlcat(fname, res->objects.objects_val[0].zo_domain,
+ sizeof (fname)) >= sizeof (fname);
+
+ if (bad_name) {
+ nis_perror(NIS_BADNAME, fname);
+ exit(1);
+ }
+ if (res->objects.objects_val[0].zo_data.zo_type != NIS_DIRECTORY_OBJ) {
+ fprintf(stderr, "%s is not a directory!\n", fname);
+ exit(1);
+ }
+
+ if (ask_remove || expand) {
+ printf("remove \"%s\"? ", fname);
+ *buf = '\0';
+ (void) fgets(buf, sizeof (buf), stdin);
+ if (tolower(*buf) != 'y')
+ exit(0);
+ }
+
+ obj = &(NIS_RES_OBJECT(res)[0]);
+ nserv = obj->DI_data.do_servers.do_servers_len;
+ servers = obj->DI_data.do_servers.do_servers_val;
+
+ switch (op) {
+ case OP_RMDIR:
+ /*
+ * remove directory object
+ */
+ rres = nis_remove(fname, 0);
+ if ((rres->status == NIS_PERMISSION) && force_remove) {
+ obj->zo_access |= 0x08080808;
+ nis_freeresult(rres);
+ rres = nis_modify(fname, obj);
+ if (rres->status == NIS_SUCCESS) {
+ nis_freeresult(rres);
+ rres = nis_remove(fname, 0);
+ }
+ }
+ if (rres->status != NIS_SUCCESS) {
+ if (force_remove)
+ exit(0);
+ nis_perror(rres->status, "can't remove directory");
+ exit(1);
+ }
+
+ /*
+ * fork a child and *try* to do nis_rmdirs. this may take
+ * a while since we may try to talk to servers that aren't
+ * up/responding.
+ */
+ if (force_remove)
+ switch (fork()) {
+ case 0:
+ for (i = nserv-1; i >= 0; i--)
+ nis_rmdir(fname, &(servers[i]));
+ default:
+ exit(0);
+ }
+
+ signal(SIGINT, (void(*)(int))cleanup_rmdir);
+
+ /*
+ * remove slave directories
+ */
+ for (nur = 0, i = 1; i < nserv; i++) {
+ s = nis_rmdir(fname, &(servers[i]));
+ if (s != NIS_SUCCESS) {
+ nur++;
+ fprintf(stderr,
+ "cannot remove replica \"%s\": %s.\n",
+ servers[i].name, nis_sperrno(s));
+ } else
+ servers[i].name = 0;
+ }
+ if (nur)
+ cleanup_rmdir();
+
+ /*
+ * if all slave directories were removed, remove master
+ */
+ s = nis_rmdir(fname, &(servers[0]));
+ if (s != NIS_SUCCESS) {
+ fprintf(stderr, "cannot remove master \"%s\": %s.\n",
+ servers[0].name, nis_sperrno(s));
+ cleanup_rmdir();
+ }
+
+ exit(0);
+
+ case OP_RMSLAVE:
+ /*
+ * find the slave.
+ */
+ for (found = -1, i = 0; i < nserv; i++) {
+ if (match_host(host, servers[i].name)) {
+ if (found >= 0) {
+ fprintf(stderr,
+ "\"%s\" is not unique, please use full host name.\n",
+ host);
+ exit(1);
+ }
+ found = i;
+ }
+ }
+
+ /*
+ * Host no longer listed as a replica. Try anyhow; perhaps
+ * user trying to clean up.
+ */
+ if (found == -1) {
+ if (!force_remove) {
+ fprintf(stderr,
+ "Host \"%s\" is not listed as a replica for \"%s\".\n",
+ host, fname);
+ fprintf(stderr,
+ "Use the -fs option to attempt rmdir anyhow.\n");
+ exit(1);
+ }
+
+ sservp = __nis_host2nis_server_g(host, FALSE,
+ TRUE, &errcode);
+ if (sservp == NULL) {
+ nis_perror(errcode, host);
+ exit(1);
+ }
+ s = remove_directory(fname, sservp, 0);
+ exit(0);
+ }
+
+ /* Host named is master */
+ if (found == 0) {
+ if (force_remove)
+ exit(0);
+ fprintf(stderr, "\"%s\" is master for \"%s\"!\n",
+ servers[0].name, fname);
+ exit(1);
+ }
+ sserv = servers[found];
+ sservi = found;
+
+ /*
+ * remove slave from the directory object.
+ */
+ nserv = --(obj->DI_data.do_servers.do_servers_len);
+ if (found < nserv) {
+ servers[found] = servers[nserv];
+ }
+ mres = nis_modify(fname, obj);
+ if (mres->status != NIS_SUCCESS) {
+ if (force_remove)
+ exit(0);
+ fprintf(stderr, "cannot remove replica \"%s\": %s.\n",
+ sserv.name, nis_sperrno(mres->status));
+ exit(1);
+ }
+
+ /*
+ * fork a child and *try* to do nis_rmdir. this may take
+ * a while since we may try to talk to servers that aren't
+ * up/responding.
+ */
+ if (force_remove)
+ switch (fork()) {
+ case 0:
+ nis_rmdir(fname, &sserv);
+ default:
+ exit(0);
+ }
+
+ signal(SIGINT, (void(*)(int))cleanup_rmslave);
+
+ /*
+ * remove the actual directory.
+ */
+ s = nis_rmdir(fname, &sserv);
+ if (s != NIS_SUCCESS) {
+ fprintf(stderr, "cannot remove replica \"%s\": %s.\n",
+ sserv.name, nis_sperrno(s));
+ cleanup_rmslave();
+ exit(1);
+ }
+ exit(0);
+ }
+}
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nistbladm.c b/usr/src/cmd/rpcsvc/nis/utils/nistbladm.c
new file mode 100644
index 0000000000..e25d8244cf
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nistbladm.c
@@ -0,0 +1,722 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * nistbladm.c
+ *
+ * Copyright (c) 1988-2000 Sun Microsystems Inc
+ * All Rights Reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * nistbladm.c
+ *
+ * nis+ table admin utility
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <malloc.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/nis.h>
+
+extern int optind;
+extern char *optarg;
+
+extern nis_object nis_default_obj;
+
+extern char *nisname_index();
+
+
+char *DEFAULT_PATH = "";
+char *DEFAULT_SEP = " ";
+
+#define OP_CREATE 1
+#define OP_UPDATE 2
+#define OP_DESTROY 3
+#define OP_ADD 4
+#define OP_MODIFY 5
+#define OP_REMOVE 6
+
+/* change defines */
+#define CHG_PATH 1
+#define CHG_SEP 2
+#define CHG_ACCESS 4
+#define CHG_TYPE 5
+
+struct buf {
+ char *s;
+ int len;
+ int alloc;
+};
+typedef struct buf buf;
+
+static
+void
+buf_init(buf *b)
+{
+ b->len = 0;
+ b->alloc = 100; /* start with a fair amount of room */
+ b->s = (char *)malloc(b->alloc);
+ if (b->s == NULL) {
+ fprintf(stderr, "buf_init: out of memory\n");
+ exit(1);
+ }
+}
+
+static
+void
+buf_add(buf *b, char c)
+{
+ if (b->len >= b->alloc) {
+ b->alloc += 25; /* make room for extra chars */
+ b->s = (char *)realloc((void *)b->s, b->alloc);
+ if (b->s == NULL) {
+ fprintf(stderr, "buf_check: out of memory\n");
+ exit(1);
+ }
+ }
+ b->s[b->len] = c;
+ b->len += 1;
+}
+
+static
+void
+buf_cat(buf *b, char *src)
+{
+ while (*src)
+ buf_add(b, *src++);
+}
+
+/*
+ * This function is used to copy a "colname=value" string into another
+ * string that will be passed to nis_get_request. Any unquoted ","
+ * or "]" characters are quoted so that they won't be interpreted as
+ * the end of the indexed name.
+ */
+static
+void
+buf_cat_quote(buf *b, char *src)
+{
+ /*
+ * Process each character in src. If it is a quote, copy
+ * characters until reaching the terminating quote. If it
+ * is a ',' or a ']', then quote it into dest. All other
+ * characters are copied "as is".
+ */
+ while (*src) {
+ if (*src == '"') {
+ buf_add(b, *src++);
+ while (*src && *src != '"')
+ buf_add(b, *src++);
+ if (*src)
+ buf_add(b, *src++);
+ } else if (*src == ',' || *src == ']') {
+ buf_add(b, '"');
+ buf_add(b, *src++);
+ buf_add(b, '"');
+ } else {
+ buf_add(b, *src++);
+ }
+ }
+}
+
+static
+void
+buf_cat_n(buf *b, char *s, int n)
+{
+ int i;
+
+ for (i = 0; i < n && *s; i++) {
+ buf_add(b, *s++);
+ }
+}
+
+static
+char *
+buf_value(buf *b)
+{
+ char *p;
+
+ p = malloc(b->len + 1);
+ if (p == NULL) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ memcpy(p, b->s, b->len);
+ p[b->len] = 0;
+ return (p);
+}
+
+
+void
+usage()
+{
+ fprintf(stderr,
+ "usage:\tnistbladm [-D defaults] -c [-p path] [-s sep] type\n");
+ fprintf(stderr, "\t\tcolname=[flags][,access] ... tablename\n");
+ fprintf(stderr,
+"\tnistbladm -u [-p path] [-s sep] [-t type] [colname=access ...] tablename\n");
+ fprintf(stderr, "\tnistbladm -d tablename\n");
+ fprintf(stderr,
+ "\tnistbladm [-D defaults] -a|A colname=val ... tablename\n");
+ fprintf(stderr, "\tnistbladm [-D defaults] -a|A indexedname\n");
+ fprintf(stderr, "\tnistbladm -e|E colname=val ... indexedname\n");
+ fprintf(stderr, "\tnistbladm -m colname=val ... indexedname\n");
+ fprintf(stderr, "\tnistbladm -r|R [colname=val ...] tablename\n");
+ fprintf(stderr, "\tnistbladm -r|R indexedname\n");
+ exit(1);
+}
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+ int len;
+ char *defstr = 0;
+ int op = 0;
+ unsigned flags = 0;
+ char *name;
+ nis_result *res, *ares, *mres, *rres;
+ nis_object *obj, tobj, eobj;
+ char *ta_type = 0, *ta_path = DEFAULT_PATH, *ta_sep = DEFAULT_SEP;
+ int ta_maxcol, i, j;
+ char *p, *p1;
+ table_col *tcol, *ocol;
+ char *mpred;
+ char *spred;
+ char *sname;
+ char *tname;
+ buf mpred_buf;
+ buf spred_buf;
+ nis_error s;
+ ib_request ibr;
+ entry_col *ecol;
+ int nrights = 0;
+ char **colname;
+ char **rights;
+
+ while ((c = getopt(argc, argv, "D:cudaAeEmrRp:s:t:")) != -1) {
+ switch (c) {
+ case 'D':
+ defstr = optarg;
+ break;
+ case 'c':
+ if (op)
+ usage();
+ op = OP_CREATE;
+ break;
+ case 'u':
+ if (op)
+ usage();
+ op = OP_UPDATE;
+ break;
+ case 'd':
+ if (op)
+ usage();
+ op = OP_DESTROY;
+ break;
+ case 'A':
+ flags |= ADD_OVERWRITE;
+ case 'a':
+ if (op)
+ usage();
+ op = OP_ADD;
+ break;
+ case 'e':
+ flags |= MOD_EXCLUSIVE;
+ case 'm':
+ case 'E':
+ if (op)
+ usage();
+ op = OP_MODIFY;
+ break;
+ case 'R':
+ flags |= REM_MULTIPLE;
+ case 'r':
+ if (op)
+ usage();
+ op = OP_REMOVE;
+ break;
+ case 'p':
+ ta_path = optarg;
+ break;
+ case 's':
+ ta_sep = optarg;
+ if (strlen(optarg) != 1) {
+ fprintf(stderr,
+ "separator must be a single character\n");
+ exit(1);
+ }
+ break;
+ case 't':
+ ta_type = optarg;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (!op)
+ usage();
+
+ if (!nis_defaults_init(defstr))
+ exit(1);
+
+
+ buf_init(&mpred_buf);
+ buf_init(&spred_buf);
+
+ switch (op) {
+ case OP_CREATE:
+ if (argc - optind < 3)
+ usage();
+ ta_type = argv[optind++];
+ ta_maxcol = argc - optind - 1;
+
+ /*
+ * Most standard tables are created by nissetup, so they
+ * get the correct number of columns. However, automount_map
+ * tables are an exception, and are often added by the admin.
+ * Thus we check that they have exactly two columns, in order
+ * to avoid confusing applications like automountd.
+ */
+ if (!strcasecmp(ta_type, "automount_map") && ta_maxcol != 2) {
+ fprintf(stderr,
+ "automount_map type nis+ tables must have exactly "
+ "two columns.\n");
+ exit(1);
+ }
+
+ if ((tcol = (table_col*)malloc(ta_maxcol*sizeof (table_col)))
+ == 0) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ for (i = 0; i < ta_maxcol; i++) {
+ p = tcol[i].tc_name = argv[optind++];
+ if ((p = strchr(p, '=')) == 0)
+ usage();
+ *(p++) = 0;
+ if (*p) {
+ p1 = strchr(p, ',');
+ if (p1)
+ *(p1++) = 0;
+ } else
+ p1 = 0;
+ tcol[i].tc_flags = 0;
+ if (!parse_flags(&(tcol[i].tc_flags), p))
+ usage();
+ tcol[i].tc_rights = nis_default_obj.zo_access;
+ if (!parse_rights(&(tcol[i].tc_rights), p1))
+ usage();
+ }
+
+ name = argv[optind];
+ if (name[strlen(name)-1] != '.') {
+ fprintf(stderr, "tablename must be fully qualified.\n");
+ exit(1);
+ }
+
+ tobj = nis_default_obj;
+ tobj.zo_data.zo_type = NIS_TABLE_OBJ;
+ tobj.TA_data.ta_type = ta_type;
+ tobj.TA_data.ta_maxcol = ta_maxcol;
+ tobj.TA_data.ta_sep = *ta_sep;
+ tobj.TA_data.ta_path = ta_path;
+ tobj.TA_data.ta_cols.ta_cols_len = ta_maxcol;
+ tobj.TA_data.ta_cols.ta_cols_val = tcol;
+
+ ares = nis_add(name, &tobj);
+ if (ares->status != NIS_SUCCESS) {
+ nis_perror(ares->status, "can't create table");
+ exit(1);
+ }
+ exit(0);
+
+ case OP_UPDATE:
+ if (argc <= optind)
+ usage();
+ if (argc - optind > 1) {
+ nrights = argc - optind - 1;
+ colname = (char **)malloc(nrights * sizeof (char *));
+ rights = (char **)malloc(nrights * sizeof (char *));
+ if (colname == 0 || rights == 0) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+
+ for (i = 0; i < nrights; i++) {
+ p = strchr(argv[optind], '=');
+ if (p == 0) {
+ fprintf(stderr,
+ "Missing access rights for "
+ "\"%s\"\n", argv[optind]);
+ exit(1);
+ }
+ *p++ = 0;
+ colname[i] = strdup(argv[optind]);
+ rights[i] = strdup(p);
+ if (colname[i] == 0 || rights[i] == 0) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+
+ optind++;
+ }
+ }
+
+ name = argv[optind];
+ if (*name == '[')
+ usage();
+ break;
+
+ case OP_DESTROY:
+ if (argc - optind < 1)
+ usage();
+ name = argv[optind];
+ break;
+
+ case OP_ADD:
+ case OP_REMOVE:
+ if (argc - optind < 1)
+ usage();
+ p = argv[optind++];
+ if (*p == '[') {
+ name = nisname_index(p, ']');
+ if (name == 0)
+ usage();
+ name++;
+ buf_cat_n(&spred_buf, p, name-p);
+ if (*name == ',')
+ name++;
+ } else if (argc - optind < 1) {
+ if (op == OP_ADD)
+ usage();
+ name = p;
+ } else {
+ buf_cat_quote(&spred_buf, p);
+ while (argc - optind > 1) {
+ buf_cat(&spred_buf, ",");
+ buf_cat_quote(&spred_buf, argv[optind++]);
+ }
+ name = argv[optind++];
+ if (*name == '[')
+ usage();
+ }
+ spred = buf_value(&spred_buf);
+ break;
+
+ case OP_MODIFY:
+ if (argc - optind < 2)
+ usage();
+ buf_cat_quote(&mpred_buf, argv[optind++]);
+ while (argc - optind > 1) {
+ buf_cat(&mpred_buf, ",");
+ buf_cat_quote(&mpred_buf, argv[optind++]);
+ }
+ mpred = buf_value(&mpred_buf);
+
+ p = argv[optind++];
+ if (*p != '[')
+ usage();
+ name = nisname_index(p, ']');
+ if (name == 0)
+ usage();
+ name++;
+ buf_cat_n(&spred_buf, p, name-p);
+ if (*name == ',')
+ name++;
+ spred = buf_value(&spred_buf);
+ break;
+ }
+
+ /*
+ * Get the table object.
+ */
+ res = nis_lookup(name, MASTER_ONLY|FOLLOW_LINKS|EXPAND_NAME);
+ if (res->status != NIS_SUCCESS) {
+ nis_perror(res->status, name);
+ exit(1);
+ }
+
+ obj = &(NIS_RES_OBJECT(res)[0]);
+ len = strlen(res->objects.objects_val[0].zo_name) +
+ strlen(res->objects.objects_val[0].zo_domain) +
+ strlen(".") + 1;
+ tname = (char *)malloc(len);
+ if (tname == NULL) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ sprintf(tname, "%s.", res->objects.objects_val[0].zo_name);
+ if (*(res->objects.objects_val[0].zo_domain) != '.')
+ strcat(tname, res->objects.objects_val[0].zo_domain);
+
+ if (obj->zo_data.zo_type != NIS_TABLE_OBJ) {
+ fprintf(stderr, "\"%s\" is not a table!\n", name);
+ exit(1);
+ }
+
+ ta_maxcol = obj->TA_data.ta_maxcol;
+
+ switch (op) {
+ case OP_UPDATE:
+
+ /* Now we morph the table object, first start with orig */
+ tobj = *obj;
+
+ /* Change its type if desired */
+ if (ta_type)
+ tobj.TA_data.ta_type = ta_type;
+
+ /* Change the separator if desired */
+ if (ta_sep != DEFAULT_SEP)
+ tobj.TA_data.ta_sep = *ta_sep;
+
+ /* Change the path if desired */
+ if (ta_path != DEFAULT_PATH)
+ tobj.TA_data.ta_path = ta_path;
+
+ /* Allocate some column descriptors */
+ tcol = (table_col *)calloc(ta_maxcol, sizeof (table_col));
+ if (tcol == 0) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+
+ /* Set the new table to have these malloc'd columns */
+ tobj.TA_data.ta_cols.ta_cols_val = tcol;
+ ocol = obj->TA_data.ta_cols.ta_cols_val;
+
+ /* check the table schema */
+ for (i = 0; i < nrights; ++i) {
+ for (j = 0; j < ta_maxcol; ++j)
+ if (strcmp(ocol[j].tc_name, colname[i]) == 0)
+ break;
+ if (j == ta_maxcol) {
+ fprintf(stderr,
+ "Invalid table schema: \"%s\" is an invalid "
+ "column.\n", colname[i]);
+ exit(1);
+ }
+ }
+
+ for (j = 0; j < ta_maxcol; j++) {
+
+ /* Copy the old column into the new column */
+ tcol[j] = ocol[j];
+
+ /* Check to see if new access rights were specified */
+ for (i = 0; i < nrights; i++) {
+
+ /* If so change them appropriately */
+ if (strcmp(ocol[j].tc_name, colname[i]) == 0) {
+ if (! parse_rights(&(tcol[j].tc_rights),
+ rights[i])) {
+ fprintf(stderr,
+ "Couldn't parse access rights (%s) for column \"%s\"\n",
+ rights[i],
+ colname[i]);
+ exit(1);
+ } else {
+ tcol[j].tc_flags |= TA_MODIFIED;
+ break;
+ } /* parsed correctly */
+ } /* same name as index */
+ } /* for all indices */
+ } /* for all columns */
+
+ mres = nis_modify(tname, &tobj);
+ if (mres->status != NIS_SUCCESS) {
+ nis_perror(mres->status, "can't modify table");
+ exit(1);
+ }
+ exit(0);
+
+ case OP_DESTROY:
+ rres = nis_remove(tname, 0);
+ if (rres->status != NIS_SUCCESS) {
+ nis_perror(rres->status, "can't remove table");
+ exit(1);
+ }
+ exit(0);
+
+ case OP_ADD:
+ len = strlen(spred) + strlen(tname) + strlen("[]") + 1;
+ sname = (char *)malloc(len);
+ if (sname == NULL) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ if (*spred == '[')
+ sprintf(sname, "%s%s", spred, tname);
+ else
+ sprintf(sname, "[%s]%s", spred, tname);
+
+ /*
+ * Parse search criteria.
+ */
+ s = nis_get_request(sname, 0, 0, &ibr);
+ if (s != NIS_SUCCESS) {
+ nis_perror(s, "can't parse column values");
+ exit(1);
+ }
+
+ eobj = nis_default_obj;
+ if (eobj.zo_ttl > obj->zo_ttl)
+ eobj.zo_ttl = obj->zo_ttl;
+ eobj.zo_data.zo_type = NIS_ENTRY_OBJ;
+ eobj.EN_data.en_type = obj->TA_data.ta_type;
+
+ if ((ecol = (entry_col*)malloc(ta_maxcol*sizeof (entry_col)))
+ == 0) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ memset((char *)ecol, 0, ta_maxcol*sizeof (entry_col));
+ eobj.EN_data.en_cols.en_cols_len = ta_maxcol;
+ eobj.EN_data.en_cols.en_cols_val = ecol;
+
+ for (i = 0; i < ibr.ibr_srch.ibr_srch_len; i++) {
+ for (j = 0; j < ta_maxcol; j++)
+ if (strcmp(
+ obj->TA_data.ta_cols.ta_cols_val[j].tc_name,
+ ibr.ibr_srch.ibr_srch_val[i].zattr_ndx) == 0)
+ break;
+ if (j < ta_maxcol) {
+ ecol[j].ec_value.ec_value_len =
+ ibr.ibr_srch.ibr_srch_val[i].zattr_val.zattr_val_len;
+ ecol[j].ec_value.ec_value_val =
+ ibr.ibr_srch.ibr_srch_val[i].zattr_val.zattr_val_val;
+ ecol[j].ec_flags =
+ obj->TA_data.ta_cols.ta_cols_val[j].tc_flags &
+ (TA_BINARY|TA_CRYPT|TA_XDR|TA_ASN1);
+ } else {
+ fprintf(stderr,
+ "table has no column named \"%s\"\n",
+ ibr.ibr_srch.ibr_srch_val[i].zattr_ndx);
+ exit(1);
+ }
+ }
+
+ ares = nis_add_entry(tname, &eobj, flags);
+ if (ares->status != NIS_SUCCESS) {
+ nis_perror(ares->status, "can't add entry");
+ exit(1);
+ }
+ break;
+
+ case OP_MODIFY:
+ /*
+ * Parse modify criteria.
+ */
+ len = strlen(mpred) + strlen(tname) + strlen("[]") + 1;
+ sname = (char *)malloc(len);
+ if (sname == NULL) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ sprintf(sname, "[%s]%s", mpred, tname);
+ s = nis_get_request(sname, 0, 0, &ibr);
+ if (s != NIS_SUCCESS) {
+ nis_perror(s, "can't parse column values");
+ exit(1);
+ }
+
+ memset((char *)&eobj, 0, sizeof (eobj));
+ eobj.zo_data.zo_type = NIS_ENTRY_OBJ;
+ eobj.EN_data.en_type = obj->TA_data.ta_type;
+
+ if ((ecol = (entry_col*)malloc(ta_maxcol*sizeof (entry_col)))
+ == 0) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ memset((char *)ecol, 0, ta_maxcol*sizeof (entry_col));
+ eobj.EN_data.en_cols.en_cols_len = ta_maxcol;
+ eobj.EN_data.en_cols.en_cols_val = ecol;
+
+ for (i = 0; i < ibr.ibr_srch.ibr_srch_len; i++) {
+ for (j = 0; j < ta_maxcol; j++)
+ if (strcmp(
+ obj->TA_data.ta_cols.ta_cols_val[j].tc_name,
+ ibr.ibr_srch.ibr_srch_val[i].zattr_ndx) == 0)
+ break;
+ if (j < ta_maxcol) {
+ ecol[j].ec_value.ec_value_len =
+ ibr.ibr_srch.ibr_srch_val[i].zattr_val.zattr_val_len;
+ ecol[j].ec_value.ec_value_val =
+ ibr.ibr_srch.ibr_srch_val[i].zattr_val.zattr_val_val;
+ ecol[j].ec_flags =
+ (obj->TA_data.ta_cols.ta_cols_val[j].tc_flags &
+ (TA_BINARY|TA_CRYPT|TA_XDR|TA_ASN1)) |
+ EN_MODIFIED;
+ } else {
+ fprintf(stderr,
+ "table has no column named \"%s\"\n",
+ ibr.ibr_srch.ibr_srch_val[i].zattr_ndx);
+ exit(1);
+ }
+ }
+
+ free(sname); /* old value no longer needed */
+
+ len = strlen(spred) + strlen(tname) + 1;
+ sname = (char *)malloc(len);
+ if (sname == NULL) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ sprintf(sname, "%s%s", spred, tname);
+
+ mres = nis_modify_entry(sname, &eobj, flags);
+ if (mres->status != NIS_SUCCESS) {
+ nis_perror(mres->status, "can't modify entry");
+ exit(1);
+ }
+ break;
+
+ case OP_REMOVE:
+ len = strlen(spred) + strlen(tname) + strlen("[]") + 1;
+ sname = (char *)malloc(len);
+ if (sname == NULL) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ if (*spred == '[')
+ sprintf(sname, "%s%s", spred, tname);
+ else
+ sprintf(sname, "[%s]%s", spred, tname);
+
+ rres = nis_remove_entry(sname, 0, flags);
+ if (rres->status != NIS_SUCCESS) {
+ nis_perror(rres->status, "can't remove entry");
+ exit(1);
+ }
+ break;
+ }
+
+ exit(0);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nistest.c b/usr/src/cmd/rpcsvc/nis/utils/nistest.c
new file mode 100644
index 0000000000..b2f77ea4b4
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nistest.c
@@ -0,0 +1,320 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+/*
+ * nistest.c
+ *
+ * Copyright (c) 1988-1992 Sun Microsystems Inc
+ * All Rights Reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * nistest.c
+ *
+ * nis+ object test utility
+ */
+
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/nis.h>
+
+extern int optind;
+extern char *optarg;
+
+
+#define EXIT_TRUE 0
+#define EXIT_FALSE 1
+#define EXIT_ERROR 2
+
+#define CMP_LT 0
+#define CMP_LE 1
+#define CMP_EQ 2
+#define CMP_HT 3
+#define CMP_HE 4
+#define CMP_NE 5
+#define CMP_NS 6
+
+struct cmp_key {
+ char *key;
+ int value;
+};
+
+struct cmp_key cmp[] = {
+ "<", CMP_LT,
+ "lt", CMP_LT,
+ "<=", CMP_LE,
+ "le", CMP_LE,
+ "=", CMP_EQ,
+ "eq", CMP_EQ,
+ ">", CMP_HT,
+ "ht", CMP_HT,
+ "gt", CMP_HT,
+ ">=", CMP_HE,
+ "he", CMP_HE,
+ "ge", CMP_HE,
+ "!=", CMP_NE,
+ "ne", CMP_NE,
+ "ns", CMP_NS,
+ NULL, 0,
+};
+
+void
+usage()
+{
+ fprintf(stderr,
+ "usage: nistest [-LPAM] [-t G|D|L|T|E|P] [-a mode] name\n");
+ fprintf(stderr,
+ " nistest -c [dir op dir]\n");
+ exit(EXIT_ERROR);
+}
+
+#define TEST_TYPE 1
+#define TEST_ACCESS 2
+#define TEST_CMP 3
+
+unsigned int flags = 0;
+zotypes otype;
+u_long oaccess;
+int dotest_called = 0;
+
+static
+void
+cmp_list()
+{
+ struct cmp_key *p;
+
+ printf("Op\tMeaning\n");
+ printf("----\t-------\n");
+ for (p = cmp; p->key; p++) {
+ printf("%s\t", p->key);
+ switch (p->value) {
+ case CMP_LT:
+ printf("lower than");
+ break;
+ case CMP_LE:
+ printf("lower than or equal");
+ break;
+ case CMP_EQ:
+ printf("equal");
+ break;
+ case CMP_HT:
+ printf("higher than");
+ break;
+ case CMP_HE:
+ printf("higher than or equal");
+ break;
+ case CMP_NE:
+ printf("not equal");
+ break;
+ case CMP_NS:
+ printf("not sequential");
+ break;
+ }
+ printf("\n");
+ }
+}
+
+static
+int
+cmp_test(char *d1, char *op, char *d2)
+{
+ int ret;
+ enum name_pos st;
+ struct cmp_key *p;
+
+ for (p = cmp; p->key; p++) {
+ if (strcasecmp(p->key, op) == 0)
+ break;
+ }
+ if (p->key == NULL) {
+ fprintf(stderr, "nistest: bad op\n");
+ return (EXIT_ERROR);
+ }
+
+ st = nis_dir_cmp(d1, d2);
+ if (st == BAD_NAME) {
+ fprintf(stderr, "nistest: bad name\n");
+ return (EXIT_ERROR);
+ }
+
+ switch (p->value) {
+ case CMP_LT:
+ ret = (st == LOWER_NAME);
+ break;
+ case CMP_LE:
+ ret = (st == LOWER_NAME || st == SAME_NAME);
+ break;
+ case CMP_EQ:
+ ret = (st == SAME_NAME);
+ break;
+ case CMP_HT:
+ ret = (st == HIGHER_NAME);
+ break;
+ case CMP_HE:
+ ret = (st == HIGHER_NAME || st == SAME_NAME);
+ break;
+ case CMP_NE:
+ ret = !(st == SAME_NAME);
+ break;
+ case CMP_NS:
+ ret = (st == NOT_SEQUENTIAL);
+ break;
+ }
+
+ if (ret)
+ return (EXIT_TRUE);
+ return (EXIT_FALSE);
+}
+
+int
+dotest(tab, obj, udata)
+ char *tab;
+ nis_object *obj;
+ void *udata;
+{
+ if ((flags & TEST_TYPE) &&
+ (obj->zo_data.zo_type != otype))
+ exit(EXIT_FALSE);
+
+ if ((flags & TEST_ACCESS) &&
+ ((obj->zo_access & oaccess) != oaccess))
+ exit(EXIT_FALSE);
+
+ dotest_called = 1;
+}
+
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+ u_long flinks = 0, fpath = 0, allres = 0, master = 0;
+ char *name;
+ nis_result *ores;
+
+ while ((c = getopt(argc, argv, "LPAMt:a:c")) != -1)
+ switch (c) {
+ case 'L':
+ flinks = FOLLOW_LINKS;
+ break;
+ case 'P':
+ fpath = FOLLOW_PATH;
+ break;
+ case 'A':
+ allres = ALL_RESULTS;
+ break;
+ case 'M':
+ master = MASTER_ONLY;
+ break;
+ case 't':
+ flags |= TEST_TYPE;
+ switch (*optarg) {
+ case 'G':
+ otype = NIS_GROUP_OBJ;
+ break;
+ case 'D':
+ otype = NIS_DIRECTORY_OBJ;
+ break;
+ case 'T':
+ otype = NIS_TABLE_OBJ;
+ break;
+ case 'L':
+ otype = NIS_LINK_OBJ;
+ break;
+ case 'E':
+ otype = NIS_ENTRY_OBJ;
+ break;
+ case 'P':
+ otype = NIS_PRIVATE_OBJ;
+ break;
+ default:
+ usage();
+ break;
+ }
+ break;
+ case 'a':
+ flags |= TEST_ACCESS;
+ oaccess = 0;
+ if (!parse_rights(&oaccess, optarg))
+ usage();
+ break;
+ case 'c':
+ if (flags == TEST_CMP) {
+ fprintf(stderr, "%s: %s\n",
+ "nistest",
+ "-c can only be specified once");
+ exit(EXIT_ERROR);
+ } else if (flags != 0) {
+ fprintf(stderr, "%s: %s\n",
+ "nistest",
+ "-c cannot be combined with other tests");
+ exit(EXIT_ERROR);
+ }
+ flags = TEST_CMP;
+ break;
+ default:
+ usage();
+ }
+
+ if (flags == TEST_CMP) {
+ switch (argc - optind) {
+ case 0:
+ cmp_list();
+ exit(EXIT_TRUE);
+ break;
+ case 3:
+ exit(cmp_test(argv[optind],
+ argv[optind+1], argv[optind+2]));
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if (argc - optind != 1)
+ usage();
+
+ name = argv[optind];
+
+ /*
+ * Get the object using expand name magic, and test it.
+ */
+ if (*name == '[') {
+ ores = nis_list(name,
+ fpath|allres|master|FOLLOW_LINKS|EXPAND_NAME, dotest, 0);
+ if (ores->status != NIS_CBRESULTS)
+ exit(EXIT_FALSE);
+ } else {
+ ores = nis_lookup(name, flinks|master|EXPAND_NAME);
+ if (ores->status != NIS_SUCCESS)
+ exit(EXIT_FALSE);
+ dotest(0, ores->objects.objects_val, 0);
+ }
+
+ if (dotest_called)
+ exit(EXIT_TRUE);
+
+ exit(EXIT_FALSE);
+}
diff --git a/usr/src/cmd/rpcsvc/nis/utils/req.flg b/usr/src/cmd/rpcsvc/nis/utils/req.flg
new file mode 100644
index 0000000000..9841aaddb9
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/req.flg
@@ -0,0 +1,49 @@
+#!/bin/sh
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+
+echo_file usr/src/lib/libnsl/include/rpcsvc/nis_dhext.h
+echo_file usr/src/lib/libnisdb/nisdb_ldap.h
+echo_file usr/src/lib/libnisdb/ldap_attr.h
+echo_file usr/src/lib/libnisdb/ldap_map.h
+echo_file usr/src/lib/libnisdb/ldap_nisdbquery.h
+echo_file usr/src/lib/libnisdb/ldap_nisplus.h
+echo_file usr/src/lib/libnisdb/ldap_op.h
+echo_file usr/src/lib/libnisdb/ldap_parse.h
+echo_file usr/src/lib/libnisdb/ldap_print.h
+echo_file usr/src/lib/libnisdb/ldap_ruleval.h
+echo_file usr/src/lib/libnisdb/ldap_structs.h
+echo_file usr/src/lib/libnisdb/ldap_util.h
+echo_file usr/src/lib/libnisdb/ldap_xdr.h
+echo_file usr/src/lib/libnisdb/nis_hashitem.h
+echo_file usr/src/lib/libnisdb/nis_ldap.h
+echo_file usr/src/lib/libnisdb/nis_parse_ldap_conf.h
+echo_file usr/src/lib/libnisdb/nis_servlist.h
+echo_file usr/src/lib/libnisdb/nisdb_ldap.h
+echo_file usr/src/lib/libnisdb/nisdb_mt.h
+echo_file usr/src/lib/libnisdb/nisdb_rw.h
diff --git a/usr/src/cmd/rpcsvc/nisplus b/usr/src/cmd/rpcsvc/nisplus
new file mode 100644
index 0000000000..d6e804bf82
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nisplus
@@ -0,0 +1,80 @@
+#!/sbin/sh
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+. /lib/svc/share/smf_include.sh
+
+cachemgr=0
+if [ -f /var/nis/NIS_COLD_START ]; then
+ cache=`/usr/bin/svcprop -p application_ovr/clear_cache $SMF_FMRI \
+ 2>/dev/null`
+ if [ $? != 0 ]; then
+ cache=`/usr/bin/svcprop -p application/clear_cache $SMF_FMRI \
+ 2>/dev/null`
+ fi
+
+ [ "$cache" = "true" ] && cachemgr_flags="$cachemgr_flags -i"
+
+ /usr/sbin/nis_cachemgr $cachemgr_flags || exit $?
+ cachemgr=1
+fi
+
+[ -z "$_INIT_UTS_NODENAME" ] && \
+ _INIT_UTS_NODENAME=`/usr/bin/uname -n`
+
+hostname=`echo "$_INIT_UTS_NODENAME" | /usr/bin/cut -d. -f1 | \
+ /usr/bin/tr '[A-Z]' '[a-z]'`
+
+nisd=0
+if [ -d /var/nis/data -o -d /var/nis/$hostname ]; then
+ emulate=`/usr/bin/svcprop -p application/emulate_yp $SMF_FMRI \
+ 2>/dev/null`
+ [ $? = 0 ] && [ "$emulate" = "true" ] && nisd_flags="$nisd_flags -Y"
+
+ security=`/usr/bin/svcprop -p application/security $SMF_FMRI \
+ 2>/dev/null`
+ [ $? = 0 ] && nisd_flags="$nisd_flags -S $security"
+
+ /usr/sbin/rpc.nisd $nisd_flags || exit $?
+ nisd=1
+
+ # We always start the NIS+ Password Update Daemon. If it
+ # finds the NIS+ server is not a Master it will just exit.
+ # It also determines if the server is running in NIS (YP)
+ # compat mode and automatically registers a yppasswdd so
+ # NIS (YP) clients can change their passwords.
+
+ /usr/sbin/rpc.nispasswdd || exit $?
+fi
+
+if [ "$cachemgr" = 0 -a "$nisd" = 0 ]; then
+ # If we're neither able to be a client or a server, then
+ # we are misconfigured.
+ exit $SMF_EXIT_ERR_CONFIG
+fi
+
+exit $SMF_EXIT_OK
diff --git a/usr/src/cmd/rpcsvc/nisplus.xml b/usr/src/cmd/rpcsvc/nisplus.xml
new file mode 100644
index 0000000000..ff29f5afe5
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nisplus.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ 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
+ or http://www.opensolaris.org/os/licensing.
+ 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
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+-->
+
+<service_bundle type='manifest' name='SUNWnisr:nisplus'>
+
+<service
+ name='network/rpc/nisplus'
+ type='service'
+ version='1'>
+
+ <dependency
+ name='keyserv'
+ grouping='require_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/network/rpc/keyserv' />
+ </dependency>
+
+ <exec_method
+ type='method'
+ name='start'
+ exec='/lib/svc/method/nisplus'
+ timeout_seconds='60' />
+
+ <exec_method
+ type='method'
+ name='stop'
+ exec=':kill'
+ timeout_seconds='60' />
+
+ <instance name='default' enabled='false'>
+ <property_group name='application' type='application'>
+ <stability value='Unstable' />
+ <propval name='emulate_yp' type='boolean'
+ value='false' />
+ </property_group>
+ </instance>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ NIS+
+ </loctext>
+ </common_name>
+ <documentation>
+ <manpage title='rpc.nisd' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/rpcsvc/rpc.bootparamd/Makefile b/usr/src/cmd/rpcsvc/rpc.bootparamd/Makefile
new file mode 100644
index 0000000000..28abf8b2ce
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/rpc.bootparamd/Makefile
@@ -0,0 +1,63 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/rpcsvc/rpc.bootparamd/Makefile
+
+PROG= rpc.bootparamd
+MANIFEST= bootparams.xml
+
+OBJS= bootparam_ip_route.o bootparam_prot_svc.o \
+ bootparam_prot_xdr.o bootparam_subr.o
+
+include ../Makefile.rpc
+
+CPPFLAGS= -I. $(CPPFLAGS.master)
+CFLAGS += -DSYSV
+LDLIBS += -lsocket -lnsl
+
+SRCS= $(OBJS:%.o=%.c)
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+# multi-object targets
+
+rpc.bootparamd: $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+install: all $(ROOTUSRSBINPROG) $(ROOTMANIFEST)
+
+check: $(CHKMANIFEST)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+include ../../Makefile.targ
diff --git a/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_ip_route.c b/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_ip_route.c
new file mode 100644
index 0000000000..f011e17d38
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_ip_route.c
@@ -0,0 +1,523 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 1991-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#include <sys/tihdr.h>
+#include <sys/tiuser.h>
+#include <sys/timod.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <inet/common.h>
+#include <inet/mib2.h>
+#include <inet/ip.h>
+#include <netinet/igmp_var.h>
+#include <netinet/ip_mroute.h>
+
+#include <arpa/inet.h>
+
+#include <netdb.h>
+#include <nss_dbdefs.h>
+#include <fcntl.h>
+#include <stropts.h>
+
+#include "bootparam_private.h"
+
+typedef struct mib_item_s {
+ struct mib_item_s *next_item;
+ long group;
+ long mib_id;
+ long length;
+ char *valp;
+} mib_item_t;
+
+static void free_itemlist(mib_item_t *);
+
+static mib_item_t *
+mibget(int sd)
+{
+ char buf[512];
+ int flags;
+ int i, j, getcode;
+ struct strbuf ctlbuf, databuf;
+ struct T_optmgmt_req *tor = (struct T_optmgmt_req *)(void *)buf;
+ struct T_optmgmt_ack *toa = (struct T_optmgmt_ack *)(void *)buf;
+ struct T_error_ack *tea = (struct T_error_ack *)(void *)buf;
+ struct opthdr *req;
+ mib_item_t *first_item = nilp(mib_item_t);
+ mib_item_t *last_item = nilp(mib_item_t);
+ mib_item_t *temp;
+
+ tor->PRIM_type = T_SVR4_OPTMGMT_REQ;
+ tor->OPT_offset = sizeof (struct T_optmgmt_req);
+ tor->OPT_length = sizeof (struct opthdr);
+ tor->MGMT_flags = T_CURRENT;
+ req = (struct opthdr *)&tor[1];
+ req->level = MIB2_IP; /* any MIB2_xxx value ok here */
+ req->name = 0;
+ req->len = 0;
+
+ ctlbuf.buf = buf;
+ ctlbuf.len = tor->OPT_length + tor->OPT_offset;
+ flags = 0;
+ if (putmsg(sd, &ctlbuf, nilp(struct strbuf), flags) == -1) {
+ perror("mibget: putmsg(ctl) failed");
+ goto error_exit;
+ }
+ /*
+ * each reply consists of a ctl part for one fixed structure
+ * or table, as defined in mib2.h. The format is a T_OPTMGMT_ACK,
+ * containing an opthdr structure. level/name identify the entry,
+ * len is the size of the data part of the message.
+ */
+ req = (struct opthdr *)&toa[1];
+ ctlbuf.maxlen = sizeof (buf);
+ for (j = 1; ; j++) {
+ flags = 0;
+ getcode = getmsg(sd, &ctlbuf, nilp(struct strbuf), &flags);
+ if (getcode == -1) {
+ perror("mibget getmsg(ctl) failed");
+ if (debug) {
+ msgout("# level name len");
+ i = 0;
+ for (last_item = first_item; last_item;
+ last_item = last_item->next_item)
+ msgout("%d %4ld %5ld %ld", ++i,
+ last_item->group,
+ last_item->mib_id,
+ last_item->length);
+ }
+ goto error_exit;
+ }
+ if ((getcode == 0) &&
+ (ctlbuf.len >= sizeof (struct T_optmgmt_ack))&&
+ (toa->PRIM_type == T_OPTMGMT_ACK) &&
+ (toa->MGMT_flags == T_SUCCESS) &&
+ (req->len == 0)) {
+ if (debug)
+ msgout("mibget getmsg() %d returned EOD "
+ "(level %lu, name %lu)",
+ j, req->level, req->name);
+ return (first_item); /* this is EOD msg */
+ }
+
+ if (ctlbuf.len >= sizeof (struct T_error_ack) &&
+ tea->PRIM_type == T_ERROR_ACK) {
+ msgout("mibget %d gives T_ERROR_ACK: "
+ "TLI_error = 0x%lx, UNIX_error = 0x%lx",
+ j, tea->TLI_error, tea->UNIX_error);
+ errno = (tea->TLI_error == TSYSERR)
+ ? tea->UNIX_error : EPROTO;
+ goto error_exit;
+ }
+
+ if (getcode != MOREDATA ||
+ ctlbuf.len < sizeof (struct T_optmgmt_ack) ||
+ toa->PRIM_type != T_OPTMGMT_ACK ||
+ toa->MGMT_flags != T_SUCCESS) {
+ msgout("mibget getmsg(ctl) %d returned %d, "
+ "ctlbuf.len = %d, PRIM_type = %ld",
+ j, getcode, ctlbuf.len, toa->PRIM_type);
+ if (toa->PRIM_type == T_OPTMGMT_ACK)
+ msgout("T_OPTMGMT_ACK: MGMT_flags = 0x%lx, "
+ "req->len = %lu",
+ toa->MGMT_flags, req->len);
+ errno = ENOMSG;
+ goto error_exit;
+ }
+
+ temp = (mib_item_t *)malloc(sizeof (mib_item_t));
+ if (!temp) {
+ perror("mibget malloc failed");
+ goto error_exit;
+ }
+ if (last_item)
+ last_item->next_item = temp;
+ else
+ first_item = temp;
+ last_item = temp;
+ last_item->next_item = nilp(mib_item_t);
+ last_item->group = req->level;
+ last_item->mib_id = req->name;
+ last_item->length = req->len;
+ last_item->valp = (char *)malloc(req->len);
+ if (debug)
+ msgout(
+ "msg %d: group = %4ld mib_id = %5ld length = %ld",
+ j, last_item->group, last_item->mib_id,
+ last_item->length);
+
+ databuf.maxlen = last_item->length;
+ databuf.buf = last_item->valp;
+ databuf.len = 0;
+ flags = 0;
+ getcode = getmsg(sd, nilp(struct strbuf), &databuf, &flags);
+ if (getcode == -1) {
+ perror("mibget getmsg(data) failed");
+ goto error_exit;
+ } else if (getcode != 0) {
+ msgout("xmibget getmsg(data) returned %d, "
+ "databuf.maxlen = %d, databuf.len = %d",
+ getcode, databuf.maxlen, databuf.len);
+ goto error_exit;
+ }
+ }
+
+error_exit:
+ free_itemlist(first_item);
+ return (NULL);
+}
+
+static void
+free_itemlist(mib_item_t *item_list)
+{
+ mib_item_t *item;
+
+ while (item_list) {
+ item = item_list;
+ item_list = item->next_item;
+ if (item->valp)
+ free(item->valp);
+ free(item);
+ }
+}
+
+/*
+ * If we are a router, return address of interface closest to client.
+ * If we are not a router, look through our routing table and return
+ * address of "best" router that is on same net as client.
+ *
+ * We expect the router flag to show up first, followed by interface
+ * addr group, followed by the routing table.
+ */
+
+in_addr_t
+get_ip_route(struct in_addr client_addr)
+{
+ boolean_t found;
+ mib_item_t *item_list;
+ mib_item_t *item;
+ int sd;
+ mib2_ip_t *mip;
+ mib2_ipAddrEntry_t *map;
+ mib2_ipRouteEntry_t *rp;
+ int ip_forwarding = 2; /* off */
+ /* mask of interface used to route to client and best_router */
+ struct in_addr interface_mask;
+ /* address of interface used to route to client and best_router */
+ struct in_addr interface_addr;
+ /* address of "best router"; i.e. the answer */
+ struct in_addr best_router;
+
+ interface_mask.s_addr = 0L;
+ interface_addr.s_addr = 0L;
+ best_router.s_addr = 0L;
+
+ /* open a stream to IP */
+ sd = open("/dev/ip", O_RDWR);
+ if (sd == -1) {
+ perror("ip open");
+ (void) close(sd);
+ msgout("can't open mib stream");
+ return (0);
+ }
+
+ /* send down a request and suck up all the mib info from IP */
+ if ((item_list = mibget(sd)) == nilp(mib_item_t)) {
+ msgout("mibget() failed");
+ (void) close(sd);
+ return (0);
+ }
+
+ /*
+ * We make three passes through the list of collected IP mib
+ * information. First we figure out if we are a router. Next,
+ * we find which of our interfaces is on the same subnet as
+ * the client. Third, we paw through our own routing table
+ * looking for a useful router address.
+ */
+
+ /*
+ * The general IP group.
+ */
+ for (item = item_list; item; item = item->next_item) {
+ if ((item->group == MIB2_IP) && (item->mib_id == 0)) {
+ /* are we an IP router? */
+ mip = (mib2_ip_t *)(void *)item->valp;
+ ip_forwarding = mip->ipForwarding;
+ break;
+ }
+ }
+
+ /*
+ * The interface group.
+ */
+ for (item = item_list, found = B_FALSE; item != NULL && !found;
+ item = item->next_item) {
+ if ((item->group == MIB2_IP) && (item->mib_id == MIB2_IP_20)) {
+ /*
+ * Try to find out which interface is up, configured,
+ * not loopback, and on the same subnet as the client.
+ * Save its address and netmask.
+ */
+ map = (mib2_ipAddrEntry_t *)(void *)item->valp;
+ while ((char *)map < item->valp + item->length) {
+ in_addr_t addr, mask, net;
+ int ifflags;
+
+ ifflags = map->ipAdEntInfo.ae_flags;
+ addr = map->ipAdEntAddr;
+ mask = map->ipAdEntNetMask;
+ net = addr & mask;
+
+ if ((ifflags & IFF_LOOPBACK | IFF_UP) ==
+ IFF_UP && addr != INADDR_ANY &&
+ net == (client_addr.s_addr & mask)) {
+ interface_addr.s_addr = addr;
+ interface_mask.s_addr = mask;
+ found = B_TRUE;
+ break;
+ }
+ map++;
+ }
+ }
+ }
+
+ /*
+ * If this exercise found no interface on the same subnet as
+ * the client, then we can't suggest any router address to
+ * use.
+ */
+ if (interface_addr.s_addr == 0) {
+ if (debug)
+ msgout("get_ip_route: no interface on same net "
+ "as client");
+ (void) close(sd);
+ free_itemlist(item_list);
+ return (0);
+ }
+
+ /*
+ * If we are a router, we return to client the address of our
+ * interface on the same net as the client.
+ */
+ if (ip_forwarding == 1) {
+ if (debug)
+ msgout("get_ip_route: returning local addr %s",
+ inet_ntoa(interface_addr));
+ (void) close(sd);
+ free_itemlist(item_list);
+ return (interface_addr.s_addr);
+ }
+
+ if (debug) {
+ msgout("interface_addr = %s.", inet_ntoa(interface_addr));
+ msgout("interface_mask = %s", inet_ntoa(interface_mask));
+ }
+
+
+ /*
+ * The routing table group.
+ */
+ for (item = item_list; item; item = item->next_item) {
+ if ((item->group == MIB2_IP) && (item->mib_id == MIB2_IP_21)) {
+ if (debug)
+ msgout("%lu records for ipRouteEntryTable",
+ item->length /
+ sizeof (mib2_ipRouteEntry_t));
+
+ for (rp = (mib2_ipRouteEntry_t *)(void *)item->valp;
+ (char *)rp < item->valp + item->length;
+ rp++) {
+ if (debug >= 2)
+ msgout("ire_type = %d, next_hop = 0x%x",
+ rp->ipRouteInfo.re_ire_type,
+ rp->ipRouteNextHop);
+
+ /*
+ * We are only interested in real
+ * gateway routes.
+ */
+ if ((rp->ipRouteInfo.re_ire_type !=
+ IRE_DEFAULT) &&
+ (rp->ipRouteInfo.re_ire_type !=
+ IRE_PREFIX) &&
+ (rp->ipRouteInfo.re_ire_type !=
+ IRE_HOST) &&
+ (rp->ipRouteInfo.re_ire_type !=
+ IRE_HOST_REDIRECT))
+ continue;
+
+ /*
+ * We are only interested in routes with
+ * a next hop on the same subnet as
+ * the client.
+ */
+ if ((rp->ipRouteNextHop &
+ interface_mask.s_addr) !=
+ (interface_addr.s_addr &
+ interface_mask.s_addr))
+ continue;
+
+ /*
+ * We have a valid route. Give preference
+ * to default routes.
+ */
+ if ((rp->ipRouteDest == 0) ||
+ (best_router.s_addr == 0))
+ best_router.s_addr =
+ rp->ipRouteNextHop;
+ }
+ }
+ }
+
+ if (debug && (best_router.s_addr == 0))
+ msgout("get_ip_route: no route found for client");
+
+ (void) close(sd);
+ free_itemlist(item_list);
+ return (best_router.s_addr);
+}
+
+/*
+ * Return address of server interface closest to client.
+ *
+ * If the server has only a single IP address return it. Otherwise check
+ * if the server has an interface on the same subnet as the client and
+ * return the address of that interface.
+ */
+
+in_addr_t
+find_best_server_int(char **addr_list, char *client_name)
+{
+ in_addr_t server_addr = 0;
+ struct hostent h, *hp;
+ char hbuf[NSS_BUFLEN_HOSTS];
+ int err;
+ struct in_addr client_addr;
+ mib_item_t *item_list;
+ mib_item_t *item;
+ int sd;
+ mib2_ipAddrEntry_t *map;
+ in_addr_t client_net = 0, client_mask = 0;
+ boolean_t found_client_int;
+
+ (void) memcpy(&server_addr, addr_list[0], sizeof (in_addr_t));
+ if (addr_list[1] == NULL)
+ return (server_addr);
+
+ hp = gethostbyname_r(client_name, &h, hbuf, sizeof (hbuf), &err);
+ if (hp == NULL)
+ return (server_addr);
+ (void) memcpy(&client_addr, hp->h_addr_list[0], sizeof (client_addr));
+
+ /* open a stream to IP */
+ sd = open("/dev/ip", O_RDWR);
+ if (sd == -1) {
+ perror("ip open");
+ (void) close(sd);
+ msgout("can't open mib stream");
+ return (server_addr);
+ }
+
+ /* send down a request and suck up all the mib info from IP */
+ if ((item_list = mibget(sd)) == nilp(mib_item_t)) {
+ msgout("mibget() failed");
+ (void) close(sd);
+ return (server_addr);
+ }
+ (void) close(sd);
+
+ /*
+ * Search through the list for our interface which is on the same
+ * subnet as the client and get the netmask.
+ */
+ for (item = item_list, found_client_int = B_FALSE;
+ item != NULL && !found_client_int; item = item->next_item) {
+ if ((item->group == MIB2_IP) && (item->mib_id == MIB2_IP_20)) {
+ /*
+ * Try to find out which interface is up, configured,
+ * not loopback, and on the same subnet as the client.
+ * Save its address and netmask.
+ */
+ map = (mib2_ipAddrEntry_t *)(void *)item->valp;
+ while ((char *)map < item->valp + item->length) {
+ in_addr_t addr, mask, net;
+ int ifflags;
+
+ ifflags = map->ipAdEntInfo.ae_flags;
+ addr = map->ipAdEntAddr;
+ mask = map->ipAdEntNetMask;
+ net = addr & mask;
+
+ if ((ifflags & IFF_LOOPBACK|IFF_UP) == IFF_UP &&
+ addr != INADDR_ANY &&
+ (client_addr.s_addr & mask) == net) {
+ client_net = net;
+ client_mask = mask;
+ found_client_int = B_TRUE;
+ break;
+ }
+ map++;
+ }
+ }
+ }
+
+ /*
+ * If we found the interface check which is the best IP address.
+ */
+ if (found_client_int) {
+ while (*addr_list != NULL) {
+ in_addr_t addr;
+
+ (void) memcpy(&addr, *addr_list, sizeof (in_addr_t));
+ if ((addr & client_mask) == client_net) {
+ server_addr = addr;
+ break;
+ }
+ addr_list++;
+ }
+ }
+
+ if (debug && server_addr == 0)
+ msgout("No usable interface for returning reply");
+
+ free_itemlist(item_list);
+ return (server_addr);
+}
diff --git a/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_private.h b/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_private.h
new file mode 100644
index 0000000000..4a5dcb00ab
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_private.h
@@ -0,0 +1,46 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2001-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _BOOTPARAM_PRIVATE_H
+#define _BOOTPARAM_PRIVATE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int debug;
+
+extern void msgout(char *, ...);
+extern in_addr_t get_ip_route(struct in_addr);
+extern in_addr_t find_best_server_int(char **, char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _BOOTPARAM_PRIVATE_H */
diff --git a/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_prot_svc.c b/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_prot_svc.c
new file mode 100644
index 0000000000..b4095c5fe2
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_prot_svc.c
@@ -0,0 +1,261 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 1998-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+#include <rpc/rpc.h>
+#include <memory.h>
+#include <stropts.h>
+#include <netconfig.h>
+#include <stropts.h>
+#include <sys/termios.h>
+#include <syslog.h>
+#include <rpcsvc/bootparam_prot.h>
+
+#include "bootparam_private.h"
+
+#define _RPCSVC_CLOSEDOWN 120
+
+int debug = 0;
+
+static void bootparamprog_1(struct svc_req *, register SVCXPRT *);
+static void closedown(int);
+
+static int server_child = 0; /* program was started by another server */
+static int _rpcsvcdirty; /* Still serving ? */
+
+int
+main(int argc, char *argv[])
+{
+ pid_t pid;
+ int c;
+ char *progname = argv[0];
+ int connmaxrec = RPC_MAXDATASIZE;
+
+ while ((c = getopt(argc, argv, "d")) != -1)
+ switch ((char)c) {
+ case 'd':
+ debug++;
+ break;
+ default:
+ (void) fprintf(stderr, "usage: %s [-d]\n", progname);
+ exit(EXIT_FAILURE);
+ }
+
+
+ /*
+ * Set non-blocking mode and maximum record size for
+ * connection oriented RPC transports.
+ */
+ if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &connmaxrec)) {
+ msgout("unable to set maximum RPC record size");
+ }
+
+ /*
+ * If stdin looks like a TLI endpoint, we assume
+ * that we were started by a port monitor. If
+ * t_getstate fails with TBADF, this is not a
+ * TLI endpoint.
+ */
+ if (t_getstate(0) != -1 || t_errno != TBADF) {
+ char *netid;
+ struct netconfig *nconf = NULL;
+ SVCXPRT *transp;
+ int pmclose;
+
+ if ((netid = getenv("NLSPROVIDER")) == NULL) {
+ if (debug)
+ msgout("cannot get transport name");
+ } else if ((nconf = getnetconfigent(netid)) == NULL) {
+ if (debug)
+ msgout("cannot get transport info");
+ }
+ pmclose = (t_getstate(0) != T_DATAXFER);
+ if ((transp = svc_tli_create(0, nconf, NULL, 0, 0)) == NULL) {
+ msgout("cannot create server handle");
+ exit(EXIT_FAILURE);
+ }
+ if (nconf)
+ freenetconfigent(nconf);
+ if (!svc_reg(transp, BOOTPARAMPROG, BOOTPARAMVERS,
+ bootparamprog_1, 0)) {
+ msgout("unable to register (BOOTPARAMPROG, "
+ "BOOTPARAMVERS).");
+ exit(EXIT_FAILURE);
+ }
+ if (pmclose) {
+ (void) signal(SIGALRM, closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN);
+ }
+
+ svc_run();
+ exit(EXIT_FAILURE);
+ /* NOTREACHED */
+ }
+
+ /*
+ * run this process in the background only if it was started from
+ * a shell and the debug flag was not given.
+ */
+ if (!server_child && !debug) {
+ pid = fork();
+ if (pid < 0) {
+ perror("cannot fork");
+ exit(EXIT_FAILURE);
+ }
+ if (pid)
+ exit(EXIT_SUCCESS);
+
+ closefrom(0);
+ (void) setsid();
+ }
+
+ /*
+ * messges go to syslog if the program was started by
+ * another server, or if it was run from the command line without
+ * the debug flag.
+ */
+ if (server_child || !debug)
+ openlog("bootparam_prot", LOG_PID, LOG_DAEMON);
+
+ if (debug) {
+ if (debug == 1)
+ msgout("in debug mode.");
+ else
+ msgout("in debug mode (level %d).", debug);
+ }
+
+ if (!svc_create(bootparamprog_1, BOOTPARAMPROG, BOOTPARAMVERS,
+ "netpath")) {
+ msgout("unable to create (BOOTPARAMPROG, BOOTPARAMVERS) "
+ "for netpath.");
+ exit(EXIT_FAILURE);
+ }
+
+ svc_run();
+ msgout("svc_run returned");
+ return (EXIT_FAILURE);
+}
+
+static void
+bootparamprog_1(struct svc_req *rqstp, register SVCXPRT *transp)
+{
+ union {
+ bp_whoami_arg bootparamproc_whoami_1_arg;
+ bp_getfile_arg bootparamproc_getfile_1_arg;
+ } argument;
+ char *result;
+ bool_t (*xdr_argument)(), (*xdr_result)();
+ char *(*local)();
+
+ _rpcsvcdirty = 1;
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ (void) svc_sendreply(transp, xdr_void, (char *)NULL);
+ _rpcsvcdirty = 0;
+ return;
+
+ case BOOTPARAMPROC_WHOAMI:
+ xdr_argument = xdr_bp_whoami_arg;
+ xdr_result = xdr_bp_whoami_res;
+ local = (char *(*)()) bootparamproc_whoami_1;
+ break;
+
+ case BOOTPARAMPROC_GETFILE:
+ xdr_argument = xdr_bp_getfile_arg;
+ xdr_result = xdr_bp_getfile_res;
+ local = (char *(*)()) bootparamproc_getfile_1;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ _rpcsvcdirty = 0;
+ return;
+ }
+ (void) memset((char *)&argument, 0, sizeof (argument));
+ if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) {
+ svcerr_decode(transp);
+ _rpcsvcdirty = 0;
+ return;
+ }
+ result = (*local)(&argument, rqstp);
+ if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument)) {
+ msgout("unable to free arguments");
+ exit(EXIT_FAILURE);
+ }
+ _rpcsvcdirty = 0;
+}
+
+/*PRINTFLIKE1*/
+void
+msgout(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ /*
+ * messges go to syslog if the program was started by
+ * another server, or if it was run from the command line without
+ * the debug flag.
+ */
+ if (server_child || !debug)
+ vsyslog(LOG_ERR, fmt, ap);
+ else {
+ (void) vfprintf(stderr, fmt, ap);
+ (void) fputc('\n', stderr);
+ }
+ va_end(ap);
+}
+
+/* ARGSUSED */
+static void
+closedown(int sig)
+{
+ if (_rpcsvcdirty == 0) {
+ int size;
+ int i, openfd;
+ struct t_info tinfo;
+
+ if (!t_getinfo(0, &tinfo) && (tinfo.servtype == T_CLTS))
+ exit(EXIT_SUCCESS);
+ size = svc_max_pollfd;
+ for (i = 0, openfd = 0; i < size && openfd < 2; i++)
+ if (svc_pollfd[i].fd >= 0)
+ openfd++;
+ if (openfd <= 1)
+ exit(EXIT_SUCCESS);
+ }
+ (void) alarm(_RPCSVC_CLOSEDOWN);
+}
diff --git a/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_prot_xdr.c b/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_prot_xdr.c
new file mode 100644
index 0000000000..0c9ba4da70
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_prot_xdr.c
@@ -0,0 +1,151 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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) 1999,2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+
+#include <rpc/rpc.h>
+#include <rpcsvc/bootparam_prot.h>
+
+/* LINTLIBRARY */
+
+bool_t
+xdr_bp_machine_name_t(XDR *xdrs, bp_machine_name_t *objp)
+{
+ if (!xdr_string(xdrs, objp, MAX_MACHINE_NAME)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_bp_path_t(XDR *xdrs, bp_path_t *objp)
+{
+ if (!xdr_string(xdrs, objp, MAX_PATH_LEN)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_bp_fileid_t(XDR *xdrs, bp_fileid_t *objp)
+{
+ if (!xdr_string(xdrs, objp, MAX_FILEID)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_ip_addr_t(XDR *xdrs, ip_addr_t *objp)
+{
+ if (!xdr_char(xdrs, &objp->net)) {
+ return (FALSE);
+ }
+ if (!xdr_char(xdrs, &objp->host)) {
+ return (FALSE);
+ }
+ if (!xdr_char(xdrs, &objp->lh)) {
+ return (FALSE);
+ }
+ if (!xdr_char(xdrs, &objp->impno)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_bp_address(XDR *xdrs, bp_address *objp)
+{
+ if (!xdr_int(xdrs, &objp->address_type)) {
+ return (FALSE);
+ }
+ switch (objp->address_type) {
+ case IP_ADDR_TYPE:
+ if (!xdr_ip_addr_t(xdrs, &objp->bp_address_u.ip_addr)) {
+ return (FALSE);
+ }
+ break;
+ default:
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_bp_whoami_arg(XDR *xdrs, bp_whoami_arg *objp)
+{
+ if (!xdr_bp_address(xdrs, &objp->client_address)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_bp_whoami_res(XDR *xdrs, bp_whoami_res *objp)
+{
+ if (!xdr_bp_machine_name_t(xdrs, &objp->client_name)) {
+ return (FALSE);
+ }
+ if (!xdr_bp_machine_name_t(xdrs, &objp->domain_name)) {
+ return (FALSE);
+ }
+ if (!xdr_bp_address(xdrs, &objp->router_address)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_bp_getfile_arg(XDR *xdrs, bp_getfile_arg *objp)
+{
+ if (!xdr_bp_machine_name_t(xdrs, &objp->client_name)) {
+ return (FALSE);
+ }
+ if (!xdr_bp_fileid_t(xdrs, &objp->file_id)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_bp_getfile_res(XDR *xdrs, bp_getfile_res *objp)
+{
+ if (!xdr_bp_machine_name_t(xdrs, &objp->server_name)) {
+ return (FALSE);
+ }
+ if (!xdr_bp_address(xdrs, &objp->server_address)) {
+ return (FALSE);
+ }
+ if (!xdr_bp_path_t(xdrs, &objp->server_path)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
diff --git a/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_subr.c b/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_subr.c
new file mode 100644
index 0000000000..9a9dd01af3
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_subr.c
@@ -0,0 +1,299 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 1999-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Subroutines that implement the bootparam services.
+ */
+
+#include <rpcsvc/bootparam_prot.h>
+#include <netdb.h>
+#include <nlist.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <nsswitch.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#define KERNEL /* to get RTHASHSIZ */
+#include <sys/stream.h>
+#include <net/route.h>
+#undef KERNEL
+#include <net/if.h> /* for structs ifnet and ifaddr */
+#include <netinet/in.h>
+#include <netinet/in_var.h> /* for struct in_ifaddr */
+#include <arpa/inet.h>
+
+#include "bootparam_private.h"
+
+#define LINESIZE 1024
+
+extern int getdomainname(char *, int);
+extern int bootparams_getbyname(char *, char *, int);
+
+static char *wildcard = "*";
+static char domainkey[] = "domain=";
+static void getf_printres(bp_getfile_res *);
+static void copydomain(char *, char *, int);
+
+/*
+ * Whoami turns a client address into a client name
+ * and suggested route machine.
+ */
+/*ARGSUSED1*/
+bp_whoami_res *
+bootparamproc_whoami_1(bp_whoami_arg *argp, CLIENT *cl)
+{
+ static bp_whoami_res res;
+ struct in_addr clnt_addr;
+ struct in_addr route_addr;
+ struct hostent *hp;
+ static char clnt_entry[LINESIZE];
+ static char domain[MAX_MACHINE_NAME];
+ char *cp;
+
+ if (argp->client_address.address_type != IP_ADDR_TYPE) {
+ if (debug) {
+ msgout("Whoami failed: unknown address type %d",
+ argp->client_address.address_type);
+ }
+ return (NULL);
+ }
+ (void) memcpy(&clnt_addr, &argp->client_address.bp_address_u.ip_addr,
+ sizeof (clnt_addr));
+ hp = gethostbyaddr((char *)&clnt_addr, sizeof (clnt_addr), AF_INET);
+ if (hp == NULL) {
+ if (debug) {
+ msgout("Whoami failed: gethostbyaddr for %s.",
+ inet_ntoa(clnt_addr));
+ }
+ return (NULL);
+ }
+
+ /*
+ * We only answer requests from clients listed in the database.
+ */
+ if ((bootparams_getbyname(hp->h_name, clnt_entry,
+ sizeof (clnt_entry)) != __NSW_SUCCESS) &&
+ (bootparams_getbyname(wildcard, clnt_entry,
+ sizeof (clnt_entry)) != __NSW_SUCCESS))
+ return (NULL);
+
+ res.client_name = hp->h_name;
+
+ /*
+ * The algorithm for determining the client's domain name is:
+ * 1) look for "domain=" in the client's bootparams line.
+ * If found, use its value.
+ * 2) look for a "domain=" entry in the wildcard bootparams
+ * line (if any). If found, use its value. Otherwise,
+ * 3) return the domain name of the server answering the
+ * request.
+ */
+ if (cp = strstr(clnt_entry, domainkey)) {
+ copydomain(cp + sizeof (domainkey) - 1, domain,
+ sizeof (domain));
+ } else {
+ /* "domain=" not found - try for wildcard */
+ if ((bootparams_getbyname(wildcard, clnt_entry,
+ sizeof (clnt_entry)) == __NSW_SUCCESS) &&
+ (cp = strstr(clnt_entry, domainkey))) {
+ copydomain(cp + sizeof (domainkey) - 1, domain,
+ sizeof (domain));
+ } else {
+ (void) getdomainname(domain, sizeof (domain));
+ }
+ }
+ res.domain_name = domain;
+
+ res.router_address.address_type = IP_ADDR_TYPE;
+ route_addr.s_addr = get_ip_route(clnt_addr);
+ (void) memcpy(&res.router_address.bp_address_u.ip_addr,
+ &route_addr,
+ sizeof (res.router_address.bp_address_u.ip_addr));
+
+ if (debug) {
+ struct in_addr in;
+
+ (void) memcpy(&in.s_addr,
+ &res.router_address.bp_address_u.ip_addr,
+ sizeof (in.s_addr));
+ msgout("Whoami returning name = %s, router address = %s",
+ res.client_name,
+ inet_ntoa(in));
+ }
+ return (&res);
+}
+
+/*
+ * Getfile gets the client name and the key and returns its server
+ * and the pathname for that key.
+ */
+/*ARGSUSED1*/
+bp_getfile_res *
+bootparamproc_getfile_1(bp_getfile_arg *argp, CLIENT *cl)
+{
+ static bp_getfile_res res;
+ static char clnt_entry[LINESIZE];
+ struct hostent *hp;
+ char *cp;
+ char filekey[LINESIZE];
+ char *server_hostname;
+ char *path_on_server;
+ int do_wildcard = 0;
+ static char *zero_len_string = "";
+
+ /*
+ * The bootparams_getbyname() library function looks up a
+ * "client entry" using using the client's hostname as the
+ * key. A client entry consists of a string of "file entries"
+ * separated by white space. Each file entry is of the form:
+ *
+ * file_key=server_hostname:path_on_server
+ *
+ * In the getfile RPC call, the client gives us his hostname
+ * and a file_key. We lookup his client entry, then locate a
+ * file entry matching that file_key. We then parse out the
+ * server_hostname and path_on_server from the file entry, map
+ * the server_hostname to an IP address, and return both the
+ * IP address and path_on_server back to the client.
+ */
+
+ /* make the client's file key int a string we can use for matching */
+ (void) strncpy(filekey, argp->file_id, sizeof (filekey) - 2);
+ filekey[sizeof (filekey) - 2] = '\0';
+ (void) strcat(filekey, "=");
+
+ if (bootparams_getbyname(argp->client_name, clnt_entry,
+ sizeof (clnt_entry)) == __NSW_SUCCESS) {
+ /* locate the file_key in the client's entry */
+ cp = strstr(clnt_entry, filekey);
+ if (cp == NULL)
+ do_wildcard++;
+
+ } else
+ do_wildcard++;
+
+ if (do_wildcard) {
+ if (bootparams_getbyname(wildcard, clnt_entry,
+ sizeof (clnt_entry)) != __NSW_SUCCESS)
+ return (NULL);
+
+ /* locate the file_key in the client's entry */
+ cp = strstr(clnt_entry, filekey);
+ if (cp == NULL)
+ return (NULL);
+ }
+
+ /* locate the "data" part of file entry (r.h.s. of "=") */
+ cp = strchr(cp, '=');
+ if (cp == NULL)
+ return (NULL);
+ cp++;
+ if (*cp == '\0')
+ return (NULL);
+ server_hostname = cp;
+
+ /* null-terminate server_hostname and parse path_on_server */
+ cp = strchr(server_hostname, ':');
+ if (cp == NULL)
+ return (NULL);
+ *cp = '\0';
+ cp++;
+ /* strtok() will null-terminate path_on_server */
+ path_on_server = strtok(cp, "\t\n ");
+ if (path_on_server == NULL)
+ path_on_server = zero_len_string;
+
+ res.server_name = server_hostname;
+ res.server_path = path_on_server;
+ if (*res.server_name == 0) {
+ res.server_address.address_type = IP_ADDR_TYPE;
+ (void) memset(&res.server_address.bp_address_u.ip_addr, 0,
+ sizeof (res.server_address.bp_address_u.ip_addr));
+ } else {
+ in_addr_t addr;
+
+ if ((hp = gethostbyname(server_hostname)) != NULL) {
+ addr = find_best_server_int(hp->h_addr_list,
+ argp->client_name);
+ } else {
+ addr = inet_addr(server_hostname);
+ if (addr == INADDR_BROADCAST) {
+ if (debug) {
+ msgout("getfile_1: gethostbyname(%s) "
+ "failed", res.server_name);
+ }
+ return (NULL);
+ }
+ }
+ res.server_address.address_type = IP_ADDR_TYPE;
+ (void) memcpy(&res.server_address.bp_address_u.ip_addr,
+ &addr, sizeof (res.server_address.bp_address_u.ip_addr));
+ }
+ if (debug) {
+ getf_printres(&res);
+ }
+ return (&res);
+}
+
+void
+getf_printres(bp_getfile_res *res)
+{
+ struct in_addr in;
+
+ (void) memcpy(&in.s_addr, &res->server_address.bp_address_u.ip_addr,
+ sizeof (in.s_addr));
+ msgout("getfile_1: file is \"%s\" %s \"%s\"",
+ res->server_name,
+ inet_ntoa(in),
+ res->server_path);
+}
+
+/*
+ * Used when we've found a "domain=" key, this function copies characters
+ * from source to target until we come upon either a NULL or whitespace is
+ * found in the source string, or we run out of room in the target.
+ *
+ */
+void
+copydomain(char *source, char *target, int len)
+{
+ int n; /* number of characters copies */;
+
+ len--; /* leave room for terminating '\0' */
+ if (source)
+ for (n = 0; *source != '\0' && n < len; n++)
+ if (isspace((int)*source))
+ break;
+ else
+ *target++ = *source++;
+
+ *target = '\0';
+}
diff --git a/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparams.xml b/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparams.xml
new file mode 100644
index 0000000000..d15b26e454
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparams.xml
@@ -0,0 +1,113 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ 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
+ or http://www.opensolaris.org/os/licensing.
+ 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
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+
+ Service manifest for the rpc.bootparamd service.
+-->
+
+<service_bundle type='manifest' name='SUNWbsr:bootparams'>
+
+<service
+ name='network/rpc/bootparams'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='false' />
+
+ <single_instance/>
+
+ <dependency
+ name='network'
+ grouping='require_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/milestone/network' />
+ </dependency>
+
+ <dependency
+ name='name-services'
+ grouping='require_all'
+ restart_on='refresh'
+ type='service'>
+ <service_fmri value='svc:/milestone/name-services' />
+ </dependency>
+
+ <dependency name='rpcbind'
+ grouping='require_all'
+ restart_on='restart'
+ type='service'>
+ <service_fmri value='svc:/network/rpc/bind'/>
+ </dependency>
+
+ <dependent
+ name='rpc-bootparams_multi-user-server'
+ grouping='optional_all'
+ restart_on='none'>
+ <service_fmri
+ value='svc:/milestone/multi-user-server' />
+ </dependent>
+
+ <exec_method
+ type='method'
+ name='start'
+ exec='/usr/sbin/rpc.bootparamd'
+ timeout_seconds='60' />
+
+ <exec_method
+ type='method'
+ name='stop'
+ exec=':kill'
+ timeout_seconds='60' />
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ boot parameter server
+ </loctext>
+ </common_name>
+
+ <description>
+ <loctext xml:lang='C'>
+rpc.bootparamd provides information from a bootparams database to
+diskless clients at boot time.
+ </loctext>
+ </description>
+ <documentation>
+ <manpage title='rpc.bootparamd' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/rpcsvc/rpc.rusersd.c b/usr/src/cmd/rpcsvc/rpc.rusersd.c
new file mode 100644
index 0000000000..190b76c6b2
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/rpc.rusersd.c
@@ -0,0 +1,484 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 1998 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <rpc/rpc.h>
+#include <memory.h>
+#include <netconfig.h>
+#include <stropts.h>
+#include <syslog.h>
+#include <utmpx.h>
+#include <rpcsvc/rusers.h>
+#include <sys/resource.h>
+#include <limits.h>
+
+#ifdef DEBUG
+#define RPC_SVC_FG
+#endif
+
+#define _RPCSVC_CLOSEDOWN 120
+
+static void rusers_service();
+static void closedown();
+static void msgout();
+static unsigned min();
+
+static int _rpcpmstart; /* Started by a port monitor ? */
+static int _rpcfdtype; /* Whether Stream or Datagram ? */
+static int _rpcsvcdirty; /* Still serving ? */
+static int _rpcsvcrecent; /* set when we serivce a request; tested */
+ /* and cleared by closedown() routine */
+
+#define DIV60(t) ((t+30)/60) /* x/60 rounded */
+
+#define ALL_ENTRIES 1
+#define REAL_USERS 0
+
+utmp_array utmp_array_res;
+int used_array_len = 0;
+struct utmpidlearr utmpidlearr;
+
+main()
+{
+ pid_t pid;
+ int i;
+ int connmaxrec = RPC_MAXDATASIZE;
+
+ /*
+ * Set non-blocking mode and maximum record size for
+ * connection oriented RPC transports.
+ */
+ if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &connmaxrec)) {
+ msgout("unable to set maximum RPC record size");
+ }
+
+ /*
+ * If stdin looks like a TLI endpoint, we assume
+ * that we were started by a port monitor. If
+ * t_getstate fails with TBADF, this is not a
+ * TLI endpoint.
+ */
+ if (t_getstate(0) != -1 || t_errno != TBADF) {
+ char *netid;
+ struct netconfig *nconf = NULL;
+ SVCXPRT *transp;
+ int pmclose;
+ extern char *getenv();
+
+ _rpcpmstart = 1;
+ openlog("rusers", LOG_PID, LOG_DAEMON);
+ if ((netid = getenv("NLSPROVIDER")) == NULL) {
+#ifdef DEBUG
+ msgout("cannot get transport name");
+#endif
+ } else if ((nconf = getnetconfigent(netid)) == NULL) {
+#ifdef DEBUG
+ msgout("cannot get transport info");
+#endif
+ }
+ if ((transp = svc_tli_create(0, nconf, NULL, 0, 0)) == NULL) {
+ msgout("cannot create server handle");
+ exit(1);
+ }
+ if (nconf)
+ freenetconfigent(nconf);
+ if (!svc_reg(transp, RUSERSPROG, RUSERSVERS_3, rusers_service,
+ 0)) {
+ msgout("unable to register (RUSERSPROG, RUSERSVERS_3).");
+ exit(1);
+ }
+ if (!svc_reg(transp, RUSERSPROG, RUSERSVERS_IDLE,
+ rusers_service, 0)) {
+ msgout("unable to register (RUSERSPROG, RUSERSVERS_IDLE).");
+ exit(1);
+ }
+ (void) signal(SIGALRM, closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN);
+ svc_run();
+ msgout("svc_run returned");
+ exit(1);
+ /* NOTREACHED */
+ }
+#ifndef RPC_SVC_FG
+ pid = fork();
+ if (pid < 0) {
+ perror("rpc.rusersd: cannot fork");
+ exit(1);
+ }
+ if (pid)
+ exit(0);
+ for (i = 0; i < 20; i++)
+ (void) close(i);
+ setsid();
+ openlog("rusers", LOG_PID, LOG_DAEMON);
+#endif
+ if (!svc_create(rusers_service, RUSERSPROG, RUSERSVERS_3, "netpath")) {
+ msgout("unable to create (RUSERSPROG, RUSERSVERS_3) for netpath");
+ exit(1);
+ }
+ if (!svc_create(rusers_service, RUSERSPROG, RUSERSVERS_IDLE,
+ "netpath")) {
+ msgout(
+ "unable to create (RUSERSPROG, RUSERSVERS_IDLE) for netpath");
+ exit(1);
+ }
+
+ svc_run();
+ msgout("svc_run returned");
+ exit(1);
+ /* NOTREACHED */
+}
+
+
+/*
+ * This routine gets the user information.
+ * "all" specifies whether all listings should be counted, or only those of
+ * type "USER_PROCESS".
+ * "version" is either RUSERSVERS_IDLE or RUSERSVERS_3. If anything else,
+ * just a count is returned.
+ * "limit" specifies the maximum number of entries to be processed.
+ *
+ * For both versions, the results are placed into an external variable.
+ * For RUSERSVERS_IDLE, this routine mallocs entries in a vector as it
+ * processed each utmpx entry. These malloc'd entries must be freed after the
+ * results are returned.
+ * For RUSERSVERS_3, this routine uses array entries that are malloc'd prior
+ * to this routine being called. "limit" is the number of elements available.
+ */
+int
+getutmpx_3(all, version, limit)
+ int all; /* give all listings? */
+ int version; /* version 2 or 3 */
+ int limit; /* limits users returned, 0 means no limit */
+{
+ struct utmpx *utent;
+ struct utmpidle **q = utmpidlearr.uia_arr;
+ int minidle;
+ int cnt = 0;
+ time_t now;
+ extern char *s_malodup();
+
+ time(&now); /* only one call to time() for this rpc call */
+ setutxent(); /* reset the utmpx file */
+ while ((utent = getutxent()) != NULL && (limit == 0 || cnt < limit)) {
+ if (utent->ut_line[0] == '\0' || utent->ut_user[0] == '\0')
+ continue;
+ /*
+ * List only user processes.
+ * XXX modified to exclude cmdtool style window entries.
+ */
+ if ((all == REAL_USERS) && ((utent->ut_type != USER_PROCESS) ||
+ nonuserx(*utent)))
+ continue;
+
+ if (version == RUSERSVERS_IDLE) {
+ /*
+ * need to free this; done after svc_sendreply.
+ */
+ *q = (struct utmpidle *)
+ malloc(sizeof (struct utmpidle));
+ (*q)->ui_idle = findidle(utent->ut_line,
+ sizeof (utent->ut_line), now);
+ if (strncmp(utent->ut_line, "console",
+ strlen("console")) == 0) {
+ (*q)->ui_idle = min((*q)->ui_idle,
+ console_idle(now));
+ }
+ usys5to_ru(utent, &((*q)->ui_utmp));
+#ifdef DEBUG
+ printf("%-*s %-*s %s; idle %d",
+ sizeof (utent->ut_line),
+ utent->ut_line,
+ sizeof (utent->ut_name),
+ utent->ut_name,
+ ctime(&utent->ut_xtime),
+ (*q)->ui_idle);
+#endif
+ q++;
+ } else if (version == RUSERSVERS_3) {
+#define uav utmp_array_res.utmp_array_val
+
+ uav[cnt].ut_host =
+ s_malodup(utent->ut_host, utent->ut_syslen);
+ uav[cnt].ut_user = s_malodup(utent->ut_user,
+ sizeof (utent->ut_user));
+ uav[cnt].ut_line = s_malodup(utent->ut_line,
+ sizeof (utent->ut_line));
+ uav[cnt].ut_type = utent->ut_type;
+ uav[cnt].ut_time = utent->ut_xtime;
+ uav[cnt].ut_idle = findidle(utent->ut_line,
+ sizeof (utent->ut_line), now);
+ if (strncmp(utent->ut_line, "console",
+ strlen("console")) == 0) {
+ uav[cnt].ut_idle =
+ min(uav[cnt].ut_idle,
+ console_idle(now));
+ }
+#ifdef DEBUG
+ printf("user: %-10s line: %-10s %s; idle %d (%s)\n",
+ uav[cnt].ut_line, uav[cnt].ut_user,
+ ctime((time_t *)&uav[cnt].ut_time),
+ uav[cnt].ut_idle, uav[cnt].ut_host);
+#endif
+#undef uav
+ }
+ cnt++;
+ }
+ return (cnt);
+}
+
+/*
+ * "string" is a character array with maximum size "size". Return a
+ * malloc'd string that's a duplicate of the string.
+ */
+char *
+s_malodup(string, size)
+char *string;
+int size;
+{
+ char *tmp;
+
+ tmp = (char *)malloc(size+1);
+ if (tmp == NULL) {
+ msgout("rpc.rusersd: malloc failed (2)");
+ return (NULL);
+ }
+ strncpy(tmp, string, size);
+ tmp[size] = '\0';
+ return (tmp);
+}
+
+
+int
+console_idle(now)
+ time_t now;
+{
+ /*
+ * On the console, the user may be running a window system; if so,
+ * their activity will show up in the last-access times of
+ * "/dev/kbd" and "/dev/mouse", so take the minimum of the idle
+ * times on those two devices and "/dev/console" and treat that as
+ * the idle time.
+ */
+ return (min((unsigned)findidle("kbd", strlen("kbd"), now),
+ (unsigned)findidle("mouse", strlen("mouse"), now)));
+}
+
+static void
+rusers_service(rqstp, transp)
+ register struct svc_req *rqstp;
+ register SVCXPRT *transp;
+{
+ int i;
+ int cnt;
+ char *replyerr = "rpc.rusersd: error replying to request";
+
+ _rpcsvcrecent = _rpcsvcdirty = 1;
+ switch (rqstp->rq_proc) {
+ case 0:
+ if (svc_sendreply(transp, xdr_void, 0) == FALSE) {
+ msgout(replyerr);
+ }
+ break;
+ case RUSERSPROC_NUM:
+ cnt = getutmpx_3(REAL_USERS, 0, 0);
+ if (!svc_sendreply(transp, xdr_u_long, (caddr_t)&cnt))
+ msgout(replyerr);
+ break;
+ case RUSERSPROC_NAMES:
+ case RUSERSPROC_ALLNAMES:
+ if (rqstp->rq_vers == RUSERSVERS_IDLE) {
+ utmpidlearr.uia_arr = (struct utmpidle **)
+ malloc(MAXUSERS*sizeof (struct utmpidle *));
+ utmpidlearr.uia_cnt = getutmpx_3(rqstp->rq_proc ==
+ RUSERSPROC_ALLNAMES,
+ RUSERSVERS_IDLE, MAXUSERS);
+ if (!svc_sendreply(transp, xdr_utmpidlearr,
+ (caddr_t)&utmpidlearr))
+ msgout(replyerr);
+ for (i = 0; i < utmpidlearr.uia_cnt; i++) {
+ free(utmpidlearr.uia_arr[i]);
+ }
+ free(utmpidlearr.uia_arr);
+ } else if (rqstp->rq_vers == RUSERSVERS_3) {
+ int entries, alloc_array_len;
+
+ /*
+ * Always free strings from previous results array
+ */
+ for (i = 0; i < used_array_len; i++) {
+ free_ua_entry(&utmp_array_res.utmp_array_val[i]);
+ }
+ entries = (rqstp->rq_proc == RUSERSPROC_ALLNAMES);
+ cnt = getutmpx_3(entries, 0, 0); /* get cnt */
+ if (cnt > utmp_array_res.utmp_array_len) {
+ free(utmp_array_res.utmp_array_val);
+ utmp_array_res.utmp_array_len = 0;
+ utmp_array_res.utmp_array_val = (rusers_utmp *)
+ malloc(cnt * sizeof (rusers_utmp));
+ if (utmp_array_res.utmp_array_val == NULL) {
+ msgout("rpc.rusersd: malloc failed (1)");
+ break;
+ }
+ alloc_array_len = cnt;
+ } else {
+ alloc_array_len = utmp_array_res.utmp_array_len;
+ }
+ cnt = getutmpx_3(entries, RUSERSVERS_3, cnt);
+ utmp_array_res.utmp_array_len = used_array_len = cnt;
+ if (!svc_sendreply(transp, xdr_utmp_array,
+ (caddr_t)&utmp_array_res))
+ msgout(replyerr);
+ utmp_array_res.utmp_array_len = alloc_array_len;
+ }
+ break;
+ default:
+ svcerr_noproc(transp);
+ break;
+ }
+ _rpcsvcdirty = 0;
+
+}
+
+free_ua_entry(uap)
+rusers_utmp *uap;
+{
+ if (uap == NULL)
+ return;
+ if (uap->ut_user)
+ free(uap->ut_user);
+ if (uap->ut_line)
+ free(uap->ut_line);
+ if (uap->ut_host)
+ free(uap->ut_host);
+}
+
+
+
+/* find & return number of minutes current tty has been idle */
+findidle(name, ln, now)
+ char *name;
+ int ln;
+ time_t now;
+{
+ struct stat stbuf;
+ long lastaction, diff;
+ char ttyname[32];
+
+ strcpy(ttyname, "/dev/");
+ strncat(ttyname, name, ln);
+ if (stat(ttyname, &stbuf) < 0)
+ return (INT_MAX);
+ lastaction = stbuf.st_atime;
+ diff = now - lastaction;
+ diff = DIV60(diff);
+ if (diff < 0) diff = 0;
+ return (diff);
+}
+
+static
+usys5to_ru(s5, bss)
+ struct utmpx *s5;
+ struct ru_utmp *bss;
+{
+ int i;
+
+#ifdef DEBUG
+ printf("sizeof (bss->ut_host) == %d\n", sizeof (bss->ut_host));
+#endif
+ strncpy(bss->ut_name, s5->ut_name, sizeof (bss->ut_name));
+ strncpy(bss->ut_line, s5->ut_line, sizeof (bss->ut_line));
+ strncpy(bss->ut_host, s5->ut_host, sizeof (bss->ut_host));
+ bss->ut_time = s5->ut_xtime;
+}
+
+static void
+msgout(msg)
+ char *msg;
+{
+#ifdef RPC_SVC_FG
+ if (_rpcpmstart)
+ syslog(LOG_ERR, msg);
+ else
+ (void) fprintf(stderr, "%s\n", msg);
+#else
+ syslog(LOG_ERR, msg);
+#endif
+}
+
+static void
+closedown(sig)
+int sig;
+{
+ if (_rpcsvcrecent) {
+ _rpcsvcrecent = 0;
+ } else {
+ if (_rpcsvcdirty == 0) {
+ int i, openfd;
+ struct t_info tinfo;
+
+ if (t_getinfo(0, &tinfo) || (tinfo.servtype == T_CLTS))
+ exit(0);
+
+ for (i = 0, openfd = 0;
+ i < svc_max_pollfd && openfd < 2;
+ i++) {
+ if (svc_pollfd[i].fd >= 0)
+ openfd++;
+ }
+
+ if (openfd <= 1)
+ exit(0);
+ }
+ }
+ (void) signal(SIGALRM, closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN);
+}
+
+unsigned
+min(a, b)
+unsigned a;
+unsigned b;
+{
+ if (a < b)
+ return (a);
+ else
+ return (b);
+}
diff --git a/usr/src/cmd/rpcsvc/rstat.xml b/usr/src/cmd/rpcsvc/rstat.xml
new file mode 100644
index 0000000000..8e33cf17bd
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/rstat.xml
@@ -0,0 +1,112 @@
+<?xml version='1.0'?>
+<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
+
+<!--
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ 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
+ or http://www.opensolaris.org/os/licensing.
+ 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
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+
+ Service manifest for rpc.rstatd
+-->
+
+<service_bundle type='manifest' name='SUNWrcmdr:rstatd'>
+
+<service
+ name='network/rpc/rstat'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='false' />
+
+ <restarter>
+ <service_fmri value='svc:/network/inetd:default' />
+ </restarter>
+
+ <dependency name='rpcbind'
+ grouping='require_all'
+ restart_on='restart'
+ type='service'>
+ <service_fmri value='svc:/network/rpc/bind' />
+ </dependency>
+
+ <exec_method
+ type='method'
+ name='inetd_start'
+ exec='/usr/lib/netsvc/rstat/rpc.rstatd'
+ timeout_seconds='0'>
+ <method_context>
+ <method_credential user='root' group='root' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_offline'
+ exec=':kill_process'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_disable'
+ exec=':kill'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <property_group name='inetd' type='framework'>
+ <stability value='Evolving' />
+ <propval name='name' type='astring' value='rstatd' />
+ <propval name='endpoint_type' type='astring' value='tli' />
+ <propval name='proto' type='astring' value='datagram_v'
+ override='true' />
+ <propval name='isrpc' type='boolean' value='true' />
+ <propval name='rpc_low_version' type='integer' value='2' />
+ <propval name='rpc_high_version' type='integer' value='4' />
+ <propval name='wait' type='boolean' value='true' />
+ </property_group>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ kernel statistics server
+ </loctext>
+ </common_name>
+ <documentation>
+ <manpage title='rpc.rstatd' section='1M'
+ manpath='/usr/share/man' />
+ <manpage title='rstatd' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/rpcsvc/rstat_main.c b/usr/src/cmd/rpcsvc/rstat_main.c
new file mode 100644
index 0000000000..272986d3e9
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/rstat_main.c
@@ -0,0 +1,174 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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-2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "rstat.h"
+#include "rstat_v2.h"
+#include <stdio.h>
+#include <stdlib.h> /* getenv, exit */
+#include <signal.h>
+#include <sys/types.h>
+#include <memory.h>
+#include <stropts.h>
+#include <netconfig.h>
+#include <syslog.h>
+
+#ifdef __STDC__
+#define SIG_PF void(*)(int)
+#endif
+
+#ifdef DEBUG
+#define RPC_SVC_FG
+#endif
+
+
+int _rpcpmstart; /* Started by a port monitor ? */
+int _rpcfdtype; /* Whether Stream or Datagram ? */
+int _rpcsvcdirty; /* Still serving ? */
+
+static void _msgout(/*char *msg*/);
+static void closedown();
+
+extern void rstatprog_4(/*struct svc_req *rqstp, SVCXPRT *transp*/);
+extern void rstatprog_3(/*struct svc_req *rqstp, SVCXPRT *transp*/);
+extern void rstatprog_2(/*struct svc_req *rqstp, SVCXPRT *transp*/);
+
+main()
+{
+ pid_t pid;
+ int i;
+
+ /*
+ * If stdin looks like a TLI endpoint, we assume
+ * that we were started by a port monitor. If
+ * t_getstate fails with TBADF, this is not a
+ * TLI endpoint.
+ */
+ if (t_getstate(0) != -1 || t_errno != TBADF) {
+ char *netid;
+ struct netconfig *nconf = NULL;
+ SVCXPRT *transp;
+
+ _rpcpmstart = 1;
+ openlog("rstatd", LOG_PID, LOG_DAEMON);
+ if ((netid = getenv("NLSPROVIDER")) == NULL) {
+#ifdef DEBUG
+ _msgout("cannot get transport name");
+#endif
+ } else if ((nconf = getnetconfigent(netid)) == NULL) {
+#ifdef DEBUG
+ _msgout("cannot get transport info");
+#endif
+ }
+ if ((transp = svc_tli_create(0, nconf, NULL, 0, 0)) == NULL) {
+ _msgout("cannot create server handle");
+ exit(1);
+ }
+ if (nconf)
+ freenetconfigent(nconf);
+ if (!svc_reg(transp, RSTATPROG, RSTATVERS_VAR, rstatprog_4,
+ 0)) {
+ _msgout("unable to register (RSTATPROG, RSTATVERS_VAR).");
+ exit(1);
+ }
+ if (!svc_reg(transp, RSTATPROG, RSTATVERS_TIME, rstatprog_3,
+ 0)) {
+ _msgout("unable to register (RSTATPROG, RSTATVERS_TIME).");
+ exit(1);
+ }
+ if (!svc_reg(transp, RSTATPROG, RSTATVERS_SWTCH, rstatprog_2,
+ 0)) {
+ _msgout("unable to register (RSTATPROG, RSTATVERS_SWTCH).");
+ exit(1);
+ }
+ svc_run();
+ exit(1);
+ /* NOTREACHED */
+ }
+ else {
+#ifndef RPC_SVC_FG
+ pid = fork();
+ if (pid < 0) {
+ perror("cannot fork");
+ exit(1);
+ }
+ if (pid)
+ exit(0);
+ closefrom(0);
+ i = open("/dev/console", 2);
+ (void) dup2(i, 1);
+ (void) dup2(i, 2);
+ setsid();
+ openlog("rstatd", LOG_PID, LOG_DAEMON);
+#endif
+ }
+ if (!svc_create(rstatprog_4, RSTATPROG, RSTATVERS_VAR, "datagram_v")) {
+ _msgout("unable to create (RSTATPROG, RSTATVERS_VAR) for datagram_v.");
+ exit(1);
+ }
+ if (!svc_create(rstatprog_3, RSTATPROG, RSTATVERS_TIME,
+ "datagram_v")) {
+ _msgout("unable to create (RSTATPROG, RSTATVERS_TIME) for datagram_v.");
+ exit(1);
+ }
+ if (!svc_create(rstatprog_4, RSTATPROG, RSTATVERS_VAR, "circuit_v")) {
+ _msgout("unable to create (RSTATPROG, RSTATVERS_VAR) for circuit_v.");
+ exit(1);
+ }
+ if (!svc_create(rstatprog_3, RSTATPROG, RSTATVERS_TIME, "circuit_v")) {
+ _msgout("unable to create (RSTATPROG, RSTATVERS_TIME) for circuit_v.");
+ exit(1);
+ }
+
+ /*
+ * V2 supported on datagram transports *only*
+ */
+ if (!svc_create(rstatprog_2, RSTATPROG, RSTATVERS_SWTCH,
+ "datagram_v")) {
+ _msgout("unable to create (RSTATPROG, RSTATVERS_SWTCH) for datagram_v.");
+ exit(1);
+ }
+
+ svc_run();
+ _msgout("svc_run returned");
+ exit(1);
+ /* NOTREACHED */
+}
+
+static
+void _msgout(msg)
+ char *msg;
+{
+#ifdef RPC_SVC_FG
+ if (_rpcpmstart)
+ syslog(LOG_ERR, msg);
+ else
+ (void) fprintf(stderr, "%s\n", msg);
+#else
+ syslog(LOG_ERR, msg);
+#endif
+}
diff --git a/usr/src/cmd/rpcsvc/rstat_proc.c b/usr/src/cmd/rpcsvc/rstat_proc.c
new file mode 100644
index 0000000000..a871985bd8
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/rstat_proc.c
@@ -0,0 +1,1095 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * rstat service: built with rstat.x
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <signal.h>
+#include <utmpx.h>
+#include <nlist.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <kstat.h>
+
+#include <rpc/rpc.h>
+
+#include <sys/socket.h>
+#include <sys/cpuvar.h>
+#include <sys/sysinfo.h>
+#include <sys/systm.h>
+#include <errno.h>
+#include <sys/stropts.h>
+#include <sys/tihdr.h>
+#include <sys/sysmacros.h>
+
+#include <net/if.h>
+#include <inet/mib2.h>
+
+#include "rstat.h"
+#include "rstat_v2.h"
+
+typedef struct {
+ kstat_t sys;
+ kstat_t vm;
+} _cpu_stats_t;
+
+/*
+ * system and cpu stats
+ */
+static kstat_ctl_t *kc; /* libkstat cookie */
+static int ncpus;
+static _cpu_stats_t *cpu_stats_list = NULL;
+static kstat_t *system_misc_ksp;
+static kstat_named_t *boot_time_knp;
+static kstat_named_t *avenrun_1min_knp, *avenrun_5min_knp, *avenrun_15min_knp;
+static int hz;
+static struct timeval btm; /* boottime */
+
+/*
+ * network interface stats
+ */
+
+typedef struct mib_item_s {
+ struct mib_item_s *next_item;
+ long group;
+ long mib_id;
+ long length;
+ char *valp;
+} mib_item_t;
+
+mib_item_t *netstat_item;
+
+/*
+ * disk stats
+ */
+
+struct diskinfo {
+ struct diskinfo *next;
+ kstat_t *ks;
+ kstat_io_t kios;
+};
+
+#define NULLDISK (struct diskinfo *)0
+static struct diskinfo zerodisk = { NULL, NULL };
+static struct diskinfo *firstdisk = NULLDISK;
+static struct diskinfo *lastdisk = NULLDISK;
+static struct diskinfo *snip = NULLDISK;
+static int ndisks;
+
+/*
+ * net stats
+ */
+
+struct netinfo {
+ struct netinfo *next;
+ kstat_t *ks;
+ kstat_named_t *ipackets;
+ kstat_named_t *opackets;
+ kstat_named_t *ierrors;
+ kstat_named_t *oerrors;
+ kstat_named_t *collisions;
+};
+
+#define NULLNET (struct netinfo *)0
+static struct netinfo zeronet = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
+static struct netinfo *firstnet = NULLNET;
+static struct netinfo *lastnet = NULLNET;
+static struct netinfo *netsnip = NULLNET;
+static int nnets;
+
+/*
+ * Define EXIT_WHEN_IDLE if you are able to have this program invoked
+ * automatically on demand (as from inetd). When defined, the service
+ * will terminated after being idle for 120 seconds.
+ */
+
+#define EXIT_WHEN_IDLE 1
+
+int sincelastreq = 0; /* number of alarms since last request */
+#ifdef EXIT_WHEN_IDLE
+#define CLOSEDOWN 120 /* how long to wait before exiting */
+#endif /* def EXIT_WHEN_IDLE */
+
+statstime stats_s3;
+statsvar stats_s4;
+/* V2 support for backwards compatibility to pre-5.0 systems */
+statsswtch stats_s2;
+
+static stat_is_init = 0;
+
+static void fail(int, char *, ...);
+static void safe_zalloc(void **, int, int);
+static kid_t safe_kstat_read(kstat_ctl_t *, kstat_t *, void *);
+static kstat_t *safe_kstat_lookup(kstat_ctl_t *, char *, int, char *);
+static void *safe_kstat_data_lookup(kstat_t *, char *);
+static void system_stat_init(void);
+static int system_stat_load(void);
+static void init_disks(void);
+static int diskinfo_load(void);
+static void init_net(void);
+static int netinfo_load(void);
+
+static void updatestat(int);
+
+static mib_item_t *mibget(int sd);
+static int mibopen(void);
+static char *octetstr(char *buf, Octet_t *op, int code);
+
+static void kstat_copy(kstat_t *, kstat_t *, int);
+
+static char *cmdname = "rpc.rstatd";
+
+#define CPU_STAT(ksp, name) (((kstat_named_t *)safe_kstat_data_lookup( \
+ (ksp), (name)))->value.ui64)
+static _cpu_stats_t cpu_stats_all = { 0 };
+
+static void
+stat_init(void)
+{
+ struct utmpx *utmpx, utmpx_id;
+
+ stat_is_init = 1;
+
+ if ((kc = kstat_open()) == NULL)
+ fail(1, "kstat_open(): can't open /dev/kstat");
+
+ /*
+ * Preallocate minimal set of drive entries.
+ */
+
+ if (stats_s4.dk_xfer.dk_xfer_val == NULL) {
+ stats_s4.dk_xfer.dk_xfer_len = RSTAT_DK_NDRIVE;
+ stats_s4.dk_xfer.dk_xfer_val =
+ (int *)calloc(RSTAT_DK_NDRIVE, sizeof (int));
+ }
+
+ system_stat_init();
+ init_disks();
+ init_net();
+
+ /*
+ * To get the boot time, use utmpx, which is per-zone, but fall back
+ * to the system-wide kstat if utmpx is hosed for any reason.
+ */
+ utmpx_id.ut_type = BOOT_TIME;
+ if ((utmpx = getutxid(&utmpx_id)) != NULL)
+ btm = utmpx->ut_tv;
+ else {
+ btm.tv_sec = boot_time_knp->value.ul;
+ btm.tv_usec = 0; /* don't bother with usecs for boot time */
+ }
+ endutxent();
+ stats_s4.boottime.tv_sec =
+ stats_s2.boottime.tv_sec =
+ stats_s3.boottime.tv_sec = btm.tv_sec;
+ stats_s4.boottime.tv_usec =
+ stats_s2.boottime.tv_usec =
+ stats_s3.boottime.tv_usec = btm.tv_usec;
+
+ updatestat(0);
+ alarm(1);
+ signal(SIGALRM, updatestat);
+ sleep(2); /* allow for one wake-up */
+}
+
+statsvar *
+rstatproc_stats_4_svc(argp, svcrq)
+void *argp;
+struct svc_req *svcrq;
+{
+ if (! stat_is_init)
+ stat_init();
+#ifdef EXIT_WHEN_IDLE
+ sincelastreq = 0;
+#endif
+ return (&stats_s4);
+}
+
+statstime *
+rstatproc_stats_3_svc(argp, svcrq)
+void *argp;
+struct svc_req *svcrq;
+{
+ if (! stat_is_init)
+ stat_init();
+#ifdef EXIT_WHEN_IDLE
+ sincelastreq = 0;
+#endif
+ return (&stats_s3);
+}
+
+statsswtch *
+rstatproc_stats_2_svc(argp, svcrq)
+void *argp;
+struct svc_req *svcrq;
+{
+ if (! stat_is_init)
+ stat_init();
+#ifdef EXIT_WHEN_IDLE
+ sincelastreq = 0;
+#endif
+ return (&stats_s2);
+}
+
+
+uint_t *
+rstatproc_havedisk_4_svc(argp, svcrq)
+void *argp;
+struct svc_req *svcrq;
+{
+ return (rstatproc_havedisk_3_svc(argp, svcrq));
+}
+
+uint_t *
+rstatproc_havedisk_3_svc(argp, svcrq)
+void *argp;
+struct svc_req *svcrq;
+{
+ static uint_t have;
+
+ if (! stat_is_init)
+ stat_init();
+#ifdef EXIT_WHEN_IDLE
+ sincelastreq = 0;
+#endif
+ have = (ndisks != 0);
+ return (&have);
+}
+
+uint_t *
+rstatproc_havedisk_2_svc(argp, svcrq)
+void *argp;
+struct svc_req *svcrq;
+{
+ return (rstatproc_havedisk_3_svc(argp, svcrq));
+}
+
+void
+updatestat(int ignored)
+{
+extern int _rpcpmstart; /* Started by a port monitor ? */
+extern int _rpcsvcdirty; /* Still serving ? */
+
+#ifdef DEBUG
+ fprintf(stderr, "entering updatestat\n");
+#endif
+#ifdef EXIT_WHEN_IDLE
+ if (_rpcpmstart && sincelastreq >= CLOSEDOWN && !_rpcsvcdirty) {
+#ifdef DEBUG
+ fprintf(stderr, "about to closedown\n");
+#endif
+ exit(0);
+ }
+ sincelastreq++;
+#endif /* def EXIT_WHEN_IDLE */
+
+ (void) alarm(0);
+#ifdef DEBUG
+ fprintf(stderr, "boottime: %d %d\n", stats_s3.boottime.tv_sec,
+ stats_s3.boottime.tv_usec);
+#endif
+ while (system_stat_load() || diskinfo_load() || netinfo_load()) {
+ (void) kstat_chain_update(kc);
+ system_stat_init();
+ init_disks();
+ init_net();
+ }
+ stats_s4.cp_time.cp_time_len = CPU_STATES;
+ if (stats_s4.cp_time.cp_time_val == NULL)
+ stats_s4.cp_time.cp_time_val =
+ malloc(stats_s4.cp_time.cp_time_len * sizeof (int));
+ stats_s2.cp_time[RSTAT_CPU_USER] =
+ stats_s3.cp_time[RSTAT_CPU_USER] =
+ stats_s4.cp_time.cp_time_val[RSTAT_CPU_USER] =
+ CPU_STAT(&cpu_stats_all.sys, "cpu_ticks_user");
+ stats_s2.cp_time[RSTAT_CPU_NICE] =
+ stats_s3.cp_time[RSTAT_CPU_NICE] =
+ stats_s4.cp_time.cp_time_val[RSTAT_CPU_NICE] =
+ CPU_STAT(&cpu_stats_all.sys, "cpu_ticks_wait");
+ stats_s2.cp_time[RSTAT_CPU_SYS] =
+ stats_s3.cp_time[RSTAT_CPU_SYS] =
+ stats_s4.cp_time.cp_time_val[RSTAT_CPU_SYS] =
+ CPU_STAT(&cpu_stats_all.sys, "cpu_ticks_kernel");
+ stats_s2.cp_time[RSTAT_CPU_IDLE] =
+ stats_s3.cp_time[RSTAT_CPU_IDLE] =
+ stats_s4.cp_time.cp_time_val[RSTAT_CPU_IDLE] =
+ CPU_STAT(&cpu_stats_all.sys, "cpu_ticks_idle");
+
+#ifdef DEBUG
+ fprintf(stderr, "cpu: %d %d %d %d\n",
+ CPU_STAT(&cpu_stats_all.sys, "cpu_ticks_user"),
+ CPU_STAT(&cpu_stats_all.sys, "cpu_ticks_wait"),
+ CPU_STAT(&cpu_stats_all.sys, "cpu_ticks_kernel"),
+ CPU_STAT(&cpu_stats_all.sys, "cpu_ticks_idle"));
+ fprintf(stderr, "cp_time: %d %d %d %d\n",
+ stats_s3.cp_time[RSTAT_CPU_USER],
+ stats_s3.cp_time[RSTAT_CPU_NICE],
+ stats_s3.cp_time[RSTAT_CPU_SYS],
+ stats_s3.cp_time[RSTAT_CPU_IDLE]);
+#endif
+
+ /* current time */
+ gettimeofday((struct timeval *)&stats_s3.curtime, NULL);
+ stats_s4.curtime = stats_s3.curtime;
+
+ stats_s2.v_pgpgin =
+ stats_s3.v_pgpgin =
+ stats_s4.v_pgpgin = CPU_STAT(&cpu_stats_all.vm, "pgpgin");
+ stats_s2.v_pgpgout =
+ stats_s3.v_pgpgout =
+ stats_s4.v_pgpgout = CPU_STAT(&cpu_stats_all.vm, "pgpgout");
+ stats_s2.v_pswpin =
+ stats_s3.v_pswpin =
+ stats_s4.v_pswpin = CPU_STAT(&cpu_stats_all.vm, "pgswapin");
+ stats_s2.v_pswpout =
+ stats_s3.v_pswpout =
+ stats_s4.v_pswpout = CPU_STAT(&cpu_stats_all.vm, "pgswapout");
+ stats_s3.v_intr = CPU_STAT(&cpu_stats_all.sys, "intr");
+ stats_s3.v_intr -= hz*(stats_s3.curtime.tv_sec - btm.tv_sec) +
+ hz*(stats_s3.curtime.tv_usec - btm.tv_usec)/1000000;
+ stats_s2.v_intr =
+ stats_s4.v_intr = stats_s3.v_intr;
+ /* swtch not in V1 */
+ stats_s2.v_swtch =
+ stats_s3.v_swtch =
+ stats_s4.v_swtch = CPU_STAT(&cpu_stats_all.sys, "pswitch");
+
+#ifdef DEBUG
+ fprintf(stderr,
+ "pgin: %d pgout: %d swpin: %d swpout: %d intr: %d swtch: %d\n",
+ stats_s3.v_pgpgin,
+ stats_s3.v_pgpgout,
+ stats_s3.v_pswpin,
+ stats_s3.v_pswpout,
+ stats_s3.v_intr,
+ stats_s3.v_swtch);
+#endif
+ /*
+ * V2 and V3 of rstat are limited to RSTAT_DK_NDRIVE drives
+ */
+ memcpy(stats_s3.dk_xfer, stats_s4.dk_xfer.dk_xfer_val,
+ RSTAT_DK_NDRIVE * sizeof (int));
+ memcpy(stats_s2.dk_xfer, stats_s4.dk_xfer.dk_xfer_val,
+ RSTAT_DK_NDRIVE * sizeof (int));
+#ifdef DEBUG
+ fprintf(stderr, "dk_xfer: %d %d %d %d\n",
+ stats_s4.dk_xfer.dk_xfer_val[0],
+ stats_s4.dk_xfer.dk_xfer_val[1],
+ stats_s4.dk_xfer.dk_xfer_val[2],
+ stats_s4.dk_xfer.dk_xfer_val[3]);
+#endif
+
+ stats_s2.if_ipackets =
+ stats_s3.if_ipackets = stats_s4.if_ipackets;
+ /* no s2 opackets */
+ stats_s3.if_opackets = stats_s4.if_opackets;
+ stats_s2.if_ierrors =
+ stats_s3.if_ierrors = stats_s4.if_ierrors;
+ stats_s2.if_oerrors =
+ stats_s3.if_oerrors = stats_s4.if_oerrors;
+ stats_s2.if_collisions =
+ stats_s3.if_collisions = stats_s4.if_collisions;
+
+ stats_s2.avenrun[0] =
+ stats_s3.avenrun[0] =
+ stats_s4.avenrun[0] = avenrun_1min_knp->value.ul;
+ stats_s2.avenrun[1] =
+ stats_s3.avenrun[1] =
+ stats_s4.avenrun[1] = avenrun_5min_knp->value.ul;
+ stats_s2.avenrun[2] =
+ stats_s3.avenrun[2] =
+ stats_s4.avenrun[2] = avenrun_15min_knp->value.ul;
+#ifdef DEBUG
+ fprintf(stderr, "avenrun: %d %d %d\n", stats_s3.avenrun[0],
+ stats_s3.avenrun[1], stats_s3.avenrun[2]);
+#endif
+ signal(SIGALRM, updatestat);
+ alarm(1);
+}
+
+/* --------------------------------- MIBGET -------------------------------- */
+
+static mib_item_t *
+mibget(int sd)
+{
+ int flags;
+ int j, getcode;
+ struct strbuf ctlbuf, databuf;
+ char buf[512];
+ struct T_optmgmt_req *tor = (struct T_optmgmt_req *)buf;
+ struct T_optmgmt_ack *toa = (struct T_optmgmt_ack *)buf;
+ struct T_error_ack *tea = (struct T_error_ack *)buf;
+ struct opthdr *req;
+ mib_item_t *first_item = NULL;
+ mib_item_t *last_item = NULL;
+ mib_item_t *temp;
+
+ tor->PRIM_type = T_SVR4_OPTMGMT_REQ;
+ tor->OPT_offset = sizeof (struct T_optmgmt_req);
+ tor->OPT_length = sizeof (struct opthdr);
+ tor->MGMT_flags = T_CURRENT;
+ req = (struct opthdr *)&tor[1];
+ req->level = MIB2_IP; /* any MIB2_xxx value ok here */
+ req->name = 0;
+ req->len = 0;
+
+ ctlbuf.buf = buf;
+ ctlbuf.len = tor->OPT_length + tor->OPT_offset;
+ flags = 0;
+ if (putmsg(sd, &ctlbuf, NULL, flags) == -1) {
+ perror("mibget: putmsg(ctl) failed");
+ goto error_exit;
+ }
+ /*
+ * each reply consists of a ctl part for one fixed structure
+ * or table, as defined in mib2.h. The format is a T_OPTMGMT_ACK,
+ * containing an opthdr structure. level/name identify the entry,
+ * len is the size of the data part of the message.
+ */
+ req = (struct opthdr *)&toa[1];
+ ctlbuf.maxlen = sizeof (buf);
+ /*CSTYLED*/
+ for (j = 1; ; j++) {
+ flags = 0;
+ getcode = getmsg(sd, &ctlbuf, NULL, &flags);
+ if (getcode == -1) {
+#ifdef DEBUG_MIB
+ perror("mibget getmsg(ctl) failed");
+ fprintf(stderr, "# level name len\n");
+ i = 0;
+ for (last_item = first_item; last_item;
+ last_item = last_item->next_item)
+ fprintf(stderr, "%d %4d %5d %d\n", ++i,
+ last_item->group,
+ last_item->mib_id,
+ last_item->length);
+#endif /* DEBUG_MIB */
+ goto error_exit;
+ }
+ if (getcode == 0 &&
+ (ctlbuf.len >= sizeof (struct T_optmgmt_ack)) &&
+ (toa->PRIM_type == T_OPTMGMT_ACK) &&
+ (toa->MGMT_flags == T_SUCCESS) &&
+ req->len == 0) {
+#ifdef DEBUG_MIB
+ fprintf(stderr,
+ "mibget getmsg() %d returned EOD (level %d, name %d)\n",
+ j, req->level, req->name);
+#endif /* DEBUG_MIB */
+ return (first_item); /* this is EOD msg */
+ }
+
+ if (ctlbuf.len >= sizeof (struct T_error_ack) &&
+ (tea->PRIM_type == T_ERROR_ACK)) {
+#ifdef DEBUG_MIB
+ fprintf(stderr,
+ "mibget %d gives T_ERROR_ACK: TLI_error = 0x%x, UNIX_error = 0x%x\n",
+ j, getcode, tea->TLI_error, tea->UNIX_error);
+#endif /* DEBUG_MIB */
+ errno = (tea->TLI_error == TSYSERR)
+ ? tea->UNIX_error : EPROTO;
+ goto error_exit;
+ }
+
+ if (getcode != MOREDATA ||
+ (ctlbuf.len < sizeof (struct T_optmgmt_ack)) ||
+ (toa->PRIM_type != T_OPTMGMT_ACK) ||
+ (toa->MGMT_flags != T_SUCCESS)) {
+#ifdef DEBUG_MIB
+ fprintf(stderr,
+ "mibget getmsg(ctl) %d returned %d, ctlbuf.len = %d, PRIM_type = %d\n",
+ j, getcode, ctlbuf.len, toa->PRIM_type);
+ if (toa->PRIM_type == T_OPTMGMT_ACK)
+ fprintf(stderr,
+ "T_OPTMGMT_ACK: MGMT_flags = 0x%x, req->len = %d\n",
+ toa->MGMT_flags, req->len);
+#endif /* DEBUG_MIB */
+ errno = ENOMSG;
+ goto error_exit;
+ }
+
+ temp = malloc(sizeof (mib_item_t));
+ if (!temp) {
+ perror("mibget malloc failed");
+ goto error_exit;
+ }
+ if (last_item)
+ last_item->next_item = temp;
+ else
+ first_item = temp;
+ last_item = temp;
+ last_item->next_item = NULL;
+ last_item->group = req->level;
+ last_item->mib_id = req->name;
+ last_item->length = req->len;
+ last_item->valp = malloc(req->len);
+#ifdef DEBUG_MIB
+ fprintf(stderr,
+ "msg %d: group = %4d mib_id = %5d length = %d\n",
+ j, last_item->group, last_item->mib_id,
+ last_item->length);
+#endif /* DEBUG_MIB */
+ databuf.maxlen = last_item->length;
+ databuf.buf = last_item->valp;
+ databuf.len = 0;
+ flags = 0;
+ getcode = getmsg(sd, NULL, &databuf, &flags);
+ if (getcode == -1) {
+ perror("mibget getmsg(data) failed");
+ goto error_exit;
+ } else if (getcode != 0) {
+ fprintf(stderr,
+"mibget getmsg(data) returned %d, databuf.maxlen = %d, databuf.len = %d\n",
+ getcode, databuf.maxlen, databuf.len);
+ goto error_exit;
+ }
+ }
+
+error_exit:
+ while (first_item) {
+ last_item = first_item;
+ first_item = first_item->next_item;
+ if (last_item->valp) {
+ free(last_item->valp);
+ }
+ free(last_item);
+ }
+ return (first_item);
+}
+
+static int
+mibopen(void)
+{
+ int sd;
+
+ /* gives us ip w/ arp on top */
+ sd = open("/dev/arp", O_RDWR);
+ if (sd == -1) {
+ perror("arp open");
+ close(sd);
+ return (-1);
+ }
+ if (ioctl(sd, I_PUSH, "tcp") == -1) {
+ perror("tcp I_PUSH");
+ close(sd);
+ return (-1);
+ }
+ if (ioctl(sd, I_PUSH, "udp") == -1) {
+ perror("udp I_PUSH");
+ close(sd);
+ return (-1);
+ }
+ return (sd);
+}
+
+static char *
+octetstr(char *buf, Octet_t *op, int code)
+{
+ int i;
+ char *cp;
+
+ cp = buf;
+ if (op)
+ for (i = 0; i < op->o_length; i++)
+ switch (code) {
+ case 'd':
+ sprintf(cp, "%d.", 0xff & op->o_bytes[i]);
+ cp = strchr(cp, '\0');
+ break;
+ case 'a':
+ *cp++ = op->o_bytes[i];
+ break;
+ case 'h':
+ default:
+ sprintf(cp, "%02x:", 0xff & op->o_bytes[i]);
+ cp += 3;
+ break;
+ }
+ if (code != 'a' && cp != buf)
+ cp--;
+ *cp = '\0';
+ return (buf);
+}
+
+static void
+fail(int do_perror, char *message, ...)
+{
+ va_list args;
+
+ va_start(args, message);
+ fprintf(stderr, "%s: ", cmdname);
+ vfprintf(stderr, message, args);
+ va_end(args);
+ if (do_perror)
+ fprintf(stderr, ": %s", strerror(errno));
+ fprintf(stderr, "\n");
+ exit(2);
+}
+
+static void
+safe_zalloc(void **ptr, int size, int free_first)
+{
+ if (free_first && *ptr != NULL)
+ free(*ptr);
+ if ((*ptr = malloc(size)) == NULL)
+ fail(1, "malloc failed");
+ memset(*ptr, 0, size);
+}
+
+kid_t
+safe_kstat_read(kstat_ctl_t *kctl, kstat_t *ksp, void *data)
+{
+ kid_t kstat_chain_id = kstat_read(kctl, ksp, data);
+
+ if (kstat_chain_id == -1)
+ fail(1, "kstat_read(%x, '%s') failed", kctl, ksp->ks_name);
+ return (kstat_chain_id);
+}
+
+kstat_t *
+safe_kstat_lookup(kstat_ctl_t *kctl, char *ks_module, int ks_instance,
+ char *ks_name)
+{
+ kstat_t *ksp = kstat_lookup(kctl, ks_module, ks_instance, ks_name);
+
+ if (ksp == NULL)
+ fail(0, "kstat_lookup('%s', %d, '%s') failed",
+ ks_module == NULL ? "" : ks_module,
+ ks_instance,
+ ks_name == NULL ? "" : ks_name);
+ return (ksp);
+}
+
+void *
+safe_kstat_data_lookup(kstat_t *ksp, char *name)
+{
+ void *fp = kstat_data_lookup(ksp, name);
+
+ if (fp == NULL) {
+ fail(0, "kstat_data_lookup('%s', '%s') failed",
+ ksp->ks_name, name);
+ }
+ return (fp);
+}
+
+/*
+ * Get various KIDs for subsequent system_stat_load operations.
+ */
+
+static void
+system_stat_init(void)
+{
+ kstat_t *ksp;
+ int i, nvmks;
+
+ /*
+ * Global statistics
+ */
+
+ system_misc_ksp = safe_kstat_lookup(kc, "unix", 0, "system_misc");
+
+ safe_kstat_read(kc, system_misc_ksp, NULL);
+ boot_time_knp = safe_kstat_data_lookup(system_misc_ksp, "boot_time");
+ avenrun_1min_knp = safe_kstat_data_lookup(system_misc_ksp,
+ "avenrun_1min");
+ avenrun_5min_knp = safe_kstat_data_lookup(system_misc_ksp,
+ "avenrun_5min");
+ avenrun_15min_knp = safe_kstat_data_lookup(system_misc_ksp,
+ "avenrun_15min");
+
+ /*
+ * Per-CPU statistics
+ */
+
+ ncpus = 0;
+ for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next)
+ if (strcmp(ksp->ks_module, "cpu") == 0 &&
+ strcmp(ksp->ks_name, "sys") == 0)
+ ncpus++;
+
+ safe_zalloc((void **)&cpu_stats_list, ncpus * sizeof (*cpu_stats_list),
+ 1);
+
+ ncpus = 0;
+ for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next)
+ if (strcmp(ksp->ks_module, "cpu") == 0 &&
+ strcmp(ksp->ks_name, "sys") == 0 &&
+ kstat_read(kc, ksp, NULL) != -1) {
+ kstat_copy(ksp, &cpu_stats_list[ncpus].sys,
+ 1);
+ if ((ksp = kstat_lookup(kc, "cpu", ksp->ks_instance,
+ "vm")) != NULL && kstat_read(kc, ksp, NULL) != -1)
+ kstat_copy(ksp, &cpu_stats_list[ncpus].vm, 1);
+ else
+ fail(0, "couldn't find per-CPU VM statistics");
+ ncpus++;
+ }
+
+ if (ncpus == 0)
+ fail(0, "couldn't find per-CPU statistics");
+}
+
+/*
+ * load statistics, summing across CPUs where needed
+ */
+
+static int
+system_stat_load(void)
+{
+ int i, j;
+ _cpu_stats_t cs;
+ ulong_t *np, *tp;
+
+ /*
+ * Global statistics
+ */
+
+ safe_kstat_read(kc, system_misc_ksp, NULL);
+
+ /*
+ * Per-CPU statistics.
+ */
+
+ for (i = 0; i < ncpus; i++) {
+ if (kstat_read(kc, &cpu_stats_list[i].sys, NULL) == -1 ||
+ kstat_read(kc, &cpu_stats_list[i].vm, NULL) == -1)
+ return (1);
+ if (i == 0) {
+ kstat_copy(&cpu_stats_list[0].sys, &cpu_stats_all.sys,
+ 1);
+ kstat_copy(&cpu_stats_list[0].vm, &cpu_stats_all.vm, 1);
+ } else {
+ kstat_named_t *nkp;
+ kstat_named_t *tkp;
+
+ /*
+ * Other CPUs' statistics are accumulated in
+ * cpu_stats_all, initialized at the first iteration of
+ * the loop.
+ */
+ nkp = (kstat_named_t *)cpu_stats_all.sys.ks_data;
+ tkp = (kstat_named_t *)cpu_stats_list[i].sys.ks_data;
+ for (j = 0; j < cpu_stats_list[i].sys.ks_ndata; j++)
+ (nkp++)->value.ui64 += (tkp++)->value.ui64;
+ nkp = (kstat_named_t *)cpu_stats_all.vm.ks_data;
+ tkp = (kstat_named_t *)cpu_stats_list[i].vm.ks_data;
+ for (j = 0; j < cpu_stats_list[i].vm.ks_ndata; j++)
+ (nkp++)->value.ui64 += (tkp++)->value.ui64;
+ }
+ }
+ return (0);
+}
+
+static int
+kscmp(kstat_t *ks1, kstat_t *ks2)
+{
+ int cmp;
+
+ cmp = strcmp(ks1->ks_module, ks2->ks_module);
+ if (cmp != 0)
+ return (cmp);
+ cmp = ks1->ks_instance - ks2->ks_instance;
+ if (cmp != 0)
+ return (cmp);
+ return (strcmp(ks1->ks_name, ks2->ks_name));
+}
+
+static void
+init_disks(void)
+{
+ struct diskinfo *disk, *prevdisk, *comp;
+ kstat_t *ksp;
+
+ ndisks = 0;
+ disk = &zerodisk;
+
+ /*
+ * Patch the snip in the diskinfo list (see below)
+ */
+ if (snip)
+ lastdisk->next = snip;
+
+ for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
+
+ if (ksp->ks_type != KSTAT_TYPE_IO ||
+ strcmp(ksp->ks_class, "disk") != 0)
+ continue;
+ prevdisk = disk;
+ if (disk->next)
+ disk = disk->next;
+ else {
+ safe_zalloc((void **)&disk->next,
+ sizeof (struct diskinfo), 0);
+ disk = disk->next;
+ disk->next = NULLDISK;
+ }
+ disk->ks = ksp;
+ memset((void *)&disk->kios, 0, sizeof (kstat_io_t));
+ disk->kios.wlastupdate = disk->ks->ks_crtime;
+ disk->kios.rlastupdate = disk->ks->ks_crtime;
+
+ /*
+ * Insertion sort on (ks_module, ks_instance, ks_name)
+ */
+ comp = &zerodisk;
+ while (kscmp(disk->ks, comp->next->ks) > 0)
+ comp = comp->next;
+ if (prevdisk != comp) {
+ prevdisk->next = disk->next;
+ disk->next = comp->next;
+ comp->next = disk;
+ disk = prevdisk;
+ }
+ ndisks++;
+ }
+ /*
+ * Put a snip in the linked list of diskinfos. The idea:
+ * If there was a state change such that now there are fewer
+ * disks, we snip the list and retain the tail, rather than
+ * freeing it. At the next state change, we clip the tail back on.
+ * This prevents a lot of malloc/free activity, and it's simpler.
+ */
+ lastdisk = disk;
+ snip = disk->next;
+ disk->next = NULLDISK;
+
+ firstdisk = zerodisk.next;
+
+ if (ndisks > stats_s4.dk_xfer.dk_xfer_len) {
+ stats_s4.dk_xfer.dk_xfer_len = ndisks;
+ safe_zalloc((void **)&stats_s4.dk_xfer.dk_xfer_val,
+ ndisks * sizeof (int), 1);
+ }
+}
+
+static int
+diskinfo_load(void)
+{
+ struct diskinfo *disk;
+ int i;
+
+ for (disk = firstdisk, i = 0; disk; disk = disk->next, i++) {
+ if (kstat_read(kc, disk->ks, (void *)&disk->kios) == -1)
+ return (1);
+ stats_s4.dk_xfer.dk_xfer_val[i] = disk->kios.reads +
+ disk->kios.writes;
+ }
+ return (0);
+}
+
+static void
+init_net(void)
+{
+ static int sd;
+ mib_item_t *item;
+ mib2_ipAddrEntry_t *ap;
+ char namebuf[KSTAT_STRLEN];
+ struct netinfo *net, *prevnet, *comp;
+ kstat_t *ksp;
+
+ if (sd) {
+ close(sd);
+ }
+ while (netstat_item) {
+ item = netstat_item;
+ netstat_item = netstat_item->next_item;
+ if (item->valp) {
+ free(item->valp);
+ }
+ free(item);
+ }
+ sd = mibopen();
+ if (sd == -1) {
+#ifdef DEBUG
+ fprintf(stderr, "mibopen() failed\n");
+#endif
+ sd = 0;
+ } else {
+ if ((netstat_item = mibget(sd)) == NULL) {
+#ifdef DEBUG
+ fprintf(stderr, "mibget() failed\n");
+#endif
+ close(sd);
+ sd = 0;
+ }
+ }
+#ifdef DEBUG
+ fprintf(stderr, "mibget returned item: %x\n", netstat_item);
+#endif
+
+ nnets = 0;
+ net = &zeronet;
+
+ if (netsnip)
+ lastnet->next = netsnip;
+
+ for (item = netstat_item; item; item = item->next_item) {
+#ifdef DEBUG_MIB
+ fprintf(stderr, "\n--- Item %x ---\n", item);
+ fprintf(stderr,
+ "Group = %d, mib_id = %d, length = %d, valp = 0x%x\n",
+ item->group, item->mib_id, item->length,
+ item->valp);
+#endif
+ if (item->group != MIB2_IP || item->mib_id != MIB2_IP_20)
+ continue;
+ ap = (mib2_ipAddrEntry_t *)item->valp;
+ for (; (char *)ap < item->valp + item->length; ap++) {
+
+ octetstr(namebuf, &ap->ipAdEntIfIndex, 'a');
+#ifdef DEBUG
+ fprintf(stderr, "%s ", namebuf);
+#endif
+ if (strlen(namebuf) == 0)
+ continue;
+ /*
+ * We found a device of interest.
+ * Now, let's see if there's a kstat for it.
+ */
+ if ((ksp = kstat_lookup(kc, NULL, -1, namebuf)) == NULL)
+ continue;
+ if (ksp->ks_type != KSTAT_TYPE_NAMED)
+ continue;
+ if (kstat_read(kc, ksp, NULL) == -1)
+ continue;
+ prevnet = net;
+ if (net->next)
+ net = net->next;
+ else {
+ safe_zalloc((void **)&net->next,
+ sizeof (struct netinfo), 0);
+ net = net->next;
+ net->next = NULLNET;
+ }
+ net->ks = ksp;
+ net->ipackets = kstat_data_lookup(net->ks,
+ "ipackets");
+ net->opackets = kstat_data_lookup(net->ks,
+ "opackets");
+ net->ierrors = kstat_data_lookup(net->ks,
+ "ierrors");
+ net->oerrors = kstat_data_lookup(net->ks,
+ "oerrors");
+ net->collisions = kstat_data_lookup(net->ks,
+ "collisions");
+ /*
+ * Insertion sort on the name
+ */
+ comp = &zeronet;
+ while (strcmp(net->ks->ks_name,
+ comp->next->ks->ks_name) > 0)
+ comp = comp->next;
+ if (prevnet != comp) {
+ prevnet->next = net->next;
+ net->next = comp->next;
+ comp->next = net;
+ net = prevnet;
+ }
+ nnets++;
+ }
+#ifdef DEBUG
+ fprintf(stderr, "\n");
+#endif
+ }
+ /*
+ * Put a snip in the linked list of netinfos. The idea:
+ * If there was a state change such that now there are fewer
+ * nets, we snip the list and retain the tail, rather than
+ * freeing it. At the next state change, we clip the tail back on.
+ * This prevents a lot of malloc/free activity, and it's simpler.
+ */
+ lastnet = net;
+ netsnip = net->next;
+ net->next = NULLNET;
+
+ firstnet = zeronet.next;
+}
+
+static int
+netinfo_load(void)
+{
+ struct netinfo *net;
+
+ if (netstat_item == NULL) {
+#ifdef DEBUG
+ fprintf(stderr, "No net stats\n");
+#endif
+ return (0);
+ }
+
+ stats_s4.if_ipackets =
+ stats_s4.if_opackets =
+ stats_s4.if_ierrors =
+ stats_s4.if_oerrors =
+ stats_s4.if_collisions = 0;
+
+ for (net = firstnet; net; net = net->next) {
+ if (kstat_read(kc, net->ks, NULL) == -1)
+ return (1);
+ if (net->ipackets)
+ stats_s4.if_ipackets += net->ipackets->value.ul;
+ if (net->opackets)
+ stats_s4.if_opackets += net->opackets->value.ul;
+ if (net->ierrors)
+ stats_s4.if_ierrors += net->ierrors->value.ul;
+ if (net->oerrors)
+ stats_s4.if_oerrors += net->oerrors->value.ul;
+ if (net->collisions)
+ stats_s4.if_collisions += net->collisions->value.ul;
+ }
+#ifdef DEBUG
+ fprintf(stderr,
+ "ipackets: %d opackets: %d ierrors: %d oerrors: %d colls: %d\n",
+ stats_s4.if_ipackets,
+ stats_s4.if_opackets,
+ stats_s4.if_ierrors,
+ stats_s4.if_oerrors,
+ stats_s4.if_collisions);
+#endif
+ return (0);
+}
+
+static void
+kstat_copy(kstat_t *src, kstat_t *dst, int fr)
+{
+ if (fr)
+ free(dst->ks_data);
+ *dst = *src;
+ if (src->ks_data != NULL) {
+ safe_zalloc(&dst->ks_data, src->ks_data_size, 0);
+ (void) memcpy(dst->ks_data, src->ks_data, src->ks_data_size);
+ } else {
+ dst->ks_data = NULL;
+ dst->ks_data_size = 0;
+ }
+}
diff --git a/usr/src/cmd/rpcsvc/rstat_v2.x b/usr/src/cmd/rpcsvc/rstat_v2.x
new file mode 100644
index 0000000000..ae94297700
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/rstat_v2.x
@@ -0,0 +1,86 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+%/*
+% * Version 2 rstat; for backwards compatibility only.
+% */
+
+%/*
+% * Copyright (c) 1985, 1990, 1991 by Sun Microsystems, Inc.
+% */
+
+%/* from rstat_v2.x */
+
+#ifdef RPC_HDR
+%
+%#pragma ident "%Z%%M% %I% %E% SMI"
+%
+#endif
+
+const RSTAT_V2_CPUSTATES = 4;
+const RSTAT_V2_DK_NDRIVE = 4;
+
+/*
+ * the cpu stat values
+ */
+
+const RSTAT_V2_CPU_USER = 0;
+const RSTAT_V2_CPU_NICE = 1;
+const RSTAT_V2_CPU_SYS = 2;
+const RSTAT_V2_CPU_IDLE = 3;
+
+/*
+ * GMT since 0:00, January 1, 1970
+ */
+struct rstat_v2_timeval {
+ int tv_sec; /* seconds */
+ int tv_usec; /* and microseconds */
+};
+
+struct statsswtch { /* RSTATVERS_SWTCH */
+ int cp_time[RSTAT_V2_CPUSTATES];
+ int dk_xfer[RSTAT_V2_DK_NDRIVE];
+ int v_pgpgin; /* these are cumulative sum */
+ int v_pgpgout;
+ int v_pswpin;
+ int v_pswpout;
+ int v_intr;
+ int if_ipackets;
+ int if_ierrors;
+ int if_oerrors;
+ int if_collisions;
+ int v_swtch;
+ int avenrun[3];
+ rstat_v2_timeval boottime;
+};
+
+program RSTATPROG {
+ /*
+ * Does not have current time
+ */
+ version RSTATVERS_SWTCH {
+ statsswtch
+ RSTATPROC_STATS(void) = 1;
+
+ unsigned int
+ RSTATPROC_HAVEDISK(void) = 2;
+ } = 2;
+} = 100001;
diff --git a/usr/src/cmd/rpcsvc/rup.c b/usr/src/cmd/rpcsvc/rup.c
new file mode 100644
index 0000000000..56934c9a5a
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/rup.c
@@ -0,0 +1,537 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <rpc/rpc.h>
+#include <netdir.h>
+#include <rpcsvc/rstat.h>
+#include <rpc/pmap_clnt.h>
+
+
+#define MACHINELEN 15 /* length of machine name printed out */
+#define MACHINELENMAX 128 /* maximum machine name length */
+#define AVENSIZE (3 * sizeof (long))
+#define SLOTS 256
+
+int machinecmp();
+int loadcmp();
+int uptimecmp();
+int collectnames();
+int singlehost(); /* returns 1 if rup of given host fails */
+void printsinglehosts();
+void printnames();
+
+
+struct entry {
+ struct netconfig *nconf;
+ struct netbuf *addr;
+ char *machine;
+ struct timeval boottime;
+ time_t curtime;
+ long avenrun[3];
+};
+
+int total_entries;
+int curentry;
+struct entry *entry;
+int vers; /* which version did the broadcasting */
+int lflag; /* load: sort by load average */
+int tflag; /* time: sort by uptime average */
+int hflag; /* host: sort by machine name */
+int dflag; /* debug: list only first n machines */
+int debug;
+
+main(argc, argv)
+ char **argv;
+{
+ statsvar sv;
+ statstime st;
+ int single, nfailed;
+ enum clnt_stat bstat;
+
+ /*
+ * set number of slots to be 256 to begin with,
+ * this is large enough for most subnets but not all
+ */
+
+ curentry = 0;
+ total_entries = SLOTS;
+ entry = malloc(sizeof (struct entry) * total_entries);
+ single = nfailed = 0;
+ while (argc > 1) {
+ if (argv[1][0] != '-') {
+ single++;
+ nfailed += singlehost(argv[1]);
+ } else {
+ switch (argv[1][1]) {
+
+ case 'l':
+ lflag++;
+ break;
+ case 't':
+ tflag++;
+ break;
+ case 'h':
+ hflag++;
+ break;
+ case 'd':
+ dflag++;
+ if (argc < 3)
+ usage();
+ debug = atoi(argv[2]);
+ argc--;
+ argv++;
+ break;
+ default:
+ usage();
+ }
+ }
+ argv++;
+ argc--;
+ }
+ if (single > 0) {
+ if (hflag || tflag || lflag)
+ printsinglehosts();
+ if (nfailed == single) {
+ free(entry);
+ exit(1); /* all hosts we tried failed */
+ } else {
+ free(entry);
+ exit(0);
+ }
+
+ }
+ if (hflag || tflag || lflag) {
+ printf("collecting responses... ");
+ fflush(stdout);
+ }
+
+ sv.cp_time.cp_time_val = (int *)NULL;
+ sv.dk_xfer.dk_xfer_val = (int *)NULL;
+
+ /*
+ * Null out pointers in the statsvar struct
+ * so that we don't follow a random pointer
+ * somewhere when we get our results back.
+ * Set lengths to zero so we don't allocate
+ * some random amount of space we don't need
+ * (in the case where the reply was program
+ * not registered).
+ */
+ sv.cp_time.cp_time_len = 0;
+ sv.cp_time.cp_time_val = (int *)NULL;
+ sv.dk_xfer.dk_xfer_len = 0;
+ sv.dk_xfer.dk_xfer_val = (int *)NULL;
+
+ vers = RSTATVERS_VAR;
+ bstat = rpc_broadcast(RSTATPROG, RSTATVERS_VAR, RSTATPROC_STATS,
+ xdr_void, NULL, xdr_statsvar, (caddr_t)&sv,
+ (resultproc_t)collectnames, (char *)0);
+#ifdef TESTING
+ if (bstat != RPC_SUCCESS)
+ printf("rpc_broadcast for rstat version %d returned %s\n",
+ vers, clnt_sperrno(bstat));
+ fprintf(stderr, "starting second round of broadcasting\n");
+#endif
+ vers = RSTATVERS_TIME;
+ bstat = rpc_broadcast(RSTATPROG, RSTATVERS_TIME, RSTATPROC_STATS,
+ xdr_void, NULL, xdr_statstime, (caddr_t)&st,
+ (resultproc_t)collectnames, (char *)0);
+#ifdef TESTING
+ if (bstat != RPC_SUCCESS)
+ printf("rpc_broadcast for rstat version %d returned %s\n",
+ vers, clnt_sperrno(bstat));
+#endif
+ if (hflag || tflag || lflag)
+ printnames();
+
+
+
+ free(entry);
+ exit(0);
+ /* NOTREACHED */
+}
+
+int
+singlehost(host)
+ char *host;
+{
+ static int debugcnt;
+ enum clnt_stat err;
+ statstime st;
+ statsvar sw_var;
+ bool_t is_var_vers = FALSE;
+
+
+ if (curentry >= total_entries) {
+ struct entry *tmp;
+
+ total_entries += SLOTS;
+ tmp = realloc((struct entry *)entry, sizeof (struct entry)
+ * total_entries);
+ if (tmp == NULL) {
+ return (1);
+ }
+ entry = tmp;
+ }
+
+ sw_var.cp_time.cp_time_val = (int *)NULL;
+ sw_var.dk_xfer.dk_xfer_val = (int *)NULL;
+ err = (enum clnt_stat)callrpc(host, RSTATPROG, RSTATVERS_VAR,
+ RSTATPROC_STATS, xdr_void, 0, xdr_statsvar, &sw_var);
+ if (err == RPC_SUCCESS) {
+ is_var_vers = TRUE;
+ } else if (err == RPC_PROGVERSMISMATCH) {
+ err = (enum clnt_stat)callrpc(host, RSTATPROG, RSTATVERS_TIME,
+ RSTATPROC_STATS, xdr_void, 0, xdr_statstime, &st);
+ if (err != RPC_SUCCESS)
+ goto error;
+ } else
+ goto error;
+
+ debugcnt++;
+ if (!hflag && !lflag && !tflag) {
+ printf("%*.*s ", MACHINELEN, MACHINELEN, host);
+ if (is_var_vers == TRUE)
+ putline(sw_var.curtime.tv_sec, sw_var.boottime,
+ sw_var.avenrun);
+ else
+ putline(st.curtime.tv_sec, st.boottime, st.avenrun);
+ return (0); /* success */
+ } else {
+ entry[curentry].machine = host;
+ if (is_var_vers == FALSE) { /* RSTATVERS_TIME */
+ entry[curentry].boottime.tv_sec = st.boottime.tv_sec;
+ entry[curentry].boottime.tv_usec =
+ st.boottime.tv_usec;
+ entry[curentry].curtime = st.curtime.tv_sec;
+ memcpy(entry[curentry].avenrun, st.avenrun, AVENSIZE);
+ } else { /* RSTATVERS_VAR */
+ entry[curentry].boottime.tv_sec =
+ sw_var.boottime.tv_sec;
+ entry[curentry].boottime.tv_usec =
+ sw_var.boottime.tv_usec;
+ entry[curentry].curtime = sw_var.curtime.tv_sec;
+ memcpy(entry[curentry].avenrun, sw_var.avenrun,
+ AVENSIZE);
+ }
+ }
+ curentry++;
+ if (dflag && debugcnt >= debug)
+ return (1);
+ return (0);
+
+error:
+ fprintf(stderr, "%*.*s: ", MACHINELEN, MACHINELEN, host);
+ clnt_perrno(err);
+ /*
+ * clnt_perrno now prints a newline
+ */
+ /* fprintf(stderr, "\n"); */
+ return (1); /* a failure */
+}
+
+putline(now, boottime, avenrun)
+ time_t now;
+ struct timeval boottime;
+ long avenrun[];
+{
+ int uptime, days, hrs, mins, i;
+
+ uptime = now - boottime.tv_sec;
+ uptime += 30;
+ if (uptime < 0) /* unsynchronized clocks */
+ uptime = 0;
+ days = uptime / (60*60*24);
+ uptime %= (60*60*24);
+ hrs = uptime / (60*60);
+ uptime %= (60*60);
+ mins = uptime / 60;
+
+ printf(" up");
+ if (days > 0)
+ printf(" %2d day%s", days, days > 1 ? "s," : ", ");
+ else
+ printf(" ");
+ if (hrs > 0)
+ printf(" %2d:%02d, ", hrs, mins);
+ else
+ printf(" %2d min%s", mins, mins > 1 ? "s," : ", ");
+
+ /*
+ * Print 1, 5, and 15 minute load averages.
+ * (Found by looking in kernel for avenrun).
+ */
+ printf(" load average:");
+ for (i = 0; i < (AVENSIZE / sizeof (avenrun[0])); i++) {
+ if (i > 0)
+ printf(",");
+ printf(" %.2f", (double)avenrun[i]/FSCALE);
+ }
+ printf("\n");
+}
+
+collectnames(resultsp, taddr, nconf)
+ char *resultsp;
+ struct t_bind *taddr;
+ struct netconfig *nconf;
+{
+ static int debugcnt;
+ register struct entry *entryp, *lim;
+ statstime *st;
+ statsvar *sv;
+ struct nd_hostservlist *hs;
+ extern struct netbuf *netbufdup();
+ extern struct netconfig *netconfigdup();
+ extern int netbufeq();
+
+ /*
+ * need to realloc more space if we have more than 256 machines
+ * that responded to the broadcast
+ */
+
+ if (curentry >= total_entries) {
+ struct entry *tmp;
+
+ total_entries += SLOTS;
+ tmp = realloc((struct entry *)entry, sizeof (struct entry)
+ * total_entries);
+ if (tmp == NULL) {
+ return (1);
+ }
+ entry = tmp;
+ }
+ /*
+ * weed out duplicates
+ */
+ lim = entry + curentry;
+ for (entryp = entry; entryp < lim; entryp++)
+ if (netbufeq(&taddr->addr, entryp->addr))
+ return (0);
+
+ if (vers == RSTATVERS_TIME) {
+ st = (statstime *)resultsp;
+ } else if (vers == RSTATVERS_VAR) {
+ sv = (statsvar *)resultsp;
+ } else {
+ return (0); /* we don't handle this version */
+ }
+ debugcnt++;
+ entry[curentry].nconf = netconfigdup(nconf);
+ entry[curentry].addr = netbufdup(&taddr->addr);
+
+ /*
+ * if raw, print this entry out immediately
+ * otherwise store for later sorting
+ */
+ if (!hflag && !lflag && !tflag) {
+ if (netdir_getbyaddr(nconf, &hs, &taddr->addr) == ND_OK)
+ printf("%*.*s ", MACHINELEN, MACHINELEN,
+ hs->h_hostservs->h_host);
+ else {
+ char *uaddr = taddr2uaddr(nconf, &taddr->addr);
+
+ if (uaddr) {
+ printf(" %*.*s", MACHINELEN, MACHINELEN,
+ uaddr);
+ (void) free(uaddr);
+ } else
+ printf(" %*.*s", MACHINELEN, MACHINELEN,
+ "unknown");
+ }
+ if (vers == RSTATVERS_TIME) {
+ putline(st->curtime.tv_sec, st->boottime, st->avenrun);
+ } else if (vers == RSTATVERS_VAR) {
+ putline(sv->curtime.tv_sec, sv->boottime, sv->avenrun);
+ }
+ } else {
+ if (vers == RSTATVERS_TIME) {
+ entry[curentry].boottime.tv_sec = st->boottime.tv_sec;
+ entry[curentry].boottime.tv_usec =
+ st->boottime.tv_usec;
+ entry[curentry].curtime = st->curtime.tv_sec;
+ memcpy(entry[curentry].avenrun, st->avenrun, AVENSIZE);
+ } else if (vers == RSTATVERS_VAR) {
+ entry[curentry].boottime.tv_sec = sv->boottime.tv_sec;
+ entry[curentry].boottime.tv_usec =
+ sv->boottime.tv_usec;
+ entry[curentry].curtime = sv->curtime.tv_sec;
+ memcpy(entry[curentry].avenrun, sv->avenrun, AVENSIZE);
+ }
+ }
+ curentry++;
+ if (dflag && debugcnt >= debug)
+ return (1);
+ return (0);
+}
+
+void
+printsinglehosts()
+{
+ register int i;
+ register struct entry *ep;
+
+
+ if (hflag)
+ qsort(entry, curentry, sizeof (struct entry), machinecmp);
+ else if (lflag)
+ qsort(entry, curentry, sizeof (struct entry), loadcmp);
+ else
+ qsort(entry, curentry, sizeof (struct entry), uptimecmp);
+ for (i = 0; i < curentry; i++) {
+ ep = &entry[i];
+ printf("%*.*s ", MACHINELEN, MACHINELEN, ep->machine);
+ putline(ep->curtime, ep->boottime, ep->avenrun);
+
+ }
+}
+
+void
+printnames()
+{
+ char buf[MACHINELENMAX+1];
+ struct nd_hostservlist *hs;
+ register int i;
+ register struct entry *ep;
+
+
+ for (i = 0; i < curentry; i++) {
+ ep = &entry[i];
+ if (netdir_getbyaddr(ep->nconf, &hs, ep->addr) == ND_OK)
+ sprintf(buf, "%s", hs->h_hostservs->h_host);
+ else {
+ char *uaddr = taddr2uaddr(ep->nconf, ep->addr);
+
+ if (uaddr) {
+ sprintf(buf, "%s", uaddr);
+ (void) free(uaddr);
+ } else
+ sprintf(buf, "%s", "unknown");
+ }
+ if (ep->machine = (char *)malloc(MACHINELENMAX + 1))
+ strcpy(ep->machine, buf);
+ }
+ printf("\n");
+ printsinglehosts();
+}
+
+machinecmp(a, b)
+ struct entry *a, *b;
+{
+ return (strcmp(a->machine, b->machine));
+}
+
+uptimecmp(a, b)
+ struct entry *a, *b;
+{
+ if (a->boottime.tv_sec != b->boottime.tv_sec)
+ return (a->boottime.tv_sec - b->boottime.tv_sec);
+ else
+ return (a->boottime.tv_usec - b->boottime.tv_usec);
+}
+
+loadcmp(a, b)
+ struct entry *a, *b;
+{
+ register int i;
+
+ for (i = 0; i < AVENSIZE / sizeof (a->avenrun[0]); i++)
+ if (a->avenrun[i] != b->avenrun[i])
+ return (a->avenrun[i] - b->avenrun[i]);
+
+ return (0);
+}
+
+struct netbuf *
+netbufdup(ap)
+ register struct netbuf *ap;
+{
+ register struct netbuf *np;
+
+ np = (struct netbuf *) malloc(sizeof (struct netbuf) + ap->len);
+ if (np) {
+ np->maxlen = np->len = ap->len;
+ np->buf = ((char *)np) + sizeof (struct netbuf);
+ (void) memcpy(np->buf, ap->buf, ap->len);
+ }
+ return (np);
+}
+
+struct netconfig *
+netconfigdup(onp)
+ register struct netconfig *onp;
+{
+ register int nlookupdirs;
+ register struct netconfig *nnp;
+ extern char *strdup();
+
+ nnp = (struct netconfig *)malloc(sizeof (struct netconfig));
+ if (nnp) {
+ nnp->nc_netid = strdup(onp->nc_netid);
+ nnp->nc_semantics = onp->nc_semantics;
+ nnp->nc_flag = onp->nc_flag;
+ nnp->nc_protofmly = strdup(onp->nc_protofmly);
+ nnp->nc_proto = strdup(onp->nc_proto);
+ nnp->nc_device = strdup(onp->nc_device);
+ nnp->nc_nlookups = onp->nc_nlookups;
+ if (onp->nc_nlookups == 0)
+ nnp->nc_lookups = (char **)0;
+ else {
+ register int i;
+
+ nnp->nc_lookups = (char **)malloc(onp->nc_nlookups *
+ sizeof (char *));
+ if (nnp->nc_lookups)
+ for (i = 0; i < onp->nc_nlookups; i++)
+ nnp->nc_lookups[i] =
+ strdup(onp->nc_lookups[i]);
+ }
+ }
+
+ return (nnp);
+}
+
+netbufeq(ap, bp)
+ register struct netbuf *ap, *bp;
+{
+ return (ap->len == bp->len && !memcmp(ap->buf, bp->buf, ap->len));
+}
+
+usage()
+{
+ fprintf(stderr, "Usage: rup [-h] [-l] [-t] [host ...]\n");
+ free(entry);
+ exit(1);
+}
diff --git a/usr/src/cmd/rpcsvc/rusers.c b/usr/src/cmd/rpcsvc/rusers.c
new file mode 100644
index 0000000000..9ab0f7ae0f
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/rusers.c
@@ -0,0 +1,652 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <netconfig.h>
+#include <netdir.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/rusers.h>
+#include <string.h>
+#include <limits.h>
+
+#define NMAX 12 /* These are used as field width specifiers */
+#define LMAX 8 /* when printing. */
+#define HMAX 16 /* "Logged in" host name. */
+
+#define MACHINELEN 16 /* length of machine name printed out */
+#define NUMENTRIES 256
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
+struct entry {
+ int cnt;
+ int idle; /* set to INT_MAX if not present */
+ char *machine;
+ utmp_array users;
+};
+
+static int curentry;
+static int total_entries;
+static struct entry *entry;
+static int hflag; /* host: sort by machine name */
+static int iflag; /* idle: sort by idle time */
+static int uflag; /* users: sort by number of users */
+static int lflag; /* print out long form */
+static int aflag; /* all: list all machines */
+static int dflag; /* debug: list only first n machines */
+static int sorted;
+static int debug;
+static int debugcnt;
+static char *nettype;
+
+static int hcompare(const struct entry *, const struct entry *);
+static int icompare(const struct entry *, const struct entry *);
+static int ucompare(const struct entry *, const struct entry *);
+static int print_info(struct utmpidlearr *, const char *);
+static int print_info_3(utmp_array *, const char *);
+static int collectnames(void *, struct netbuf *, struct netconfig *);
+static int collectnames_3(void *, struct netbuf *, struct netconfig *);
+static void singlehost(char *);
+static void printnames(void);
+static void putline_2(char *, struct utmpidle *);
+static void putline_3(char *, rusers_utmp *);
+static void prttime(uint_t, char *);
+static void usage(void);
+
+/*
+ * rusers [-ahilu] [host...]
+ */
+int
+main(int argc, char *argv[])
+{
+ int c;
+ uint_t errflag = 0;
+ uint_t single = 0;
+ struct utmpidlearr utmpidlearr;
+ utmp_array utmp_array_res;
+
+ curentry = 0;
+ total_entries = NUMENTRIES;
+ entry = malloc(sizeof (struct entry) * total_entries);
+
+ while ((c = getopt(argc, argv, ":ad:hilun:")) != -1) {
+ switch (c) {
+ case 'a':
+ aflag++;
+ break;
+ case 'd':
+ dflag++;
+ debug = atoi(optarg);
+ (void) printf("Will collect %d responses.\n", debug);
+ break;
+ case 'h':
+ hflag++;
+ sorted++;
+ if (iflag || uflag)
+ errflag++;
+ break;
+ case 'i':
+ iflag++;
+ sorted++;
+ if (hflag || uflag)
+ errflag++;
+ break;
+ case 'u':
+ uflag++;
+ sorted++;
+ if (hflag || iflag)
+ errflag++;
+ break;
+ case 'l':
+ lflag++;
+ break;
+ case ':': /* required operand missing */
+ errflag++;
+ break;
+ case 'n':
+ nettype = optarg;
+ break;
+ default:
+ case '?': /* Unrecognized option */
+ errflag++;
+ break;
+ }
+ }
+ if (errflag)
+ usage();
+
+ for (; optind < argc; optind++) {
+ single++;
+ singlehost(argv[optind]);
+ }
+ if (single) {
+ if (sorted)
+ printnames();
+ free(entry);
+ exit(0);
+ }
+
+ if (sorted) {
+ (void) printf("Collecting responses...\n");
+ (void) fflush(stdout);
+ }
+ utmp_array_res.utmp_array_val = NULL;
+ utmp_array_res.utmp_array_len = 0;
+ (void) printf("Sending broadcast for rusersd protocol version 3...\n");
+ (void) rpc_broadcast(RUSERSPROG, RUSERSVERS_3,
+ RUSERSPROC_NAMES, (xdrproc_t)xdr_void, NULL,
+ (xdrproc_t)xdr_utmp_array, (char *)&utmp_array_res,
+ (resultproc_t)collectnames_3, nettype);
+ utmpidlearr.uia_arr = NULL;
+ (void) printf("Sending broadcast for rusersd protocol version 2...\n");
+ (void) rpc_broadcast(RUSERSPROG, RUSERSVERS_IDLE,
+ RUSERSPROC_NAMES, (xdrproc_t)xdr_void, NULL,
+ (xdrproc_t)xdr_utmpidlearr, (char *)&utmpidlearr,
+ (resultproc_t)collectnames, nettype);
+
+ if (sorted)
+ printnames();
+
+ free(entry);
+ return (0);
+}
+
+static void
+singlehost(char *name)
+{
+ enum clnt_stat err;
+ struct utmpidlearr utmpidlearr;
+ utmp_array utmp_array_res;
+
+ if (curentry >= total_entries) {
+ struct entry *tmp;
+
+ total_entries += NUMENTRIES;
+ if ((tmp = realloc(entry, sizeof (struct entry)
+ * total_entries)) == NULL)
+ return;
+ entry = tmp;
+ }
+ utmp_array_res.utmp_array_val = NULL;
+ utmp_array_res.utmp_array_len = 0;
+ err = rpc_call(name, RUSERSPROG, RUSERSVERS_3,
+ RUSERSPROC_NAMES, (xdrproc_t)xdr_void, 0,
+ (xdrproc_t)xdr_utmp_array, (char *)&utmp_array_res,
+ nettype);
+ if (err == RPC_SUCCESS) {
+ (void) print_info_3(&utmp_array_res, name);
+ return;
+ }
+ if (err == RPC_PROGVERSMISMATCH) {
+ utmpidlearr.uia_arr = NULL;
+ err = rpc_call(name, RUSERSPROG, RUSERSVERS_IDLE,
+ RUSERSPROC_NAMES, (xdrproc_t)xdr_void, 0,
+ (xdrproc_t)xdr_utmpidlearr,
+ (char *)&utmpidlearr, nettype);
+ }
+ if (err != RPC_SUCCESS) {
+ (void) fprintf(stderr, "%s: ", name);
+ clnt_perrno(err);
+ return;
+ }
+ (void) print_info(&utmpidlearr, name);
+}
+
+/*
+ * Collect responses from RUSERSVERS_IDLE broadcast, convert to
+ * RUSERSVERS_3 format, and store in entry database.
+ */
+static int
+collectnames(void *resultsp, struct netbuf *raddrp, struct netconfig *nconf)
+{
+ struct utmpidlearr utmpidlearr;
+ struct entry *entryp, *lim;
+ struct nd_hostservlist *hs;
+ char host[MACHINELEN + 1];
+
+ utmpidlearr = *(struct utmpidlearr *)resultsp;
+ if (utmpidlearr.uia_cnt < 1 && !aflag)
+ return (0);
+
+ if (netdir_getbyaddr(nconf, &hs, raddrp)) {
+#ifdef DEBUG
+ netdir_perror("netdir_getbyaddr");
+#endif
+ /* netdir routine couldn't resolve addr;just print out uaddr */
+ (void) sprintf(host, "%.*s", MACHINELEN,
+ taddr2uaddr(nconf, raddrp));
+ } else {
+ (void) sprintf(host, "%.*s", MACHINELEN,
+ hs->h_hostservs->h_host);
+ netdir_free((char *)hs, ND_HOSTSERVLIST);
+ }
+ /*
+ * need to realloc more space if we have more than 256 machines
+ * that respond to broadcast
+ */
+ if (curentry >= total_entries) {
+ struct entry *tmp;
+
+ total_entries += NUMENTRIES;
+ if ((tmp = realloc(entry, sizeof (struct entry)
+ * total_entries)) == NULL)
+ return (1);
+ entry = tmp;
+ }
+
+
+ /*
+ * weed out duplicates
+ */
+ lim = entry + curentry;
+ for (entryp = entry; entryp < lim; entryp++) {
+ if (strcmp(entryp->machine, host) == 0)
+ return (0);
+ }
+ return (print_info((struct utmpidlearr *)resultsp, host));
+}
+
+static int
+print_info(struct utmpidlearr *utmpidlearrp, const char *name)
+{
+ utmp_array *iconvert;
+ int i, cnt, minidle;
+ char host[MACHINELEN + 1];
+ char username[NMAX + 1];
+
+ cnt = utmpidlearrp->uia_cnt;
+ (void) sprintf(host, "%.*s", MACHINELEN, name);
+
+ /*
+ * if raw, print this entry out immediately
+ * otherwise store for later sorting
+ */
+ if (!sorted) {
+ if (lflag && (cnt > 0))
+ for (i = 0; i < cnt; i++)
+ putline_2(host, utmpidlearrp->uia_arr[i]);
+ else {
+ (void) printf("%-*.*s", MACHINELEN, MACHINELEN, host);
+ for (i = 0; i < cnt; i++) {
+ (void) strlcpy(username,
+ utmpidlearrp->uia_arr[i]->ui_utmp.ut_name,
+ NMAX + 1);
+ (void) printf(" %.*s", NMAX, username);
+ }
+ (void) printf("\n");
+ }
+ /* store just the name */
+ entry[curentry].machine = malloc(MACHINELEN + 1);
+ if (entry[curentry].machine == NULL) {
+ (void) fprintf(stderr, "Ran out of memory - exiting\n");
+ exit(1);
+ }
+ (void) strlcpy(entry[curentry].machine, name, MACHINELEN + 1);
+ entry[curentry++].cnt = 0;
+ if (dflag && (++debugcnt >= debug))
+ return (1);
+ return (0);
+ }
+ entry[curentry].machine = malloc(MACHINELEN + 1);
+ if (entry[curentry].machine == NULL) {
+ (void) fprintf(stderr, "Ran out of memory - exiting\n");
+ exit(1);
+ }
+ (void) strlcpy(entry[curentry].machine, name, MACHINELEN + 1);
+ entry[curentry].cnt = cnt;
+ iconvert = &entry[curentry].users;
+ iconvert->utmp_array_len = cnt;
+ iconvert->utmp_array_val = malloc(cnt * sizeof (rusers_utmp));
+ minidle = INT_MAX;
+ for (i = 0; i < cnt; i++) {
+ iconvert->utmp_array_val[i].ut_user =
+ strdup(utmpidlearrp->uia_arr[i]->ui_utmp.ut_name);
+ iconvert->utmp_array_val[i].ut_line =
+ strdup(utmpidlearrp->uia_arr[i]->ui_utmp.ut_line);
+ iconvert->utmp_array_val[i].ut_host =
+ strdup(utmpidlearrp->uia_arr[i]->ui_utmp.ut_host);
+ iconvert->utmp_array_val[i].ut_time =
+ utmpidlearrp->uia_arr[i]->ui_utmp.ut_time;
+ iconvert->utmp_array_val[i].ut_idle =
+ utmpidlearrp->uia_arr[i]->ui_idle;
+ minidle = min(minidle, utmpidlearrp->uia_arr[i]->ui_idle);
+ }
+ entry[curentry].idle = minidle;
+ curentry++;
+ if (dflag && (++debugcnt >= debug))
+ return (1);
+ return (0);
+}
+
+
+/*
+ * Collect responses from RUSERSVERS_3 broadcast.
+ */
+static int
+collectnames_3(void *resultsp, struct netbuf *raddrp, struct netconfig *nconf)
+{
+ utmp_array *uap;
+ struct entry *entryp, *lim;
+ struct nd_hostservlist *hs;
+ char host[MACHINELEN + 1];
+
+ uap = (utmp_array *)resultsp;
+ if (uap->utmp_array_len < 1 && !aflag)
+ return (0);
+
+ if (netdir_getbyaddr(nconf, &hs, raddrp)) {
+#ifdef DEBUG
+ netdir_perror("netdir_getbyaddr");
+#endif
+ /* netdir routine couldn't resolve addr;just print out uaddr */
+ (void) sprintf(host, "%.*s", MACHINELEN,
+ taddr2uaddr(nconf, raddrp));
+ } else {
+ (void) sprintf(host, "%.*s", MACHINELEN,
+ hs->h_hostservs->h_host);
+ netdir_free((char *)hs, ND_HOSTSERVLIST);
+ }
+
+ /*
+ * need to realloc more space if we have more than 256 machines
+ * that respond to broadcast
+ */
+ if (curentry >= total_entries) {
+ struct entry *tmp;
+
+ total_entries += NUMENTRIES;
+ if ((tmp = realloc(entry, sizeof (struct entry)
+ * total_entries)) == NULL)
+ return (1);
+ entry = tmp;
+ }
+
+
+ /*
+ * weed out duplicates
+ */
+ lim = entry + curentry;
+ for (entryp = entry; entryp < lim; entryp++) {
+ if (strcmp(entryp->machine, host) == 0)
+ return (0);
+ }
+ return (print_info_3(uap, host));
+}
+
+static int
+print_info_3(utmp_array *uap, const char *name)
+{
+ int i, cnt, minidle;
+ char host[MACHINELEN + 1];
+
+ cnt = uap->utmp_array_len;
+
+ (void) sprintf(host, "%.*s", MACHINELEN, name);
+
+ /*
+ * if raw, print this entry out immediately
+ * otherwise store for later sorting
+ */
+ if (!sorted) {
+ if (lflag && (cnt > 0))
+ for (i = 0; i < cnt; i++)
+ putline_3(host, &uap->utmp_array_val[i]);
+ else {
+ (void) printf("%-*.*s", MACHINELEN, MACHINELEN, host);
+ for (i = 0; i < cnt; i++)
+ (void) printf(" %.*s", NMAX,
+ uap->utmp_array_val[i].ut_user);
+ (void) printf("\n");
+ }
+ /* store just the name */
+ entry[curentry].machine = malloc(MACHINELEN + 1);
+ if (entry[curentry].machine == NULL) {
+ (void) fprintf(stderr, "Ran out of memory - exiting\n");
+ exit(1);
+ }
+ (void) strlcpy(entry[curentry].machine, name, MACHINELEN + 1);
+ entry[curentry++].cnt = 0;
+ if (dflag && (++debugcnt >= debug))
+ return (1);
+ return (0);
+ }
+
+ entry[curentry].machine = malloc(MACHINELEN + 1);
+ if (entry[curentry].machine == NULL) {
+ (void) fprintf(stderr, "Ran out of memory - exiting\n");
+ exit(1);
+ }
+ (void) strlcpy(entry[curentry].machine, name, MACHINELEN + 1);
+ entry[curentry].cnt = cnt;
+ entry[curentry].users.utmp_array_len = cnt;
+ entry[curentry].users.utmp_array_val = malloc(cnt *
+ sizeof (rusers_utmp));
+ minidle = INT_MAX;
+ for (i = 0; i < cnt; i++) {
+ entry[curentry].users.utmp_array_val[i].ut_user =
+ strdup(uap->utmp_array_val[i].ut_user);
+ entry[curentry].users.utmp_array_val[i].ut_line =
+ strdup(uap->utmp_array_val[i].ut_line);
+ entry[curentry].users.utmp_array_val[i].ut_host =
+ strdup(uap->utmp_array_val[i].ut_host);
+ entry[curentry].users.utmp_array_val[i].ut_time =
+ uap->utmp_array_val[i].ut_time;
+ entry[curentry].users.utmp_array_val[i].ut_idle =
+ uap->utmp_array_val[i].ut_idle;
+ minidle = min(minidle, uap->utmp_array_val[i].ut_idle);
+ }
+ entry[curentry].idle = minidle;
+ curentry++;
+ if (dflag && (++debugcnt >= debug))
+ return (1);
+ return (0);
+}
+
+static void
+printnames(void)
+{
+ int i, j;
+ int (*compare)(const void *, const void *);
+
+ /* the name of the machine should already be in the structure */
+ if (iflag)
+ compare = (int (*)(const void *, const void *))icompare;
+ else if (hflag)
+ compare = (int (*)(const void *, const void *))hcompare;
+ else
+ compare = (int (*)(const void *, const void *))ucompare;
+ qsort(entry, curentry, sizeof (struct entry), compare);
+ for (i = 0; i < curentry; i++) {
+ if (!lflag || (entry[i].cnt < 1)) {
+ (void) printf("%-*.*s", MACHINELEN,
+ MACHINELEN, entry[i].machine);
+ for (j = 0; j < entry[i].cnt; j++)
+ (void) printf(" %.*s", NMAX,
+ entry[i].users.utmp_array_val[j].ut_user);
+ (void) printf("\n");
+ } else {
+ for (j = 0; j < entry[i].cnt; j++)
+ putline_3(entry[i].machine,
+ &entry[i].users.utmp_array_val[j]);
+ }
+ }
+}
+
+static int
+hcompare(const struct entry *a, const struct entry *b)
+{
+ return (strcmp(a->machine, b->machine));
+}
+
+static int
+ucompare(const struct entry *a, const struct entry *b)
+{
+ return (b->cnt - a->cnt);
+}
+
+static int
+icompare(const struct entry *a, const struct entry *b)
+{
+ return (a->idle - b->idle);
+}
+
+static void
+putline_2(char *host, struct utmpidle *uip)
+{
+ char *cbuf;
+ struct ru_utmp *up;
+ char buf[100];
+
+ up = &uip->ui_utmp;
+#define NAMEMAX ((sizeof (up->ut_name) < NMAX) ? NMAX : sizeof (up->ut_name))
+#define NAMEMIN ((sizeof (up->ut_name) > NMAX) ? NMAX : sizeof (up->ut_name))
+ /* Try and align this up nicely */
+#define LINEMAX sizeof (up->ut_line)
+#define HOSTMAX sizeof (up->ut_host)
+ /*
+ * We copy the strings into a buffer because they aren't strictly
+ * speaking strings but byte arrays (and they may not have a
+ * terminating NULL.
+ */
+
+ (void) strncpy(buf, up->ut_name, NAMEMAX);
+ buf[NAMEMIN] = '\0';
+ (void) printf("%-*.*s ", NAMEMAX, NAMEMAX, buf);
+
+ (void) strcpy(buf, host);
+ (void) strcat(buf, ":");
+ (void) strncat(buf, up->ut_line, LINEMAX);
+ buf[MACHINELEN+LINEMAX] = '\0';
+ (void) printf("%-*.*s", MACHINELEN+LINEMAX, MACHINELEN+LINEMAX, buf);
+
+ cbuf = (char *)ctime(&up->ut_time);
+ (void) printf(" %.12s ", cbuf+4);
+ if (uip->ui_idle == INT_MAX)
+ (void) printf(" ??");
+ else
+ prttime(uip->ui_idle, "");
+ if (up->ut_host[0]) {
+ (void) strncpy(buf, up->ut_host, HOSTMAX);
+ buf[HOSTMAX] = '\0';
+ (void) printf(" (%.*s)", HOSTMAX, buf);
+ }
+ (void) putchar('\n');
+}
+
+static void
+putline_3(char *host, rusers_utmp *rup)
+{
+ char *cbuf;
+ char buf[100];
+
+ (void) printf("%-*.*s ", NMAX, NMAX, rup->ut_user);
+ (void) strcpy(buf, host);
+ (void) strcat(buf, ":");
+ (void) strncat(buf, rup->ut_line, LMAX);
+ (void) printf("%-*.*s", MACHINELEN+LMAX, MACHINELEN+LMAX, buf);
+
+ cbuf = (char *)ctime((time_t *)&rup->ut_time);
+ (void) printf(" %.12s ", cbuf+4);
+ if (rup->ut_idle == INT_MAX)
+ (void) printf(" ??");
+ else
+ prttime(rup->ut_idle, "");
+ if (rup->ut_host[0])
+ (void) printf(" (%.*s)", HMAX, rup->ut_host);
+ (void) putchar('\n');
+}
+
+/*
+ * prttime prints a time in hours and minutes.
+ * The character string tail is printed at the end, obvious
+ * strings to pass are "", " ", or "am".
+ */
+static void
+prttime(uint_t tim, char *tail)
+{
+ int didhrs = 0;
+
+ if (tim >= 60) {
+ (void) printf("%3d:", tim/60);
+ didhrs++;
+ } else {
+ (void) printf(" ");
+ }
+ tim %= 60;
+ if (tim > 0 || didhrs) {
+ (void) printf(didhrs && tim < 10 ? "%02d" : "%2d", tim);
+ } else {
+ (void) printf(" ");
+ }
+ (void) printf("%s", tail);
+}
+
+#ifdef DEBUG
+/*
+ * for debugging
+ */
+int
+printit(int i)
+{
+ int j, v;
+
+ (void) printf("%12.12s: ", entry[i].machine);
+ if (entry[i].cnt) {
+ putline_3(entry[i].machine, &entry[i].users.utmp_array_val[0]);
+ for (j = 1; j < entry[i].cnt; j++) {
+ (void) printf("\t");
+ putline_3(entry[i].machine,
+ &entry[i].users.utmp_array_val[j]);
+ }
+ } else
+ (void) printf("\n");
+}
+#endif
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr, "Usage: rusers [-ahilu] [host ...]\n");
+ free(entry);
+ exit(1);
+}
diff --git a/usr/src/cmd/rpcsvc/rusers.xml b/usr/src/cmd/rpcsvc/rusers.xml
new file mode 100644
index 0000000000..c960c83e64
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/rusers.xml
@@ -0,0 +1,116 @@
+<?xml version='1.0'?>
+<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
+
+<!--
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ 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
+ or http://www.opensolaris.org/os/licensing.
+ 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
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+
+ Service manifest for rpc.rusersd
+-->
+
+<service_bundle type='manifest' name='SUNWrcmdr:rusersd'>
+
+<service
+ name='network/rpc/rusers'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='false' />
+
+ <restarter>
+ <service_fmri value='svc:/network/inetd:default' />
+ </restarter>
+
+ <dependency name='rpcbind'
+ grouping='require_all'
+ restart_on='restart'
+ type='service'>
+ <service_fmri value='svc:/network/rpc/bind' />
+ </dependency>
+
+ <exec_method
+ type='method'
+ name='inetd_start'
+ exec='/usr/lib/netsvc/rusers/rpc.rusersd'
+ timeout_seconds='0'>
+ <method_context>
+ <method_credential user='root' group='root' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_offline'
+ exec=':kill_process'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_disable'
+ exec=':kill'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <property_group name='inetd' type='framework'>
+ <stability value='Evolving' />
+ <propval name='name' type='astring' value='rusersd' />
+ <propval name='endpoint_type' type='astring' value='tli' />
+ <propval name='wait' type='boolean' value='true' />
+ <propval name='isrpc' type='boolean' value='true' />
+ <propval name='rpc_low_version' type='integer' value='2' />
+ <propval name='rpc_high_version' type='integer' value='3' />
+ <property name='proto' type='astring' override='true'>
+ <astring_list>
+ <value_node value='datagram_v'/>
+ <value_node value='circuit_v' />
+ </astring_list>
+ </property>
+ </property_group>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ network user name service
+ </loctext>
+ </common_name>
+ <documentation>
+ <manpage title='rpc.rusersd' section='1M'
+ manpath='/usr/share/man' />
+ <manpage title='rusersd' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/rpcsvc/rwall.c b/usr/src/cmd/rpcsvc/rwall.c
new file mode 100644
index 0000000000..6c4a42ef70
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/rwall.c
@@ -0,0 +1,265 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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.
+ */
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * rwall.c
+ * The client rwall program
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <thread.h>
+#include <string.h>
+#include <rpc/rpc.h>
+#include <signal.h>
+#include <pwd.h>
+#include <rpcsvc/rwall.h>
+#include <netconfig.h>
+#include <netdb.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+static void init_who(void);
+static void doall(void);
+static void doit(char *);
+static void *do_one(void *);
+static void usage(void);
+
+#define PATIENCE 10
+#define MAX_THREADS 1024
+
+static mutex_t tty = DEFAULTMUTEX;
+static char who[9] = "???";
+static char *path;
+static mutex_t thr_mtx = DEFAULTMUTEX;
+static int thread_count = 8; /* fudge factor for system threads/fds */
+static int qflag = 0; /* quiet: we don't care about errors */
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int msize;
+ char buf[BUFSIZ+1];
+ register i;
+ char hostname[256];
+ int hflag;
+ struct rlimit rl;
+
+ if (argc < 2)
+ usage();
+
+ if (getrlimit(RLIMIT_NOFILE, &rl) == 0) {
+ rl.rlim_cur = (rl.rlim_max < MAX_THREADS ?
+ rl.rlim_max : MAX_THREADS);
+ (void) setrlimit(RLIMIT_NOFILE, &rl);
+ }
+
+ (void) gethostname(hostname, sizeof (hostname));
+
+ init_who();
+
+ msize = snprintf(buf, sizeof (buf), "From %s@%s: ", who, hostname);
+ while ((i = getchar()) != EOF) {
+ if (msize >= (sizeof (buf) - 1)) {
+ (void) fprintf(stderr, "Message too long\n");
+ exit(1);
+ }
+ buf[msize++] = i;
+ }
+ buf[msize] = '\0';
+ path = buf;
+ hflag = 1;
+ while (argc > 1) {
+ if (argv[1][0] == '-') {
+ switch (argv[1][1]) {
+ case 'h':
+ hflag = 1;
+ break;
+ case 'n':
+ hflag = 0;
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ default:
+ usage();
+ break;
+ }
+ argc--;
+ argv++;
+ continue;
+ }
+ if (hflag) {
+ doit(argv[1]);
+ } else {
+ char *machine, *user, *domain;
+
+ (void) setnetgrent(argv[1]);
+ while (getnetgrent(&machine, &user, &domain)) {
+ if (machine)
+ doit(machine);
+ else
+ doall();
+ }
+ (void) endnetgrent();
+ }
+ argc--;
+ argv++;
+ }
+ thr_exit(NULL);
+ return (0);
+}
+
+static void
+init_who(void)
+{
+ char *wp;
+ struct passwd *pwd;
+
+ wp = getlogin();
+
+ if (wp != NULL)
+ (void) strncpy(who, wp, sizeof (who));
+ else {
+ pwd = getpwuid(getuid());
+ if (pwd)
+ (void) strncpy(who, pwd->pw_name, sizeof (who));
+ }
+
+}
+
+/*
+ * Saw a wild card, so do everything
+ */
+static void
+doall(void)
+{
+ (void) mutex_lock(&tty);
+ (void) fprintf(stderr, "writing to everyone not supported\n");
+ (void) mutex_unlock(&tty);
+}
+
+/*
+ * Fire off a detached thread for each host in the list, if the thread
+ * create fails simply run synchronously.
+ */
+static void
+doit(char *hostname)
+{
+ thread_t tid;
+ char *thread_hostname;
+
+ (void) mutex_lock(&thr_mtx);
+ while (thread_count >= MAX_THREADS) {
+ (void) mutex_unlock(&thr_mtx);
+ (void) sleep(PATIENCE/2);
+ (void) mutex_lock(&thr_mtx);
+ }
+
+ thread_count++;
+ (void) mutex_unlock(&thr_mtx);
+
+ thread_hostname = strdup(hostname);
+ if (thread_hostname == (char *)NULL) {
+ (void) mutex_lock(&tty);
+ (void) fprintf(stderr, "Ran out of memory\n");
+ (void) mutex_unlock(&tty);
+ exit(1);
+ }
+
+ if (thr_create(NULL, 0, do_one, thread_hostname,
+ THR_DETACHED, &tid) != 0) {
+ (void) do_one(thread_hostname);
+ }
+}
+
+static void *
+do_one(void *arg)
+{
+ char *hostname = arg;
+ CLIENT *clnt;
+ struct timeval tp;
+ void *vp = NULL;
+
+#ifdef DEBUG
+ (void) mutex_lock(&tty);
+ (void) fprintf(stderr, "sending message to %s\n%s\n", hostname, path);
+ (void) mutex_unlock(&tty);
+ return (0);
+#endif
+ tp.tv_sec = PATIENCE;
+ tp.tv_usec = 0;
+ clnt = clnt_create_timed(
+ hostname, WALLPROG, WALLVERS, "datagram_v", &tp);
+ if (clnt == NULL) {
+ if (!qflag) {
+ (void) mutex_lock(&tty);
+ (void) fprintf(stderr, "rwall: Can't send to %s\n",
+ hostname);
+ clnt_pcreateerror(hostname);
+ (void) mutex_unlock(&tty);
+ }
+ goto errout;
+ }
+
+ if (wallproc_wall_1(&path, vp, clnt) != RPC_SUCCESS) {
+ if (!qflag) {
+ (void) mutex_lock(&tty);
+ clnt_perror(clnt, hostname);
+ (void) mutex_unlock(&tty);
+ }
+ }
+ clnt_destroy(clnt);
+errout:
+ (void) mutex_lock(&thr_mtx);
+ thread_count--;
+ (void) mutex_unlock(&thr_mtx);
+ free(hostname);
+ return (0);
+}
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr,
+ "Usage: rwall [-q] host .... [-n netgroup ....] [-h host ...]\n");
+ exit(1);
+}
diff --git a/usr/src/cmd/rpcsvc/rwall_subr.c b/usr/src/cmd/rpcsvc/rwall_subr.c
new file mode 100644
index 0000000000..cae42646d2
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/rwall_subr.c
@@ -0,0 +1,119 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * rwall_subr.c
+ * The server procedure for rwalld
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <rpc/rpc.h>
+#include <thread.h>
+#include <rpcsvc/rwall.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <syslog.h>
+#include <string.h>
+
+#define WALL_PROG "/usr/sbin/wall"
+
+static mutex_t wall_mutex = DEFAULTMUTEX;
+static char *oldmsg;
+
+/* ARGSUSED */
+bool_t
+wallproc_wall_1_svc(wrapstring *argp, void *res, struct svc_req *rqstp)
+{
+ char *msg;
+ FILE *fp;
+ int rval;
+ struct stat wall;
+
+ msg = *argp;
+
+ /*
+ * Do not wall the same message twice in case of a retransmission
+ * in the rare case that two walls arrive close enough with
+ * a retransmission we might get a duplicate, but that is OK.
+ */
+ (void) mutex_lock(&wall_mutex);
+ if ((oldmsg != 0) && (strcmp(msg, oldmsg) == 0)) {
+ (void) mutex_unlock(&wall_mutex);
+ return (TRUE);
+ }
+
+ if (oldmsg)
+ free(oldmsg);
+ oldmsg = strdup(msg);
+
+ rval = stat(WALL_PROG, &wall);
+
+ /*
+ * Make sure the wall programs exists, is executeable, and runs
+ */
+ if (rval == -1 || (wall.st_mode & S_IXUSR) == 0 ||
+ (fp = popen(WALL_PROG, "w")) == NULL) {
+ syslog(LOG_NOTICE,
+ "rwall message received but could not execute %s",
+ WALL_PROG);
+ syslog(LOG_NOTICE, "%s", msg);
+#ifdef DEBUG
+ (void) fprintf(stderr,
+ "rwall message received but could not execute %s",
+ WALL_PROG);
+ (void) fprintf(stderr, "%s", msg);
+#endif
+ (void) mutex_unlock(&wall_mutex);
+ return (TRUE);
+ }
+
+ (void) fprintf(fp, "%s", msg);
+ (void) pclose(fp);
+ (void) mutex_unlock(&wall_mutex);
+
+ return (TRUE);
+}
+
+/* ARGSUSED */
+int
+wallprog_1_freeresult(SVCXPRT *transp, xdrproc_t proc, caddr_t arg)
+{
+ return (TRUE);
+}
diff --git a/usr/src/cmd/rpcsvc/spray.c b/usr/src/cmd/rpcsvc/spray.c
new file mode 100644
index 0000000000..62c0fa4790
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/spray.c
@@ -0,0 +1,180 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 1985 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/spray.h>
+#include <sys/poll.h>
+
+enum clnt_stat sprayproc_spray_1nd(/*argp, clnt*/);
+
+#define DEFBYTES 100000 /* default numbers of bytes to send */
+#define MAXPACKETLEN 1514
+#define SPRAYOVERHEAD 86 /* size of rpc packet when size=0 */
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ extern char *optarg;
+ extern int optind;
+ register CLIENT *clnt;
+ unsigned int i;
+ int delay = 0;
+ unsigned int psec, bsec;
+ int buf[SPRAYMAX/4];
+ char msgbuf[256];
+ unsigned int lnth, cnt;
+ sprayarr arr;
+ spraycumul cumul;
+ spraycumul *co;
+ char *host = NULL;
+ char *type;
+
+ if (argc < 2)
+ usage();
+
+ cnt = 0;
+ lnth = SPRAYOVERHEAD;
+ type = "netpath";
+ while (optind < argc) {
+ if (argv[optind][0] == '-') {
+ if ((c = getopt(argc, argv, "d:c:t:l:")) == EOF) {
+ break;
+ }
+ switch (c) {
+ case 'd':
+ delay = atoi(optarg);
+ break;
+ case 'c':
+ cnt = (unsigned int) atoi(optarg);
+ break;
+ case 't':
+ type = optarg;
+ break;
+ case 'l':
+ lnth = (unsigned int) atoi(optarg);
+ break;
+ default:
+ usage();
+ }
+ } else {
+ host = argv[optind++];
+ }
+ }
+ if (host == NULL)
+ usage();
+ clnt = clnt_create(host, SPRAYPROG, SPRAYVERS, type);
+ if (clnt == (CLIENT *)NULL) {
+ sprintf(msgbuf, "spray: cannot clnt_create %s:%s", host, type);
+ clnt_pcreateerror(msgbuf);
+ exit(1);
+ }
+ if (cnt == 0)
+ cnt = DEFBYTES/lnth;
+ if (lnth < SPRAYOVERHEAD)
+ lnth = SPRAYOVERHEAD;
+ else if (lnth >= SPRAYMAX)
+ lnth = SPRAYMAX;
+ if (lnth <= MAXPACKETLEN && lnth % 4 != 2)
+ lnth = ((lnth + 5) / 4) * 4 - 2;
+ arr.sprayarr_len = lnth - SPRAYOVERHEAD;
+ arr.sprayarr_val = (char *) buf;
+ printf("sending %u packets of length %u to %s ...", cnt, lnth, host);
+ fflush(stdout);
+ if (sprayproc_clear_1(NULL, clnt) == NULL) {
+ clnt_perror(clnt, "SPRAYPROC_CLEAR ");
+ exit(1);
+ }
+ for (i = 0; i < cnt; i++) {
+ sprayproc_spray_1nd(&arr, clnt);
+ if (delay > 0)
+ slp(delay);
+ }
+ if ((co = sprayproc_get_1(NULL, clnt)) == NULL) {
+ clnt_perror(clnt, "SPRAYPROC_GET ");
+ exit(1);
+ }
+ cumul = *co;
+ if (cumul.counter < cnt)
+ printf("\n\t%d packets (%.3f%%) dropped by %s\n",
+ cnt - cumul.counter,
+ 100.0 * (cnt - cumul.counter)/cnt, host);
+ else
+ printf("\n\tno packets dropped by %s\n", host);
+ psec = (1000000.0 * cumul.counter)
+ / (1000000.0 * cumul.clock.sec + cumul.clock.usec);
+ bsec = (lnth * 1000000.0 * cumul.counter)/
+ (1000000.0 * cumul.clock.sec + cumul.clock.usec);
+ printf("\t%u packets/sec, %u bytes/sec\n", psec, bsec);
+ exit(0);
+ /* NOTREACHED */
+}
+
+/*
+ * A special call, where the TIMEOUT is 0. So, every call times-out.
+ */
+static struct timeval TIMEOUT = { 0, 0 };
+
+enum clnt_stat
+sprayproc_spray_1nd(argp, clnt)
+ sprayarr *argp;
+ CLIENT *clnt;
+{
+ return (clnt_call(clnt, SPRAYPROC_SPRAY, xdr_sprayarr, (caddr_t)argp,
+ xdr_void, NULL, TIMEOUT));
+}
+
+/* A cheap milliseconds sleep call */
+slp(usecs)
+{
+ static struct pollfd pfds[1] = {
+ 0, POLLIN, 0
+ };
+ pfds[0].fd = fileno(stdout);
+ poll(pfds, 1, usecs/1000);
+}
+
+usage()
+{
+ printf("spray host [-t nettype] [-l lnth] [-c cnt] [-d delay]\n");
+ exit(1);
+}
diff --git a/usr/src/cmd/rpcsvc/spray.xml b/usr/src/cmd/rpcsvc/spray.xml
new file mode 100644
index 0000000000..f11bc45425
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/spray.xml
@@ -0,0 +1,112 @@
+<?xml version='1.0'?>
+<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
+
+<!--
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ 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
+ or http://www.opensolaris.org/os/licensing.
+ 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
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+
+ Service manifest for rpc.sprayd
+-->
+
+<service_bundle type='manifest' name='SUNWrcmdr:sprayd'>
+
+<service
+ name='network/rpc/spray'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='false' />
+
+ <restarter>
+ <service_fmri value='svc:/network/inetd:default' />
+ </restarter>
+
+ <dependency name='rpcbind'
+ grouping='require_all'
+ restart_on='restart'
+ type='service'>
+ <service_fmri value='svc:/network/rpc/bind' />
+ </dependency>
+
+ <exec_method
+ type='method'
+ name='inetd_start'
+ exec='/usr/lib/netsvc/spray/rpc.sprayd'
+ timeout_seconds='0'>
+ <method_context>
+ <method_credential user='root' group='root' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_offline'
+ exec=':kill_process'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_disable'
+ exec=':kill'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <property_group name='inetd' type='framework'>
+ <stability value='Evolving' />
+ <propval name='name' type='astring' value='sprayd' />
+ <propval name='endpoint_type' type='astring' value='tli' />
+ <propval name='proto' type='astring' value='datagram_v'
+ override='true' />
+ <propval name='isrpc' type='boolean' value='true' />
+ <propval name='rpc_low_version' type='integer' value='1' />
+ <propval name='rpc_high_version' type='integer' value='1' />
+ <propval name='wait' type='boolean' value='true' />
+ </property_group>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ RPC spray
+ </loctext>
+ </common_name>
+ <documentation>
+ <manpage title='rpc.sprayd' section='1M'
+ manpath='/usr/share/man' />
+ <manpage title='sprayd' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/rpcsvc/spray_subr.c b/usr/src/cmd/rpcsvc/spray_subr.c
new file mode 100644
index 0000000000..dbaf4cb311
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/spray_subr.c
@@ -0,0 +1,80 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 1985 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <rpc/rpc.h>
+#include <rpcsvc/spray.h>
+
+static spraycumul cumul;
+static spraytimeval start_time;
+
+void *
+sprayproc_spray_1(argp, clnt)
+ sprayarr *argp;
+ CLIENT *clnt;
+{
+ cumul.counter++;
+ return ((void *)0);
+}
+
+spraycumul *
+sprayproc_get_1(argp, clnt)
+ void *argp;
+ CLIENT *clnt;
+{
+ gettimeofday((struct timeval *)&cumul.clock, 0);
+ if (cumul.clock.usec < start_time.usec) {
+ cumul.clock.usec += 1000000;
+ cumul.clock.sec -= 1;
+ }
+ cumul.clock.sec -= start_time.sec;
+ cumul.clock.usec -= start_time.usec;
+ return (&cumul);
+}
+
+void *
+sprayproc_clear_1(argp, clnt)
+ void *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ cumul.counter = 0;
+ gettimeofday((struct timeval *)&start_time, 0);
+ (void) memset((char *)&res, 0, sizeof(res));
+ return ((void *)&res);
+}
diff --git a/usr/src/cmd/rpcsvc/wall.xml b/usr/src/cmd/rpcsvc/wall.xml
new file mode 100644
index 0000000000..b500e35062
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/wall.xml
@@ -0,0 +1,112 @@
+<?xml version='1.0'?>
+<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
+
+<!--
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ 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
+ or http://www.opensolaris.org/os/licensing.
+ 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
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+
+ Service manifest for rpc.rwalld
+-->
+
+<service_bundle type='manifest' name='SUNWrcmdr:walld'>
+
+<service
+ name='network/rpc/wall'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='false' />
+
+ <restarter>
+ <service_fmri value='svc:/network/inetd:default' />
+ </restarter>
+
+ <dependency name='rpcbind'
+ grouping='require_all'
+ restart_on='restart'
+ type='service'>
+ <service_fmri value='svc:/network/rpc/bind' />
+ </dependency>
+
+ <exec_method
+ type='method'
+ name='inetd_start'
+ exec='/usr/lib/netsvc/rwall/rpc.rwalld'
+ timeout_seconds='0'>
+ <method_context>
+ <method_credential user='noaccess' group='noaccess' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_offline'
+ exec=':kill_process'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_disable'
+ exec=':kill'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <property_group name='inetd' type='framework'>
+ <stability value='Evolving' />
+ <propval name='name' type='astring' value='walld' />
+ <propval name='endpoint_type' type='astring' value='tli' />
+ <propval name='proto' type='astring' value='datagram_v'
+ override='true' />
+ <propval name='isrpc' type='boolean' value='true' />
+ <propval name='rpc_low_version' type='integer' value='1' />
+ <propval name='rpc_high_version' type='integer' value='1' />
+ <propval name='wait' type='boolean' value='true' />
+ </property_group>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ network rwall server
+ </loctext>
+ </common_name>
+ <documentation>
+ <manpage title='rpc.rwalld' section='1M'
+ manpath='/usr/share/man' />
+ <manpage title='rwalld' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+
+</service>
+
+</service_bundle>