+diff -Nrup nfs-utils-1.0.11/configure.in nfs-utils-1.0.11.lustre/configure.in
+--- nfs-utils-1.0.11/configure.in 2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/configure.in 2007-05-23 14:35:45.000000000 -0600
+@@ -18,61 +18,14 @@ AC_ARG_WITH(release,
+ RELEASE=$withval,
+ RELEASE=1)
+ AC_SUBST(RELEASE)
+-AC_ARG_WITH(statedir,
+- [ --with-statedir=/foo use state dir /foo [/var/lib/nfs]],
+- statedir=$withval,
+- statedir=/var/lib/nfs)
+- AC_SUBST(statedir)
+-AC_ARG_WITH(statduser,
+- [AC_HELP_STRING([--with-statduser=rpcuser],
+- [statd to run under @<:@rpcuser or nobody@:>@]
+- )],
+- statduser=$withval,
+- if test "x$cross_compiling" = "xno"; then
+- if grep -s '^rpcuser:' /etc/passwd > /dev/null; then
+- statduser=rpcuser
+- else
+- statduser=nobody
+- fi
+- else
+- statduser=nobody
+- fi)
+- AC_SUBST(statduser)
+-AC_ARG_ENABLE(nfsv3,
+- [AC_HELP_STRING([--enable-nfsv3],
+- [enable support for NFSv3 @<:@default=yes@:>@])],
+- enable_nfsv3=$enableval,
+- enable_nfsv3=yes)
+- if test "$enable_nfsv3" = yes; then
+- AC_DEFINE(NFS3_SUPPORTED, 1, [Define this if you want NFSv3 support compiled in])
+- else
+- enable_nfsv3=
+- fi
+- AC_SUBST(enable_nfsv3)
+-AC_ARG_ENABLE(nfsv4,
+- [AC_HELP_STRING([--enable-nfsv4],
+- [enable support for NFSv4 @<:@default=yes@:>@])],
+- enable_nfsv4=$enableval,
+- enable_nfsv4=yes)
+- if test "$enable_nfsv4" = yes; then
+- AC_DEFINE(NFS4_SUPPORTED, 1, [Define this if you want NFSv4 support compiled in])
+- IDMAPD=idmapd
+- else
+- enable_nfsv4=
+- IDMAPD=
+- fi
+- AC_SUBST(IDMAPD)
+- AC_SUBST(enable_nfsv4)
+- AM_CONDITIONAL(CONFIG_NFSV4, [test "$enable_nfsv4" = "yes"])
+ AC_ARG_ENABLE(gss,
+ [AC_HELP_STRING([--enable-gss],
+ [enable support for rpcsec_gss @<:@default=yes@:>@])],
+ enable_gss=$enableval,
+ enable_gss=yes)
+ if test "$enable_gss" = yes; then
+- AC_DEFINE(GSS_SUPPORTED, 1, [Define this if you want rpcsec_gss support compiled in])
+- GSSD=gssd
+- SVCGSSD=svcgssd
++ GSSD=lgssd
++ SVCGSSD=lsvcgssd
+ else
+ enable_gss=
+ GSSD=
+@@ -82,38 +35,6 @@ AC_ARG_ENABLE(gss,
+ AC_SUBST(SVCGSSD)
+ AC_SUBST(enable_gss)
+ AM_CONDITIONAL(CONFIG_GSS, [test "$enable_gss" = "yes"])
+-AC_ARG_ENABLE(kprefix,
+- [AC_HELP_STRING([--enable-kprefix], [install progs as rpc.knfsd etc])],
+- test "$enableval" = "yes" && kprefix=k,
+- kprefix=)
+- AC_SUBST(kprefix)
+-AC_ARG_ENABLE(secure-statd,
+- [AC_HELP_STRING([--enable-secure-statd],
+- [Only lockd can use statd (security)])],
+- test "$enableval" = "yes" && secure_statd=yes,
+- secure_statd=no)
+- if test "$secure_statd" = yes; then
+- AC_DEFINE(RESTRICTED_STATD, 1, [Define this if you want to enable various security checks in statd. These checks basically keep anyone but lockd from using this service.])
+- fi
+- AC_SUBST(secure_statd)
+-AC_ARG_ENABLE(rquotad,
+- [AC_HELP_STRING([--enable-rquotad],
+- [enable rquotad @<:@default=yes@:>@])],
+- enable_rquotad=$enableval,
+- enable_rquotad=yes)
+- if test "$enable_rquotad" = yes; then
+- RQUOTAD=rquotad
+- else
+- RQUOTAD=
+- fi
+- AM_CONDITIONAL(CONFIG_RQUOTAD, [test "$enable_rquotad" = "yes"])
+-
+-AC_ARG_ENABLE(mount,
+- [AC_HELP_STRING([--enable-mount],
+- [Create mount.nfs and don't use the util-linux mount(8) functionality. @<:@default=no@:>@])],
+- enable_mount=$enableval,
+- enable_mount=no)
+- AM_CONDITIONAL(CONFIG_MOUNT, [test "$enable_mount" = "yes"])
+
+ # Check whether user wants TCP wrappers support
+ AC_TCP_WRAPPERS
+@@ -156,50 +77,15 @@ AC_CHECK_FUNC(connect, ,
+ AC_MSG_ERROR(Function 'socket' not found.), $LIBNSL))
+
+ AC_CHECK_LIB(crypt, crypt, [LIBCRYPT="-lcrypt"])
+-if test "$enable_nfsv4" = yes; then
+- AC_CHECK_LIB(event, event_dispatch, [libevent=1], AC_MSG_ERROR([libevent needed for nfsv4 support]))
+- AC_CHECK_LIB(nfsidmap, nfs4_init_name_mapping, [libnfsidmap=1], AC_MSG_ERROR([libnfsidmap needed for nfsv4 support]))
+- AC_CHECK_HEADERS(event.h, ,AC_MSG_ERROR([libevent needed for nfsv4 support]))
+- AC_CHECK_HEADERS(nfsidmap.h, ,AC_MSG_ERROR([libnfsidmap needed for nfsv4 support]))
+- dnl librpcsecgss already has a dependency on libgssapi,
+- dnl but we need to make sure we get the right version
+- if test "$enable_gss" = yes; then
+- PKG_CHECK_MODULES(RPCSECGSS, librpcsecgss >= 0.10, ,
+- [AC_MSG_ERROR([Unable to locate information required to use librpcsecgss. If you have pkgconfig installed, you might try setting environment variable PKG_CONFIG_PATH to /usr/local/lib/pkgconfig])
+- ]
+- )
+- PKG_CHECK_MODULES(GSSAPI, libgssapi >= 0.9)
+- fi
+-
+-fi
+-if test "$knfsd_cv_glibc2" = no; then
+- AC_CHECK_LIB(bsd, daemon, [LIBBSD="-lbsd"])
+-fi
+-AC_CHECK_LIB(blkid, blkid_get_cache, [LIBBLKID="-lblkid"], AC_MSG_ERROR([libblkid needed]))
+-AC_CHECK_HEADER(blkid/blkid.h, , AC_MSG_ERROR([Cannot file libblkid header file blkid/blkid.h]))
++PKG_CHECK_MODULES(GSSAPI, libgssapi >= 0.9)
+ AC_SUBST(LIBSOCKET)
+ AC_SUBST(LIBCRYPT)
+ AC_SUBST(LIBBSD)
+ AC_SUBST(LIBBLKID)
+
+ if test "$enable_gss" = yes; then
+- dnl 'gss' also depends on nfsidmap.h - at least for svcgssd_proc.c
+- AC_CHECK_HEADERS(nfsidmap.h, ,AC_MSG_ERROR([libnfsidmap needed for gss support]))
+- AC_CHECK_HEADERS(spkm3.h, ,AC_MSG_WARN([could not locate SPKM3 header; will not have SPKM3 support]))
+- dnl the nfs4_set_debug function doesn't appear in all version of the library
+- AC_CHECK_LIB(nfsidmap, nfs4_set_debug,
+- AC_DEFINE(HAVE_NFS4_SET_DEBUG,1,
+- [Whether nfs4_set_debug() is present in libnfsidmap]),)
+-
+ dnl Check for Kerberos V5
+ AC_KERBEROS_V5
+-
+- dnl This is not done until here because we need to have KRBLIBS set
+- dnl ("librpcsecgss=1" is so that it doesn't get added to LIBS)
+- AC_CHECK_LIB(rpcsecgss, authgss_create_default, [librpcsecgss=1], AC_MSG_ERROR([librpcsecgss needed for nfsv4 support]), -lgssapi -ldl)
+- AC_CHECK_LIB(rpcsecgss, authgss_set_debug_level,
+- AC_DEFINE(HAVE_AUTHGSS_SET_DEBUG_LEVEL, 1, [Define this if the rpcsec_gss library has the function authgss_set_debug_level]),, -lgssapi -ldl)
+-
+ fi
+
+ dnl *************************************************************
+@@ -311,33 +197,7 @@ AC_SUBST([ACLOCAL_AMFLAGS], ["-I $ac_mac
+
+ AC_CONFIG_FILES([
+ Makefile
+- linux-nfs/Makefile
+- support/Makefile
+- support/export/Makefile
+- support/include/nfs/Makefile
+- support/include/rpcsvc/Makefile
+- support/include/sys/fs/Makefile
+- support/include/sys/Makefile
+- support/include/Makefile
+- support/misc/Makefile
+- support/nfs/Makefile
+- tools/Makefile
+- tools/getiversion/Makefile
+- tools/locktest/Makefile
+- tools/nlmtest/Makefile
+- tools/rpcdebug/Makefile
+- tools/rpcgen/Makefile
+ utils/Makefile
+- utils/exportfs/Makefile
+- utils/gssd/Makefile
+- utils/idmapd/Makefile
+- utils/lockd/Makefile
+- utils/mount/Makefile
+- utils/mountd/Makefile
+- utils/nfsd/Makefile
+- utils/nfsstat/Makefile
+- utils/rquotad/Makefile
+- utils/showmount/Makefile
+- utils/statd/Makefile])
++ utils/gssd/Makefile])
+ AC_OUTPUT
+
+diff -Nrup nfs-utils-1.0.11/Makefile.am nfs-utils-1.0.11.lustre/Makefile.am
+--- nfs-utils-1.0.11/Makefile.am 2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/Makefile.am 2007-05-23 14:35:45.000000000 -0600
+@@ -1,6 +1,6 @@
+ ## Process this file with automake to produce Makefile.in
+
+-SUBDIRS = tools support utils linux-nfs
++SUBDIRS = utils
+
+ MAINTAINERCLEANFILES = Makefile.in
+
+diff -Nrup nfs-utils-1.0.11/utils/gssd/cacheio.c nfs-utils-1.0.11.lustre/utils/gssd/cacheio.c
+--- nfs-utils-1.0.11/utils/gssd/cacheio.c 2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/cacheio.c 2007-05-23 14:36:28.000000000 -0600
+@@ -240,7 +240,8 @@ int qword_get(char **bpp, char *dest, in
+ return -1;
+ while (*bp == ' ') bp++;
+ *bpp = bp;
+- *dest = '\0';
++// why should we clear *dest???
++// *dest = '\0';
+ return len;
+ }
+
+diff -Nrup nfs-utils-1.0.11/utils/gssd/context.c nfs-utils-1.0.11.lustre/utils/gssd/context.c
+--- nfs-utils-1.0.11/utils/gssd/context.c 2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/context.c 2007-05-23 14:36:29.000000000 -0600
+@@ -33,11 +33,14 @@
+ #include <syslog.h>
+ #include <string.h>
+ #include <gssapi/gssapi.h>
+-#include <rpc/rpc.h>
+-#include <rpc/auth_gss.h>
+-#include "gss_util.h"
+-#include "gss_oids.h"
+-#include "err_util.h"
++
++#ifdef _NEW_BUILD_
++# include "lgss_utils.h"
++#else
++# include "gss_util.h"
++# include "gss_oids.h"
++# include "err_util.h"
++#endif
+ #include "context.h"
+
+ int
+diff -Nrup nfs-utils-1.0.11/utils/gssd/context.h nfs-utils-1.0.11.lustre/utils/gssd/context.h
+--- nfs-utils-1.0.11/utils/gssd/context.h 2007-05-23 14:35:21.000000000 -0600
++++ nfs-utils-1.0.11.lustre/utils/gssd/context.h 2007-05-23 14:36:30.000000000 -0600
+@@ -31,8 +31,6 @@
+ #ifndef _CONTEXT_H_
+ #define _CONTEXT_H_
+
+-#include <rpc/rpc.h>
+-
+ /* Hopefully big enough to hold any serialized context */
+ #define MAX_CTX_LEN 4096
+
+diff -Nrup nfs-utils-1.0.11/utils/gssd/context_heimdal.c nfs-utils-1.0.11.lustre/utils/gssd/context_heimdal.c
+--- nfs-utils-1.0.11/utils/gssd/context_heimdal.c 2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/context_heimdal.c 2007-05-23 14:36:30.000000000 -0600
+@@ -43,8 +43,13 @@
+ #ifdef HAVE_COM_ERR_H
+ #include <com_err.h>
+ #endif
+-#include "err_util.h"
+-#include "gss_oids.h"
++
++#ifdef _NEW_BUILD_
++# include "lgss_utils.h"
++#else
++# include "err_util.h"
++# include "gss_oids.h"
++#endif
+ #include "write_bytes.h"
+
+ int write_heimdal_keyblock(char **p, char *end, krb5_keyblock *key)
+diff -Nrup nfs-utils-1.0.11/utils/gssd/context_lucid.c nfs-utils-1.0.11.lustre/utils/gssd/context_lucid.c
+--- nfs-utils-1.0.11/utils/gssd/context_lucid.c 2007-05-23 14:35:21.000000000 -0600
++++ nfs-utils-1.0.11.lustre/utils/gssd/context_lucid.c 2007-05-23 14:36:31.000000000 -0600
+@@ -41,11 +41,7 @@
+ #include <syslog.h>
+ #include <string.h>
+ #include <errno.h>
+-#include "gss_util.h"
+-#include "gss_oids.h"
+-#include "err_util.h"
+-#include "context.h"
+-
++#include <stdint.h>
+ #include <krb5.h>
+ #include <gssapi/gssapi.h>
+ #ifndef OM_uint64
+@@ -53,6 +49,16 @@ typedef uint64_t OM_uint64;
+ #endif
+ #include <gssapi/gssapi_krb5.h>
+
++#ifdef _NEW_BUILD_
++# include "lgss_utils.h"
++#else
++# include "gss_util.h"
++# include "gss_oids.h"
++# include "err_util.h"
++#endif
++#include "write_bytes.h"
++#include "context.h"
++
+ static int
+ write_lucid_keyblock(char **p, char *end, gss_krb5_lucid_key_t *key)
+ {
+@@ -354,6 +360,7 @@ static int
+ prepare_krb5_rfc4121_buffer(gss_krb5_lucid_context_v1_t *lctx,
+ gss_buffer_desc *buf)
+ {
++ static int constant_two = 2;
+ char *p, *end;
+ uint32_t v2_flags = 0;
+ gss_krb5_lucid_key_t enc_key;
+@@ -372,7 +379,7 @@ prepare_krb5_rfc4121_buffer(gss_krb5_luc
+ end = buf->value + MAX_CTX_LEN;
+
+ /* Version 2 */
+- if (WRITE_BYTES(&p, end, lctx->initiate)) goto out_err;
++ if (WRITE_BYTES(&p, end, constant_two)) goto out_err;
+ if (WRITE_BYTES(&p, end, lctx->endtime)) goto out_err;
+
+ if (lctx->initiate)
+@@ -387,7 +394,7 @@ prepare_krb5_rfc4121_buffer(gss_krb5_luc
+ if (WRITE_BYTES(&p, end, lctx->send_seq)) goto out_err;
+
+ /* Protocol 0 here implies DES3 or RC4 */
+- printerr(2, "%s: protocol %d\n", __FUNCTION__, lctx->protocol);
++ printerr(3, "protocol %d\n", lctx->protocol);
+ if (lctx->protocol == 0) {
+ enctype = lctx->rfc1964_kd.ctx_key.type;
+ #ifdef HAVE_HEIMDAL
+@@ -415,8 +422,8 @@ prepare_krb5_rfc4121_buffer(gss_krb5_luc
+ }
+ numkeys = 3;
+ }
+- printerr(2, "%s: serializing %d keys with enctype %d and size %d\n",
+- __FUNCTION__, numkeys, enctype, keysize);
++ printerr(3, "serializing %d keys with enctype %d and size %d\n",
++ numkeys, enctype, keysize);
+ if (WRITE_BYTES(&p, end, enctype)) goto out_err;
+ if (WRITE_BYTES(&p, end, keysize)) goto out_err;
+ if (WRITE_BYTES(&p, end, numkeys)) goto out_err;
+@@ -434,14 +441,25 @@ prepare_krb5_rfc4121_buffer(gss_krb5_luc
+ goto out_err;
+
+ /* Kc */
+- if (derive_key_lucid(&lctx->rfc1964_kd.ctx_key,
+- &derived_key,
+- KG_USAGE_SIGN, KEY_USAGE_SEED_CHECKSUM))
+- goto out_err;
+- if (write_bytes(&p, end, derived_key.data,
+- derived_key.length))
+- goto out_err;
+- free(derived_key.data);
++ /*
++ * RC4 is special, it dosen't need key derivation. Actually
++ * the Ke is based on plain text. Here we just let all three
++ * key identical, kernel will handle everything. --ericm
++ */
++ if (lctx->rfc1964_kd.ctx_key.type == ENCTYPE_ARCFOUR_HMAC) {
++ if (write_bytes(&p, end, lctx->rfc1964_kd.ctx_key.data,
++ lctx->rfc1964_kd.ctx_key.length))
++ goto out_err;
++ } else {
++ if (derive_key_lucid(&lctx->rfc1964_kd.ctx_key,
++ &derived_key,
++ KG_USAGE_SIGN, KEY_USAGE_SEED_CHECKSUM))
++ goto out_err;
++ if (write_bytes(&p, end, derived_key.data,
++ derived_key.length))
++ goto out_err;
++ free(derived_key.data);
++ }
+ } else {
+ gss_krb5_lucid_key_t *keyptr;
+ uint32_t sign_usage, seal_usage;
+@@ -451,6 +469,7 @@ prepare_krb5_rfc4121_buffer(gss_krb5_luc
+ else
+ keyptr = &lctx->cfx_kd.ctx_key;
+
++#if 0
+ if (lctx->initiate == 1) {
+ sign_usage = KG_USAGE_INITIATOR_SIGN;
+ seal_usage = KG_USAGE_INITIATOR_SEAL;
+@@ -458,6 +477,19 @@ prepare_krb5_rfc4121_buffer(gss_krb5_luc
+ sign_usage = KG_USAGE_ACCEPTOR_SIGN;
+ seal_usage = KG_USAGE_ACCEPTOR_SEAL;
+ }
++#else
++ /* FIXME
++ * These are from rfc4142, but I don't understand: if we supply
++ * different 'usage' value for client & server, then the peers
++ * will have different derived keys. How could this work?
++ *
++ * Here we simply use old SIGN/SEAL values until we find the
++ * answer. --ericm
++ * FIXME
++ */
++ sign_usage = KG_USAGE_SIGN;
++ seal_usage = KG_USAGE_SEAL;
++#endif
+
+ /* derive and send down: Ke, Ki, and Kc */
+
+@@ -515,7 +547,7 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss
+ gss_krb5_lucid_context_v1_t *lctx = 0;
+ int retcode = 0;
+
+- printerr(2, "DEBUG: %s: lucid version!\n", __FUNCTION__);
++ printerr(3, "lucid version!\n");
+ maj_stat = gss_export_lucid_sec_context(&min_stat, &ctx,
+ 1, &return_ctx);
+ if (maj_stat != GSS_S_COMPLETE) {
+diff -Nrup nfs-utils-1.0.11/utils/gssd/context_mit.c nfs-utils-1.0.11.lustre/utils/gssd/context_mit.c
+--- nfs-utils-1.0.11/utils/gssd/context_mit.c 2007-05-23 14:35:21.000000000 -0600
++++ nfs-utils-1.0.11.lustre/utils/gssd/context_mit.c 2007-05-23 14:36:32.000000000 -0600
+@@ -39,10 +39,14 @@
+ #include <errno.h>
+ #include <gssapi/gssapi.h>
+ #include <rpc/rpc.h>
+-#include <rpc/auth_gss.h>
+-#include "gss_util.h"
+-#include "gss_oids.h"
+-#include "err_util.h"
++
++#ifdef _NEW_BUILD_
++# include "lgss_utils.h"
++#else
++# include "gss_util.h"
++# include "gss_oids.h"
++# include "err_util.h"
++#endif
+ #include "context.h"
+
+ #include <krb5.h>
+@@ -333,12 +337,7 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss
+ * keydata-2; ( Ki (Kseq for DES3) )
+ * keydata-3; ( Kc (derived checksum key) )
+ */
+- if (kctx->initiate) {
+- if (WRITE_BYTES(&p, end, constant_one)) goto out_err;
+- }
+- else {
+- if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
+- }
++ if (WRITE_BYTES(&p, end, constant_two)) goto out_err;
+ if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err;
+
+ /* Only applicable flag for this is initiator */
+diff -Nrup nfs-utils-1.0.11/utils/gssd/context_spkm3.c nfs-utils-1.0.11.lustre/utils/gssd/context_spkm3.c
+--- nfs-utils-1.0.11/utils/gssd/context_spkm3.c 2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/context_spkm3.c 2007-05-23 14:36:32.000000000 -0600
+@@ -33,8 +33,6 @@
+ #include <syslog.h>
+ #include <string.h>
+ #include <gssapi/gssapi.h>
+-#include <rpc/rpc.h>
+-#include <rpc/auth_gss.h>
+ #include "gss_util.h"
+ #include "gss_oids.h"
+ #include "err_util.h"
+diff -Nrup nfs-utils-1.0.11/utils/gssd/err_util.c nfs-utils-1.0.11.lustre/utils/gssd/err_util.c
+--- nfs-utils-1.0.11/utils/gssd/err_util.c 2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/err_util.c 2007-05-23 14:36:33.000000000 -0600
+@@ -32,6 +32,8 @@
+ #include <stdarg.h>
+ #include <syslog.h>
+ #include <string.h>
++#include <fcntl.h>
++#include <ctype.h>
+ #include "err_util.h"
+
+ static int verbosity = 0;
+@@ -91,3 +93,40 @@ printit:
+ /* reset the buffer */
+ memset(message_buf, 0, sizeof(message_buf));
+ }
++
++void print_hexl(int pri, unsigned char *cp, int length)
++{
++ int i, j, jm;
++ unsigned char c;
++
++ printerr(pri, "length %d\n",length);
++ printerr(pri, "\n");
++
++ for (i = 0; i < length; i += 0x10) {
++ printerr(pri, " %04x: ", (u_int)i);
++ jm = length - i;
++ jm = jm > 16 ? 16 : jm;
++
++ for (j = 0; j < jm; j++) {
++ if ((j % 2) == 1)
++ printerr(pri,"%02x ", (u_int)cp[i+j]);
++ else
++ printerr(pri,"%02x", (u_int)cp[i+j]);
++ }
++ for (; j < 16; j++) {
++ if ((j % 2) == 1)
++ printerr(pri," ");
++ else
++ printerr(pri," ");
++ }
++ printerr(pri," ");
++
++ for (j = 0; j < jm; j++) {
++ c = cp[i+j];
++ c = isprint(c) ? c : '.';
++ printerr(pri,"%c", c);
++ }
++ printerr(pri,"\n");
++ }
++}
++
+diff -Nrup nfs-utils-1.0.11/utils/gssd/err_util.h nfs-utils-1.0.11.lustre/utils/gssd/err_util.h
+--- nfs-utils-1.0.11/utils/gssd/err_util.h 2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/err_util.h 2007-05-23 14:36:33.000000000 -0600
+@@ -33,5 +33,6 @@
+
+ void initerr(char *progname, int verbosity, int fg);
+ void printerr(int priority, char *format, ...);
++void print_hexl(int pri, unsigned char *cp, int length);
+
+ #endif /* _ERR_UTIL_H_ */
+diff -Nrup nfs-utils-1.0.11/utils/gssd/gss_clnt_send_err.c nfs-utils-1.0.11.lustre/utils/gssd/gss_clnt_send_err.c
+--- nfs-utils-1.0.11/utils/gssd/gss_clnt_send_err.c 2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/gss_clnt_send_err.c 2007-05-23 14:35:45.000000000 -0600
+@@ -47,6 +47,7 @@
+ #include "gssd.h"
+ #include "write_bytes.h"
+
++#if 0
+ char pipefsdir[PATH_MAX] = GSSD_PIPEFS_DIR;
+
+ static void
+@@ -102,3 +103,4 @@ main(int argc, char *argv[])
+ }
+ exit(0);
+ }
++#endif
+diff -Nrup nfs-utils-1.0.11/utils/gssd/gssd.c nfs-utils-1.0.11.lustre/utils/gssd/gssd.c
+--- nfs-utils-1.0.11/utils/gssd/gssd.c 2007-05-23 14:35:21.000000000 -0600
++++ nfs-utils-1.0.11.lustre/utils/gssd/gssd.c 2007-05-23 14:36:34.000000000 -0600
+@@ -38,9 +38,12 @@
+
+ #include "config.h"
+
++#include <sys/types.h>
+ #include <sys/param.h>
+ #include <sys/socket.h>
+-#include <rpc/rpc.h>
++#include <sys/wait.h>
++#include <sys/ipc.h>
++#include <sys/sem.h>
+
+ #include <unistd.h>
+ #include <err.h>
+@@ -48,23 +51,107 @@
+ #include <stdlib.h>
+ #include <string.h>
+ #include <signal.h>
++#include <errno.h>
+ #include "gssd.h"
+ #include "err_util.h"
+ #include "gss_util.h"
+ #include "krb5_util.h"
++#include "lsupport.h"
+
+ char pipefs_dir[PATH_MAX] = GSSD_PIPEFS_DIR;
+ char pipefs_nfsdir[PATH_MAX] = GSSD_PIPEFS_DIR;
+ char keytabfile[PATH_MAX] = GSSD_DEFAULT_KEYTAB_FILE;
+ char ccachedir[PATH_MAX] = GSSD_DEFAULT_CRED_DIR;
+ int use_memcache = 0;
++int lgssd_mutex_downcall = -1;
+
+-void
+-sig_die(int signal)
++static int lgssd_create_mutex(int *semid)
++{
++ int id;
++ int arg;
++
++ id = semget(IPC_PRIVATE, 1, IPC_CREAT);
++ if (id == -1) {
++ printerr(0, "semget: %s\n", strerror(errno));
++ return -1;
++ }
++
++ arg = 1;
++ if (semctl(id, 0, SETVAL, arg) != 0) {
++ printerr(0, "semctl: %s\n", strerror(errno));
++ semctl(id, 1, IPC_RMID, arg);
++ return -1;
++ }
++
++ *semid = id;
++ return 0;
++}
++
++void lgssd_init_mutexs(void)
++{
++ if (lgssd_create_mutex(&lgssd_mutex_downcall)) {
++ printerr(0, "can't create downcall mutex\n");
++ exit(1);
++ }
++}
++
++void lgssd_fini_mutexs(void)
++{
++ int arg = 0;
++
++ if (lgssd_mutex_downcall != -1)
++ semctl(lgssd_mutex_downcall, 1, IPC_RMID, arg);
++}
++
++void lgssd_mutex_get(int semid)
++{
++ struct sembuf op[1] = { {0, -1, SEM_UNDO} };
++ int rc;
++
++ rc = semop(semid, op, 1);
++ if (rc != 0) {
++ printerr(0, "exit on mutex_get err %d: %s\n",
++ rc, strerror(errno));
++ exit(1);
++ }
++}
++
++void lgssd_mutex_put(int semid)
+ {
++ struct sembuf op[1] = { {0, 1, 0} };
++ int rc;
++
++ rc = semop(semid, op, 1);
++ if (rc != 0) {
++ printerr(0, "ignore mutex_put err %d: %s\n",
++ rc, strerror(errno));
++ }
++}
++
++static void lgssd_cleanup(void)
++{
++ pid_t child_pid;
++
++ /* make sure all children finished */
++ while (1) {
++ child_pid = waitpid(-1, NULL, 0);
++ if (child_pid < 0)
++ break;
++
++ printerr(3, "cleanup: child %d terminated\n", child_pid);
++ }
++
++ lgssd_fini_mutexs();
++
+ /* destroy krb5 machine creds */
+ gssd_destroy_krb5_machine_creds();
++}
++
++void
++sig_die(int signal)
++{
+ printerr(1, "exiting on signal %d\n", signal);
++ lgssd_cleanup();
+ exit(1);
+ }
+
+@@ -79,7 +166,7 @@ sig_hup(int signal)
+ static void
+ usage(char *progname)
+ {
+- fprintf(stderr, "usage: %s [-f] [-v] [-r] [-p pipefsdir] [-k keytab] [-d ccachedir]\n",
++ fprintf(stderr, "usage: %s [-f] [-v] [-p pipefsdir] [-k keytab] [-d ccachedir]\n",
+ progname);
+ exit(1);
+ }
+@@ -89,7 +176,6 @@ main(int argc, char *argv[])
+ {
+ int fg = 0;
+ int verbosity = 0;
+- int rpc_verbosity = 0;
+ int opt;
+ extern char *optarg;
+ char *progname;
+@@ -99,18 +185,12 @@ main(int argc, char *argv[])
+ case 'f':
+ fg = 1;
+ break;
+- case 'm':
+- /* Accept but ignore this. Now the default. */
+- break;
+ case 'M':
+ use_memcache = 1;
+ break;
+ case 'v':
+ verbosity++;
+ break;
+- case 'r':
+- rpc_verbosity++;
+- break;
+ case 'p':
+ strncpy(pipefs_dir, optarg, sizeof(pipefs_dir));
+ if (pipefs_dir[sizeof(pipefs_dir)-1] != '\0')
+@@ -131,10 +211,6 @@ main(int argc, char *argv[])
+ break;
+ }
+ }
+- snprintf(pipefs_nfsdir, sizeof(pipefs_nfsdir), "%s/%s",
+- pipefs_dir, GSSD_SERVICE_NAME);
+- if (pipefs_nfsdir[sizeof(pipefs_nfsdir)-1] != '\0')
+- errx(1, "pipefs_nfsdir path name too long");
+
+ if ((progname = strrchr(argv[0], '/')))
+ progname++;
+@@ -142,30 +218,42 @@ main(int argc, char *argv[])
+ progname = argv[0];
+
+ initerr(progname, verbosity, fg);
+-#ifdef HAVE_AUTHGSS_SET_DEBUG_LEVEL
+- authgss_set_debug_level(rpc_verbosity);
+-#else
+- if (rpc_verbosity > 0)
+- printerr(0, "Warning: rpcsec_gss library does not "
+- "support setting debug level\n");
+-#endif
+
+ if (gssd_check_mechs() != 0)
+ errx(1, "Problem with gssapi library");
+
++ if (gssd_get_local_realm())
++ errx(1, "get local realm");
++
+ if (!fg && daemon(0, 0) < 0)
+ errx(1, "fork");
+
++ /* This should be checked _after_ daemon(), because we need to own
++ * the undo-able semaphore by this process
++ */
++ gssd_init_unique(GSSD_CLI);
++
++ /* Process keytab file and get machine credentials. This will modify
++ * disk status so do it after we are sure we are the only instance
++ */
++ if (gssd_refresh_krb5_machine_creds())
++ return -1;
++
+ signal(SIGINT, sig_die);
+ signal(SIGTERM, sig_die);
+ signal(SIGHUP, sig_hup);
+
+- /* Process keytab file and get machine credentials */
+- gssd_refresh_krb5_machine_creds();
++#if 0
+ /* Determine Kerberos information from the kernel */
+ gssd_obtain_kernel_krb5_info();
++#endif
++
++ lgssd_init_mutexs();
++
++ printerr(0, "lgssd initialized and ready to serve\n");
++ lgssd_run();
+
+- gssd_run();
+- printerr(0, "gssd_run returned!\n");
+- abort();
++ lgssd_cleanup();
++ printerr(0, "lgssd exiting\n");
++ return 0;
+ }
+diff -Nrup nfs-utils-1.0.11/utils/gssd/gssd.h nfs-utils-1.0.11.lustre/utils/gssd/gssd.h
+--- nfs-utils-1.0.11/utils/gssd/gssd.h 2007-05-23 14:35:21.000000000 -0600
++++ nfs-utils-1.0.11.lustre/utils/gssd/gssd.h 2007-05-23 14:36:34.000000000 -0600
+@@ -48,8 +48,13 @@
+ #define GSSD_DEFAULT_CRED_PREFIX "krb5cc_"
+ #define GSSD_DEFAULT_MACHINE_CRED_SUFFIX "machine"
+ #define GSSD_DEFAULT_KEYTAB_FILE "/etc/krb5.keytab"
+-#define GSSD_SERVICE_NAME "nfs"
+-#define GSSD_SERVICE_NAME_LEN 3
++#define GSSD_SERVICE_MDS "lustre_mds"
++#define GSSD_SERVICE_OSS "lustre_oss"
++#define GSSD_SERVICE_MDS_NAMELEN 10
++#define GSSD_SERVICE_OSS_NAMELEN 10
++
++#define LUSTRE_ROOT_NAME "lustre_root"
++#define LUSTRE_ROOT_NAMELEN 11
+
+ /*
+ * The gss mechanisms that we can handle
+@@ -59,9 +64,9 @@ enum {AUTHTYPE_KRB5, AUTHTYPE_SPKM3, AUT
+
+
+ extern char pipefs_dir[PATH_MAX];
+-extern char pipefs_nfsdir[PATH_MAX];
+ extern char keytabfile[PATH_MAX];
+ extern char ccachedir[PATH_MAX];
++extern char gethostname_ex[PATH_MAX];
+ extern int use_memcache;
+
+ TAILQ_HEAD(clnt_list_head, clnt_info) clnt_list;
+@@ -71,10 +76,6 @@ struct clnt_info {
+ char *dirname;
+ int dir_fd;
+ char *servicename;
+- char *servername;
+- int prog;
+- int vers;
+- char *protocol;
+ int krb5_fd;
+ int krb5_poll_index;
+ int spkm3_fd;
+@@ -85,8 +86,14 @@ void init_client_list(void);
+ int update_client_list(void);
+ void handle_krb5_upcall(struct clnt_info *clp);
+ void handle_spkm3_upcall(struct clnt_info *clp);
+-int gssd_acquire_cred(char *server_name);
+-void gssd_run(void);
++void lgssd_run(void);
++
++
++extern int lgssd_mutex_downcall;
+
++void lgssd_init_mutexs(void);
++void lgssd_fini_mutexs(void);
++void lgssd_mutex_get(int semid);
++void lgssd_mutex_put(int semid);
+
+ #endif /* _RPC_GSSD_H_ */
+diff -Nrup nfs-utils-1.0.11/utils/gssd/gssd_main_loop.c nfs-utils-1.0.11.lustre/utils/gssd/gssd_main_loop.c
+--- nfs-utils-1.0.11/utils/gssd/gssd_main_loop.c 2007-05-23 14:35:21.000000000 -0600
++++ nfs-utils-1.0.11.lustre/utils/gssd/gssd_main_loop.c 2007-05-23 14:36:35.000000000 -0600
+@@ -94,11 +94,13 @@ scan_poll_results(int ret)
+ };
+
+ void
+-gssd_run()
++lgssd_run()
+ {
+ int ret;
+ struct sigaction dn_act;
+ int fd;
++ time_t child_check = 0;
++ pid_t child_pid;
+
+ /* Taken from linux/Documentation/dnotify.txt: */
+ dn_act.sa_sigaction = dir_notify_handler;
+@@ -106,10 +108,10 @@ gssd_run()
+ dn_act.sa_flags = SA_SIGINFO;
+ sigaction(DNOTIFY_SIGNAL, &dn_act, NULL);
+
+- if ((fd = open(pipefs_nfsdir, O_RDONLY)) == -1) {
++ if ((fd = open(pipefs_dir, O_RDONLY)) == -1) {
+ printerr(0, "ERROR: failed to open %s: %s\n",
+- pipefs_nfsdir, strerror(errno));
+- exit(1);
++ pipefs_dir, strerror(errno));
++ return;
+ }
+ fcntl(fd, F_SETSIG, DNOTIFY_SIGNAL);
+ fcntl(fd, F_NOTIFY, DN_CREATE|DN_DELETE|DN_MODIFY|DN_MULTISHOT);
+@@ -119,12 +121,30 @@ gssd_run()
+ while (1) {
+ while (dir_changed) {
+ dir_changed = 0;
++ printerr(2, "pipefs root dir changed\n");
+ if (update_client_list()) {
+ printerr(0, "ERROR: couldn't update "
+ "client list\n");
+- exit(1);
++ goto out;
+ }
+ }
++
++ /* every 5s cleanup possible zombies of child processes */
++ if (time(NULL) - child_check >= 5) {
++ printerr(3, "check zombie children...\n");
++
++ while (1) {
++ child_pid = waitpid(-1, NULL, WNOHANG);
++ if (child_pid <= 0)
++ break;
++
++ printerr(2, "terminate zombie child: %d\n",
++ child_pid);
++ }
++
++ child_check = time(NULL);
++ }
++
+ /* race condition here: dir_changed could be set before we
+ * enter the poll, and we'd never notice if it weren't for the
+ * timeout. */
+@@ -139,6 +159,7 @@ gssd_run()
+ scan_poll_results(ret);
+ }
+ }
++out:
+ close(fd);
+ return;
+ }
+diff -Nrup nfs-utils-1.0.11/utils/gssd/gssd_proc.c nfs-utils-1.0.11.lustre/utils/gssd/gssd_proc.c
+--- nfs-utils-1.0.11/utils/gssd/gssd_proc.c 2007-05-23 14:35:21.000000000 -0600
++++ nfs-utils-1.0.11.lustre/utils/gssd/gssd_proc.c 2007-05-23 14:36:35.000000000 -0600
+@@ -43,7 +43,6 @@
+ #endif
+ #include "config.h"
+ #include <sys/param.h>
+-#include <rpc/rpc.h>
+ #include <sys/stat.h>
+ #include <sys/socket.h>
+ #include <arpa/inet.h>
+@@ -69,6 +68,7 @@
+ #include "gss_oids.h"
+ #include "krb5_util.h"
+ #include "context.h"
++#include "lsupport.h"
+
+ /*
+ * pollarray:
+@@ -99,86 +99,10 @@ struct pollfd * pollarray;
+
+ int pollsize; /* the size of pollaray (in pollfd's) */
+
+-/* XXX buffer problems: */
+-static int
+-read_service_info(char *info_file_name, char **servicename, char **servername,
+- int *prog, int *vers, char **protocol) {
+-#define INFOBUFLEN 256
+- char buf[INFOBUFLEN];
+- static char dummy[128];
+- int nbytes;
+- static char service[128];
+- static char address[128];
+- char program[16];
+- char version[16];
+- char protoname[16];
+- in_addr_t inaddr;
+- int fd = -1;
+- struct hostent *ent = NULL;
+- int numfields;
+-
+- *servicename = *servername = *protocol = NULL;
+-
+- if ((fd = open(info_file_name, O_RDONLY)) == -1) {
+- printerr(0, "ERROR: can't open %s: %s\n", info_file_name,
+- strerror(errno));
+- goto fail;
+- }
+- if ((nbytes = read(fd, buf, INFOBUFLEN)) == -1)
+- goto fail;
+- close(fd);
+-
+- numfields = sscanf(buf,"RPC server: %127s\n"
+- "service: %127s %15s version %15s\n"
+- "address: %127s\n"
+- "protocol: %15s\n",
+- dummy,
+- service, program, version,
+- address,
+- protoname);
+-
+- if (numfields == 5) {
+- strcpy(protoname, "tcp");
+- } else if (numfields != 6) {
+- goto fail;
+- }
+-
+- /* check service, program, and version */
+- if(memcmp(service, "nfs", 3)) return -1;
+- *prog = atoi(program + 1); /* skip open paren */
+- *vers = atoi(version);
+- if((*prog != 100003) || ((*vers != 2) && (*vers != 3) && (*vers != 4)))
+- goto fail;
+-
+- /* create service name */
+- inaddr = inet_addr(address);
+- if (!(ent = gethostbyaddr(&inaddr, sizeof(inaddr), AF_INET))) {
+- printerr(0, "ERROR: can't resolve server %s name\n", address);
+- goto fail;
+- }
+- if (!(*servername = calloc(strlen(ent->h_name) + 1, 1)))
+- goto fail;
+- memcpy(*servername, ent->h_name, strlen(ent->h_name));
+- snprintf(buf, INFOBUFLEN, "%s@%s", service, ent->h_name);
+- if (!(*servicename = calloc(strlen(buf) + 1, 1)))
+- goto fail;
+- memcpy(*servicename, buf, strlen(buf));
+-
+- if (!(*protocol = strdup(protoname)))
+- goto fail;
+- return 0;
+-fail:
+- printerr(0, "ERROR: failed to read service info\n");
+- if (fd != -1) close(fd);
+- if (*servername) free(*servername);
+- if (*servicename) free(*servicename);
+- if (*protocol) free(*protocol);
+- return -1;
+-}
+-
+ static void
+ destroy_client(struct clnt_info *clp)
+ {
++ printerr(3, "clp %p: dirname %s, krb5fd %d\n", clp, clp->dirname, clp->krb5_fd);
+ if (clp->krb5_poll_index != -1)
+ memset(&pollarray[clp->krb5_poll_index], 0,
+ sizeof(struct pollfd));
+@@ -190,8 +114,6 @@ destroy_client(struct clnt_info *clp)
+ if (clp->spkm3_fd != -1) close(clp->spkm3_fd);
+ if (clp->dirname) free(clp->dirname);
+ if (clp->servicename) free(clp->servicename);
+- if (clp->servername) free(clp->servername);
+- if (clp->protocol) free(clp->protocol);
+ free(clp);
+ }
+
+@@ -221,7 +143,6 @@ process_clnt_dir_files(struct clnt_info
+ {
+ char kname[32];
+ char sname[32];
+- char info_file_name[32];
+
+ if (clp->krb5_fd == -1) {
+ snprintf(kname, sizeof(kname), "%s/krb5", clp->dirname);
+@@ -233,13 +154,6 @@ process_clnt_dir_files(struct clnt_info
+ }
+ if((clp->krb5_fd == -1) && (clp->spkm3_fd == -1))
+ return -1;
+- snprintf(info_file_name, sizeof(info_file_name), "%s/info",
+- clp->dirname);
+- if ((clp->servicename == NULL) &&
+- read_service_info(info_file_name, &clp->servicename,
+- &clp->servername, &clp->prog, &clp->vers,
+- &clp->protocol))
+- return -1;
+ return 0;
+ }
+
+@@ -273,6 +187,8 @@ insert_clnt_poll(struct clnt_info *clp)
+ }
+ pollarray[clp->krb5_poll_index].fd = clp->krb5_fd;
+ pollarray[clp->krb5_poll_index].events |= POLLIN;
++ printerr(2, "monitoring krb5 channel under %s\n",
++ clp->dirname);
+ }
+
+ if ((clp->spkm3_fd != -1) && (clp->spkm3_poll_index == -1)) {
+@@ -386,67 +302,106 @@ find_client(char *dirname)
+ int
+ update_client_list(void)
+ {
+- struct dirent **namelist;
++ char lustre_dir[PATH_MAX];
++ struct dirent lustre_dirent = { .d_name = "lustre" };
++ struct dirent *namelist[1];
++ struct stat statbuf;
+ int i, j;
+
+- if (chdir(pipefs_nfsdir) < 0) {
++ if (chdir(pipefs_dir) < 0) {
+ printerr(0, "ERROR: can't chdir to %s: %s\n",
+- pipefs_nfsdir, strerror(errno));
++ pipefs_dir, strerror(errno));
+ return -1;
+ }
+
+- j = scandir(pipefs_nfsdir, &namelist, NULL, alphasort);
+- if (j < 0) {
+- printerr(0, "ERROR: can't scandir %s: %s\n",
+- pipefs_nfsdir, strerror(errno));
+- return -1;
++ snprintf(lustre_dir, sizeof(lustre_dir), "%s/%s", pipefs_dir, "lustre");
++ if (stat(lustre_dir, &statbuf) == 0) {
++ namelist[0] = &lustre_dirent;
++ j = 1;
++ printerr(2, "re-processing lustre directory\n");
++ } else {
++ namelist[0] = NULL;
++ j = 0;
++ printerr(2, "lustre directory not exist\n");
+ }
++
+ update_old_clients(namelist, j);
+ for (i=0; i < j; i++) {
+- if (i < FD_ALLOC_BLOCK
+- && !strncmp(namelist[i]->d_name, "clnt", 4)
+- && !find_client(namelist[i]->d_name))
++ if (i < FD_ALLOC_BLOCK && !find_client(namelist[i]->d_name))
+ process_clnt_dir(namelist[i]->d_name);
+- free(namelist[i]);
+ }
+
+- free(namelist);
++ chdir("/");
+ return 0;
+ }
+
++/* Context creation response. */
++struct lustre_gss_init_res {
++ gss_buffer_desc gr_ctx; /* context handle */
++ u_int gr_major; /* major status */
++ u_int gr_minor; /* minor status */
++ u_int gr_win; /* sequence window */
++ gss_buffer_desc gr_token; /* token */
++};
++
++struct lustre_gss_data {
++ int lgd_established;
++ int lgd_lustre_svc; /* mds/oss */
++ int lgd_uid; /* uid */
++ char *lgd_uuid; /* client device uuid */
++ gss_name_t lgd_name; /* service name */
++
++ gss_OID lgd_mech; /* mech OID */
++ u_int lgd_req_flags; /* request flags */
++ gss_cred_id_t lgd_cred; /* credential */
++ gss_ctx_id_t lgd_ctx; /* session context */
++ gss_buffer_desc lgd_rmt_ctx; /* remote handle of context */
++ uint32_t lgd_seq_win; /* sequence window */
++
++ int lgd_rpc_err;
++ int lgd_gss_err;
++};
++
+ static int
+-do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
+- gss_buffer_desc *context_token)
++do_downcall(int k5_fd, struct lgssd_upcall_data *updata,
++ struct lustre_gss_data *lgd, gss_buffer_desc *context_token)
+ {
+ char *buf = NULL, *p = NULL, *end = NULL;
+ unsigned int timeout = 0; /* XXX decide on a reasonable value */
+ unsigned int buf_size = 0;
+
+- printerr(1, "doing downcall\n");
+- buf_size = sizeof(uid) + sizeof(timeout) + sizeof(pd->pd_seq_win) +
+- sizeof(pd->pd_ctx_hndl.length) + pd->pd_ctx_hndl.length +
++ printerr(2, "doing downcall\n");
++ buf_size = sizeof(updata->seq) + sizeof(timeout) +
++ sizeof(lgd->lgd_seq_win) +
++ sizeof(lgd->lgd_rmt_ctx.length) + lgd->lgd_rmt_ctx.length +
+ sizeof(context_token->length) + context_token->length;
+ p = buf = malloc(buf_size);
+ end = buf + buf_size;
+
+- if (WRITE_BYTES(&p, end, uid)) goto out_err;
++ if (WRITE_BYTES(&p, end, updata->seq)) goto out_err;
+ /* Not setting any timeout for now: */
+ if (WRITE_BYTES(&p, end, timeout)) goto out_err;
+- if (WRITE_BYTES(&p, end, pd->pd_seq_win)) goto out_err;
+- if (write_buffer(&p, end, &pd->pd_ctx_hndl)) goto out_err;
++ if (WRITE_BYTES(&p, end, lgd->lgd_seq_win)) goto out_err;
++ if (write_buffer(&p, end, &lgd->lgd_rmt_ctx)) goto out_err;
+ if (write_buffer(&p, end, context_token)) goto out_err;
+
+- if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
++ lgssd_mutex_get(lgssd_mutex_downcall);
++ if (write(k5_fd, buf, p - buf) < p - buf) {
++ lgssd_mutex_put(lgssd_mutex_downcall);
++ goto out_err;
++ }
++ lgssd_mutex_put(lgssd_mutex_downcall);
++
+ if (buf) free(buf);
+ return 0;
+ out_err:
+ if (buf) free(buf);
+- printerr(0, "Failed to write downcall!\n");
++ printerr(0, "ERROR: Failed to write downcall!\n");
+ return -1;
+ }
+
+ static int
+-do_error_downcall(int k5_fd, uid_t uid, int err)
++do_error_downcall(int k5_fd, uint32_t seq, int rpc_err, int gss_err)
+ {
+ char buf[1024];
+ char *p = buf, *end = buf + 1024;
+@@ -455,19 +410,26 @@ do_error_downcall(int k5_fd, uid_t uid,
+
+ printerr(1, "doing error downcall\n");
+
+- if (WRITE_BYTES(&p, end, uid)) goto out_err;
++ if (WRITE_BYTES(&p, end, seq)) goto out_err;
+ if (WRITE_BYTES(&p, end, timeout)) goto out_err;
+ /* use seq_win = 0 to indicate an error: */
+ if (WRITE_BYTES(&p, end, zero)) goto out_err;
+- if (WRITE_BYTES(&p, end, err)) goto out_err;
++ if (WRITE_BYTES(&p, end, rpc_err)) goto out_err;
++ if (WRITE_BYTES(&p, end, gss_err)) goto out_err;
+
+- if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
++ lgssd_mutex_get(lgssd_mutex_downcall);
++ if (write(k5_fd, buf, p - buf) < p - buf) {
++ lgssd_mutex_put(lgssd_mutex_downcall);
++ goto out_err;
++ }
++ lgssd_mutex_put(lgssd_mutex_downcall);
+ return 0;
+ out_err:
+ printerr(0, "Failed to write error downcall!\n");
+ return -1;
+ }
+
++#if 0
+ /*
+ * Create an RPC connection and establish an authenticated
+ * gss context with a server.
+@@ -659,7 +621,287 @@ int create_auth_rpc_client(struct clnt_i
+
+ goto out;
+ }
++#endif
++
++static
++int do_negotiation(struct lustre_gss_data *lgd,
++ gss_buffer_desc *gss_token,
++ struct lustre_gss_init_res *gr,
++ int timeout)
++{
++ char *file = "/proc/fs/lustre/sptlrpc/gss/init_channel";
++ struct lgssd_ioctl_param param;
++ struct passwd *pw;
++ int fd, ret;
++ char outbuf[8192];
++ unsigned int *p;
++ int res;
++
++ pw = getpwuid(lgd->lgd_uid);
++ if (!pw) {
++ printerr(0, "no uid %u in local user database\n",
++ lgd->lgd_uid);
++ return -1;
++ }
++
++ param.version = GSSD_INTERFACE_VERSION;
++ param.uuid = lgd->lgd_uuid;
++ param.lustre_svc = lgd->lgd_lustre_svc;
++ param.uid = lgd->lgd_uid;
++ param.gid = pw->pw_gid;
++ param.send_token_size = gss_token->length;
++ param.send_token = (char *) gss_token->value;
++ param.reply_buf_size = sizeof(outbuf);
++ param.reply_buf = outbuf;
++
++ fd = open(file, O_RDWR);
++ if (fd < 0) {
++ printerr(0, "can't open file %s\n", file);
++ return -1;
++ }
++
++ ret = write(fd, ¶m, sizeof(param));
++
++ if (ret != sizeof(param)) {
++ printerr(0, "lustre ioctl err: %d\n", strerror(errno));
++ close(fd);
++ return -1;
++ }
++ if (param.status) {
++ close(fd);
++ printerr(0, "status: %d (%s)\n",
++ param.status, strerror((int)param.status));
++ if (param.status == -ETIMEDOUT) {
++ /* kernel return -ETIMEDOUT means the rpc timedout,
++ * we should notify the caller to reinitiate the
++ * gss negotiation, by return -ERESTART
++ */
++ lgd->lgd_rpc_err = -ERESTART;
++ lgd->lgd_gss_err = 0;
++ } else {
++ lgd->lgd_rpc_err = param.status;
++ lgd->lgd_gss_err = 0;
++ }
++ return -1;
++ }
++ p = (unsigned int *)outbuf;
++ res = *p++;
++ gr->gr_major = *p++;
++ gr->gr_minor = *p++;
++ gr->gr_win = *p++;
++
++ gr->gr_ctx.length = *p++;
++ gr->gr_ctx.value = malloc(gr->gr_ctx.length);
++ memcpy(gr->gr_ctx.value, p, gr->gr_ctx.length);
++ p += (((gr->gr_ctx.length + 3) & ~3) / 4);
++
++ gr->gr_token.length = *p++;
++ gr->gr_token.value = malloc(gr->gr_token.length);
++ memcpy(gr->gr_token.value, p, gr->gr_token.length);
++ p += (((gr->gr_token.length + 3) & ~3) / 4);
++
++ printerr(2, "do_negotiation: receive handle len %d, token len %d\n",
++ gr->gr_ctx.length, gr->gr_token.length);
++ close(fd);
++ return 0;
++}
++
++static
++int gssd_refresh_lgd(struct lustre_gss_data *lgd)
++{
++ struct lustre_gss_init_res gr;
++ gss_buffer_desc *recv_tokenp, send_token;
++ OM_uint32 maj_stat, min_stat, call_stat, ret_flags;
++
++ /* GSS context establishment loop. */
++ memset(&gr, 0, sizeof(gr));
++ recv_tokenp = GSS_C_NO_BUFFER;
++
++ for (;;) {
++ /* print the token we just received */
++ if (recv_tokenp != GSS_C_NO_BUFFER) {
++ printerr(3, "The received token length %d\n",
++ recv_tokenp->length);
++ print_hexl(3, recv_tokenp->value, recv_tokenp->length);
++ }
++
++ maj_stat = gss_init_sec_context(&min_stat,
++ lgd->lgd_cred,
++ &lgd->lgd_ctx,
++ lgd->lgd_name,
++ lgd->lgd_mech,
++ lgd->lgd_req_flags,
++ 0, /* time req */
++ NULL, /* channel */
++ recv_tokenp,
++ NULL, /* used mech */
++ &send_token,
++ &ret_flags,
++ NULL); /* time rec */
++
++ if (recv_tokenp != GSS_C_NO_BUFFER) {
++ gss_release_buffer(&min_stat, &gr.gr_token);
++ recv_tokenp = GSS_C_NO_BUFFER;
++ }
++ if (maj_stat != GSS_S_COMPLETE &&
++ maj_stat != GSS_S_CONTINUE_NEEDED) {
++ pgsserr("gss_init_sec_context", maj_stat, min_stat,
++ lgd->lgd_mech);
++ break;
++ }
++ if (send_token.length != 0) {
++ memset(&gr, 0, sizeof(gr));
++
++ /* print the token we are about to send */
++ printerr(3, "token being sent length %d\n",
++ send_token.length);
++ print_hexl(3, send_token.value, send_token.length);
++
++ call_stat = do_negotiation(lgd, &send_token, &gr, 0);
++ gss_release_buffer(&min_stat, &send_token);
++
++ if (call_stat != 0 ||
++ (gr.gr_major != GSS_S_COMPLETE &&
++ gr.gr_major != GSS_S_CONTINUE_NEEDED)) {
++ printerr(0, "call stat %d, major stat 0x%x\n",
++ (int)call_stat, gr.gr_major);
++ return -1;
++ }
++
++ if (gr.gr_ctx.length != 0) {
++ if (lgd->lgd_rmt_ctx.value)
++ gss_release_buffer(&min_stat,
++ &lgd->lgd_rmt_ctx);
++ lgd->lgd_rmt_ctx = gr.gr_ctx;
++ }
++ if (gr.gr_token.length != 0) {
++ if (maj_stat != GSS_S_CONTINUE_NEEDED)
++ break;
++ recv_tokenp = &gr.gr_token;
++ }
++ }
++
++ /* GSS_S_COMPLETE => check gss header verifier,
++ * usually checked in gss_validate
++ */
++ if (maj_stat == GSS_S_COMPLETE) {
++ lgd->lgd_established = 1;
++ lgd->lgd_seq_win = gr.gr_win;
++ break;
++ }
++ }
++ /* End context negotiation loop. */
++ if (!lgd->lgd_established) {
++ if (gr.gr_token.length != 0)
++ gss_release_buffer(&min_stat, &gr.gr_token);
++
++ printerr(0, "context negotiation failed\n");
++ return -1;
++ }
++
++ printerr(2, "successfully refreshed lgd\n");
++ return 0;
++}
+
++static
++int gssd_create_lgd(struct clnt_info *clp,
++ struct lustre_gss_data *lgd,
++ struct lgssd_upcall_data *updata,
++ int authtype)
++{
++ gss_buffer_desc sname;
++ OM_uint32 maj_stat, min_stat;
++ int retval = -1;
++
++ lgd->lgd_established = 0;
++ lgd->lgd_lustre_svc = updata->svc;
++ lgd->lgd_uid = updata->uid;
++ lgd->lgd_uuid = updata->obd;
++
++ switch (authtype) {
++ case AUTHTYPE_KRB5:
++ lgd->lgd_mech = (gss_OID) &krb5oid;
++ lgd->lgd_req_flags = GSS_C_MUTUAL_FLAG;
++ break;
++ case AUTHTYPE_SPKM3:
++ lgd->lgd_mech = (gss_OID) &spkm3oid;
++ /* XXX sec.req_flags = GSS_C_ANON_FLAG;
++ * Need a way to switch....
++ */
++ lgd->lgd_req_flags = GSS_C_MUTUAL_FLAG;
++ break;
++ default:
++ printerr(0, "Invalid authentication type (%d)\n", authtype);
++ return -1;
++ }
++
++ lgd->lgd_cred = GSS_C_NO_CREDENTIAL;
++ lgd->lgd_ctx = GSS_C_NO_CONTEXT;
++ lgd->lgd_rmt_ctx = (gss_buffer_desc) GSS_C_EMPTY_BUFFER;
++ lgd->lgd_seq_win = 0;
++
++ sname.value = clp->servicename;
++ sname.length = strlen(clp->servicename);
++
++ maj_stat = gss_import_name(&min_stat, &sname,
++ (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
++ &lgd->lgd_name);
++ if (maj_stat != GSS_S_COMPLETE) {
++ pgsserr(0, maj_stat, min_stat, lgd->lgd_mech);
++ goto out_fail;
++ }
++
++ retval = gssd_refresh_lgd(lgd);
++
++ if (lgd->lgd_name != GSS_C_NO_NAME)
++ gss_release_name(&min_stat, &lgd->lgd_name);
++
++ if (lgd->lgd_cred != GSS_C_NO_CREDENTIAL)
++ gss_release_cred(&min_stat, &lgd->lgd_cred);
++
++ out_fail:
++ return retval;
++}
++
++static
++void gssd_free_lgd(struct lustre_gss_data *lgd)
++{
++ gss_buffer_t token = GSS_C_NO_BUFFER;
++ OM_uint32 maj_stat, min_stat;
++
++ if (lgd->lgd_ctx == GSS_C_NO_CONTEXT)
++ return;
++
++ maj_stat = gss_delete_sec_context(&min_stat, &lgd->lgd_ctx, token);
++}
++
++static
++int construct_service_name(struct clnt_info *clp,
++ struct lgssd_upcall_data *ud)
++{
++ const int buflen = 256;
++ char name[buflen];
++
++ if (clp->servicename) {
++ free(clp->servicename);
++ clp->servicename = NULL;
++ }
++
++ if (lnet_nid2hostname(ud->nid, name, buflen))
++ return -1;
++
++ clp->servicename = malloc(32 + strlen(name));
++ if (!clp->servicename) {
++ printerr(0, "can't alloc memory\n");
++ return -1;
++ }
++ sprintf(clp->servicename, "%s@%s",
++ ud->svc == LUSTRE_GSS_SVC_MDS ?
++ GSSD_SERVICE_MDS : GSSD_SERVICE_OSS,
++ name);
++ printerr(2, "constructed servicename: %s\n", clp->servicename);
++ return 0;
++}
+
+ /*
+ * this code uses the userland rpcsec gss library to create a krb5
+@@ -668,103 +910,145 @@ int create_auth_rpc_client(struct clnt_i
+ void
+ handle_krb5_upcall(struct clnt_info *clp)
+ {
+- uid_t uid;
+- CLIENT *rpc_clnt = NULL;
+- AUTH *auth = NULL;
+- struct authgss_private_data pd;
+- gss_buffer_desc token;
++ pid_t pid;
++ gss_buffer_desc token = { 0, NULL };
++ struct lgssd_upcall_data updata;
++ struct lustre_gss_data lgd;
+ char **credlist = NULL;
+ char **ccname;
++ int read_rc;
+
+- printerr(1, "handling krb5 upcall\n");
++ printerr(2, "handling krb5 upcall\n");
+
+- token.length = 0;
+- token.value = NULL;
+- memset(&pd, 0, sizeof(struct authgss_private_data));
++ memset(&lgd, 0, sizeof(lgd));
++ lgd.lgd_rpc_err = -EPERM; /* default error code */
+
+- if (read(clp->krb5_fd, &uid, sizeof(uid)) < sizeof(uid)) {
+- printerr(0, "WARNING: failed reading uid from krb5 "
++ read_rc = read(clp->krb5_fd, &updata, sizeof(updata));
++ if (read_rc < 0) {
++ printerr(0, "WARNING: failed reading from krb5 "
+ "upcall pipe: %s\n", strerror(errno));
+- goto out;
++ return;
++ } else if (read_rc != sizeof(updata)) {
++ printerr(0, "upcall data mismatch: length %d, expect %d\n",
++ read_rc, sizeof(updata));
++
++ /* the sequence number must be the first field. if read >= 4
++ * bytes then we know at least sequence is fine, try to send
++ * error notification nicely.
++ */
++ if (read_rc >= 4)
++ do_error_downcall(clp->krb5_fd, updata.seq, -EPERM, 0);
++ return;
++ }
++
++ /* FIXME temporary fix, do this before fork.
++ * in case of errors could have memory leak!!!
++ */
++ if (updata.uid == 0) {
++ if (gssd_get_krb5_machine_cred_list(&credlist)) {
++ printerr(0, "ERROR: Failed to obtain machine "
++ "credentials\n");
++ do_error_downcall(clp->krb5_fd, updata.seq, -EPERM, 0);
++ return;
++ }
++ }
++
++ /* fork child process */
++ pid = fork();
++ if (pid < 0) {
++ printerr(0, "can't fork: %s\n", strerror(errno));
++ do_error_downcall(clp->krb5_fd, updata.seq, -EPERM, 0);
++ return;
++ } else if (pid > 0) {
++ printerr(2, "forked child process: %d\n", pid);
++ return;
++ }
++
++ printerr(1, "krb5 upcall: seq %u, uid %u, svc %u, nid 0x%llx, obd %s\n",
++ updata.seq, updata.uid, updata.svc, updata.nid, updata.obd);
++
++ if (updata.svc != LUSTRE_GSS_SVC_MDS &&
++ updata.svc != LUSTRE_GSS_SVC_OSS) {
++ printerr(0, "invalid svc %d\n", updata.svc);
++ lgd.lgd_rpc_err = -EPROTO;
++ goto out_return_error;
++ }
++ updata.obd[sizeof(updata.obd)-1] = '\0';
++
++ if (construct_service_name(clp, &updata)) {
++ printerr(0, "failed to construct service name\n");
++ goto out_return_error;
+ }
+
+- if (uid == 0) {
++ if (updata.uid == 0) {
+ int success = 0;
+
+ /*
+ * Get a list of credential cache names and try each
+ * of them until one works or we've tried them all
+ */
++/*
+ if (gssd_get_krb5_machine_cred_list(&credlist)) {
+- printerr(0, "WARNING: Failed to obtain machine "
+- "credentials for connection to "
+- "server %s\n", clp->servername);
+- goto out_return_error;
++ printerr(0, "ERROR: Failed to obtain machine "
++ "credentials for %s\n", clp->servicename);
++ goto out_return_error;
+ }
++*/
+ for (ccname = credlist; ccname && *ccname; ccname++) {
+ gssd_setup_krb5_machine_gss_ccache(*ccname);
+- if ((create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
+- AUTHTYPE_KRB5)) == 0) {
++ if ((gssd_create_lgd(clp, &lgd, &updata,
++ AUTHTYPE_KRB5)) == 0) {
+ /* Success! */
+ success++;
+ break;
+ }
+ printerr(2, "WARNING: Failed to create krb5 context "
+ "for user with uid %d with credentials "
+- "cache %s for server %s\n",
+- uid, *ccname, clp->servername);
++ "cache %s for service %s\n",
++ updata.uid, *ccname, clp->servicename);
+ }
+ gssd_free_krb5_machine_cred_list(credlist);
+ if (!success) {
+- printerr(0, "WARNING: Failed to create krb5 context "
++ printerr(0, "ERROR: Failed to create krb5 context "
+ "for user with uid %d with any "
+- "credentials cache for server %s\n",
+- uid, clp->servername);
++ "credentials cache for service %s\n",
++ updata.uid, clp->servicename);
+ goto out_return_error;
+ }
+ }
+ else {
+ /* Tell krb5 gss which credentials cache to use */
+- gssd_setup_krb5_user_gss_ccache(uid, clp->servername);
++ gssd_setup_krb5_user_gss_ccache(updata.uid, clp->servicename);
+
+- if ((create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
+- AUTHTYPE_KRB5)) != 0) {
++ if ((gssd_create_lgd(clp, &lgd, &updata, AUTHTYPE_KRB5)) != 0) {
+ printerr(0, "WARNING: Failed to create krb5 context "
+- "for user with uid %d for server %s\n",
+- uid, clp->servername);
++ "for user with uid %d for service %s\n",
++ updata.uid, clp->servicename);
+ goto out_return_error;
+ }
+ }
+
+- if (!authgss_get_private_data(auth, &pd)) {
+- printerr(0, "WARNING: Failed to obtain authentication "
+- "data for user with uid %d for server %s\n",
+- uid, clp->servername);
+- goto out_return_error;
+- }
+-
+- if (serialize_context_for_kernel(pd.pd_ctx, &token, &krb5oid)) {
++ if (serialize_context_for_kernel(lgd.lgd_ctx, &token, &krb5oid)) {
+ printerr(0, "WARNING: Failed to serialize krb5 context for "
+- "user with uid %d for server %s\n",
+- uid, clp->servername);
++ "user with uid %d for service %s\n",
++ updata.uid, clp->servicename);
+ goto out_return_error;
+ }
+
+- do_downcall(clp->krb5_fd, uid, &pd, &token);
++ printerr(1, "refreshed: %u@%s for %s\n",
++ updata.uid, updata.obd, clp->servicename);
++ do_downcall(clp->krb5_fd, &updata, &lgd, &token);
+
+ out:
+ if (token.value)
+ free(token.value);
+- if (pd.pd_ctx_hndl.length != 0)
+- authgss_free_private_data(&pd);
+- if (auth)
+- AUTH_DESTROY(auth);
+- if (rpc_clnt)
+- clnt_destroy(rpc_clnt);
+- return;
++
++ gssd_free_lgd(&lgd);
++ exit(0); /* i'm child process */
+
+ out_return_error:
+- do_error_downcall(clp->krb5_fd, uid, -1);
++ do_error_downcall(clp->krb5_fd, updata.seq,
++ lgd.lgd_rpc_err, lgd.lgd_gss_err);
+ goto out;
+ }
+
+@@ -775,6 +1059,7 @@ out_return_error:
+ void
+ handle_spkm3_upcall(struct clnt_info *clp)
+ {
++#if 0
+ uid_t uid;
+ CLIENT *rpc_clnt = NULL;
+ AUTH *auth = NULL;
+@@ -826,4 +1111,5 @@ out:
+ out_return_error:
+ do_error_downcall(clp->spkm3_fd, uid, -1);
+ goto out;
++#endif
+ }
+diff -Nrup nfs-utils-1.0.11/utils/gssd/gss_util.c nfs-utils-1.0.11.lustre/utils/gssd/gss_util.c
+--- nfs-utils-1.0.11/utils/gssd/gss_util.c 2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/gss_util.c 2007-05-23 14:36:37.000000000 -0600
+@@ -87,9 +87,16 @@
+ #ifdef HAVE_COM_ERR_H
+ #include <com_err.h>
+ #endif
++#include "lsupport.h"
+
+ /* Global gssd_credentials handle */
+-gss_cred_id_t gssd_creds;
++gss_cred_id_t gssd_cred_mds;
++gss_cred_id_t gssd_cred_oss;
++int gssd_cred_mds_valid = 0;
++int gssd_cred_oss_valid = 0;
++
++char *mds_local_realm = NULL;
++char *oss_local_realm = NULL;
+
+ gss_OID g_mechOid = GSS_C_NULL_OID;;
+
+@@ -183,15 +190,56 @@ pgsserr(char *msg, u_int32_t maj_stat, u
+ display_status_2(msg, maj_stat, min_stat, mech);
+ }
+
+-int
+-gssd_acquire_cred(char *server_name)
++static
++int extract_realm_name(gss_buffer_desc *name, char **realm)
++{
++ char *sname, *c;
++ int rc = 0;
++
++ sname = malloc(name->length + 1);
++ if (!sname) {
++ printerr(0, "out of memory\n");
++ return -ENOMEM;
++ }
++
++ memcpy(sname, name->value, name->length);
++ sname[name->length] = '\0';
++ printerr(1, "service principal: %s\n", sname);
++
++ c = strchr(sname, '@');
++ if (!c) {
++ printerr(2, "no realm found in principal, use default\n");
++ *realm = strdup(this_realm);
++ if (!*realm) {
++ printerr(0, "failed to duplicate default realm\n");
++ rc = -ENOMEM;
++ }
++ } else {
++ c++;
++ *realm = strdup(c);
++ if (!*realm) {
++ printerr(0, "failed to duplicated realm\n");
++ rc = -ENOMEM;
++ }
++ }
++ free(sname);
++
++ return rc;
++}
++
++static
++int gssd_acquire_cred(char *server_name, gss_cred_id_t *cred,
++ char **local_realm, int *valid)
+ {
+ gss_buffer_desc name;
+ gss_name_t target_name;
+ u_int32_t maj_stat, min_stat;
+ u_int32_t ignore_maj_stat, ignore_min_stat;
++ gss_OID name_type;
+ gss_buffer_desc pbuf;
+
++ *valid = 0;
++
+ name.value = (void *)server_name;
+ name.length = strlen(server_name);
+
+@@ -201,12 +249,20 @@ gssd_acquire_cred(char *server_name)
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("gss_import_name", maj_stat, min_stat, g_mechOid);
+- return (FALSE);
++ return -1;
++ }
++
++ maj_stat = gss_display_name(&min_stat, target_name, &name, &name_type);
++ if (maj_stat != GSS_S_COMPLETE) {
++ pgsserr(0, maj_stat, min_stat, g_mechOid);
++ return -1;
+ }
++ if (extract_realm_name(&name, local_realm))
++ return -1;
+
+ maj_stat = gss_acquire_cred(&min_stat, target_name, 0,
+ GSS_C_NULL_OID_SET, GSS_C_ACCEPT,
+- &gssd_creds, NULL, NULL);
++ cred, NULL, NULL);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("gss_acquire_cred", maj_stat, min_stat, g_mechOid);
+@@ -218,11 +274,67 @@ gssd_acquire_cred(char *server_name)
+ ignore_maj_stat = gss_release_buffer(&ignore_min_stat,
+ &pbuf);
+ }
+- }
++ } else
++ *valid = 1;
+
+ ignore_maj_stat = gss_release_name(&ignore_min_stat, &target_name);
+
+- return (maj_stat == GSS_S_COMPLETE);
++ if (maj_stat != GSS_S_COMPLETE)
++ return -1;
++ return 0;
++}
++
++int gssd_prepare_creds(int must_srv_mds, int must_srv_oss)
++{
++ if (gssd_acquire_cred(GSSD_SERVICE_MDS, &gssd_cred_mds,
++ &mds_local_realm, &gssd_cred_mds_valid)) {
++ if (must_srv_mds)
++ return -1;
++ }
++
++ if (gssd_acquire_cred(GSSD_SERVICE_OSS, &gssd_cred_oss,
++ &oss_local_realm, &gssd_cred_oss_valid)) {
++ if (must_srv_oss)
++ return -1;
++ }
++
++ if (!gssd_cred_mds_valid && !gssd_cred_oss_valid) {
++ printerr(0, "can't obtain both mds & oss creds, exit\n");
++ return -1;
++ }
++
++ if (gssd_cred_mds_valid)
++ printerr(0, "Ready to serve Lustre MDS in realm %s\n",
++ mds_local_realm ? mds_local_realm : "N/A");
++ if (gssd_cred_oss_valid)
++ printerr(0, "Ready to serve Lustre OSS in realm %s\n",
++ oss_local_realm ? oss_local_realm : "N/A");
++
++ return 0;
++}
++
++gss_cred_id_t gssd_select_svc_cred(int lustre_svc)
++{
++ switch (lustre_svc) {
++ case LUSTRE_GSS_SVC_MDS:
++ if (!gssd_cred_mds_valid) {
++ printerr(0, "ERROR: service cred for mds not ready\n");
++ return NULL;
++ }
++ printerr(2, "select mds service cred\n");
++ return gssd_cred_mds;
++ case LUSTRE_GSS_SVC_OSS:
++ if (!gssd_cred_oss_valid) {
++ printerr(0, "ERROR: service cred for oss not ready\n");
++ return NULL;
++ }
++ printerr(2, "select oss service cred\n");
++ return gssd_cred_oss;
++ default:
++ printerr(0, "ERROR: invalid lustre svc id %d\n", lustre_svc);
++ }
++
++ return NULL;
+ }
+
+ int gssd_check_mechs(void)
+@@ -249,3 +361,42 @@ out:
+ return retval;
+ }
+
++/*********************************
++ * FIXME should be in krb5_util.c
++ *********************************/
++
++#include "krb5_util.h"
++
++/* realm of this node */
++char *this_realm = NULL;
++
++int gssd_get_local_realm(void)
++{
++ krb5_context context = NULL;
++ krb5_error_code code;
++ int retval = -1;
++
++ if (this_realm != NULL)
++ return 0;
++
++ code = krb5_init_context(&context);
++ if (code) {
++ printerr(0, "ERROR: get default realm: init ctx: %s\n",
++ error_message(code));
++ goto out;
++ }
++
++ code = krb5_get_default_realm(context, &this_realm);
++ if (code) {
++ printerr(0, "ERROR: get default realm: %s\n",
++ error_message(code));
++ goto out;
++ }
++ retval = 0;
++
++ printerr(1, "Local realm: %s\n", this_realm);
++out:
++ krb5_free_context(context);
++ return retval;
++}
++
+diff -Nrup nfs-utils-1.0.11/utils/gssd/gss_util.h nfs-utils-1.0.11.lustre/utils/gssd/gss_util.h
+--- nfs-utils-1.0.11/utils/gssd/gss_util.h 2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/gss_util.h 2007-05-23 14:36:37.000000000 -0600
+@@ -32,14 +32,14 @@
+ #define _GSS_UTIL_H_
+
+ #include <stdlib.h>
+-#include <rpc/rpc.h>
+ #include "write_bytes.h"
+
++char *this_realm;
+ extern gss_cred_id_t gssd_creds;
+
+-int gssd_acquire_cred(char *server_name);
+ void pgsserr(char *msg, u_int32_t maj_stat, u_int32_t min_stat,
+ const gss_OID mech);
+ int gssd_check_mechs(void);
++int gssd_get_local_realm(void);
+
+ #endif /* _GSS_UTIL_H_ */
+diff -Nrup nfs-utils-1.0.11/utils/gssd/krb5_util.c nfs-utils-1.0.11.lustre/utils/gssd/krb5_util.c
+--- nfs-utils-1.0.11/utils/gssd/krb5_util.c 2007-05-23 14:35:21.000000000 -0600
++++ nfs-utils-1.0.11.lustre/utils/gssd/krb5_util.c 2007-05-23 14:36:38.000000000 -0600
+@@ -99,12 +99,15 @@
+ #include <rpc/rpc.h>
+ #include <sys/types.h>
+ #include <sys/stat.h>
++#include <sys/utsname.h>
+ #include <sys/socket.h>
+ #include <arpa/inet.h>
+
++#include <unistd.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
++#include <netdb.h>
+ #include <dirent.h>
+ #include <fcntl.h>
+ #include <errno.h>
+@@ -114,7 +117,6 @@
+ #include <gssapi/gssapi_krb5.h>
+ #endif
+ #include <krb5.h>
+-#include <rpc/auth_gss.h>
+
+ #include "gssd.h"
+ #include "err_util.h"
+@@ -129,6 +131,9 @@ struct gssd_k5_kt_princ *gssd_k5_kt_prin
+ int num_krb5_enctypes = 0;
+ krb5_enctype *krb5_enctypes = NULL;
+
++/* credential expire time in advance */
++unsigned long machine_cred_expire_advance = 300; /* 5 mins */
++
+ /*==========================*/
+ /*=== Internal routines ===*/
+ /*==========================*/
+@@ -137,11 +142,55 @@ static int select_krb5_ccache(const stru
+ static int gssd_find_existing_krb5_ccache(uid_t uid, struct dirent **d);
+ static int gssd_get_single_krb5_cred(krb5_context context,
+ krb5_keytab kt, struct gssd_k5_kt_princ *ple);
+-static int gssd_have_realm_ple(void *realm);
+ static int gssd_process_krb5_keytab(krb5_context context, krb5_keytab kt,
+ char *kt_name);
+
+ /*
++ * convenient macros, these perhaps need further cleanup
++ */
++#ifdef HAVE_KRB5
++
++#define KEYTAB_ENTRY_MATCH(kte, name) \
++ ( \
++ (kte).principal->data[0].length == (sizeof(name)-1) && \
++ strncmp((kte).principal->data[0].data, (name), sizeof(name)-1) == 0 \
++ )
++#define KRB5_FREE_UNPARSED_NAME(ctx, name) \
++ krb5_free_unparsed_name((ctx), (name));
++#define KRB5_STRDUP(str) \
++ strndup((str).data, (str).length)
++#define KRB5_STRCMP(str, name) \
++ ( \
++ (str)->length != strlen(name) || \
++ strncmp((str)->data, (name), (str)->length) != 0 \
++ )
++#define KRB5_STRCASECMP(str, name) \
++ ( \
++ (str)->length != strlen(name) || \
++ strncasecmp((str)->data, (name), (str)->length) != 0 \
++ )
++
++#else /* !HAVE_KRB5 */
++
++#define KEYTAB_ENTRY_MATCH(kte, name) \
++ ( \
++ strlen((kte).principal->name.name_string.val[0]) == \
++ (sizeof(name)-1) && \
++ strncmp(kte.principal->name.name_string.val[0], (name), \
++ sizeof(name)-1) == 0 \
++ )
++#define KRB5_FREE_UNPARSED_NAME(ctx, name) \
++ free(pname);
++#define KRB5_STRDUP(str) \
++ strdup(str)
++#define KRB5_STRCMP(str, name) \
++ strcmp((str), (name))
++#define KRB5_STRCASECMP(str, name) \
++ strcmp((str), (name))
++
++#endif /* HAVE_KRB5 */
++
++/*
+ * Called from the scandir function to weed out potential krb5
+ * credentials cache files
+ *
+@@ -288,7 +337,7 @@ gssd_get_single_krb5_cred(krb5_context c
+
+ memset(&my_creds, 0, sizeof(my_creds));
+
+- if (ple->ccname && ple->endtime > now) {
++ if (ple->ccname && ple->endtime > now + machine_cred_expire_advance) {
+ printerr(2, "INFO: Credentials in CC '%s' are good until %d\n",
+ ple->ccname, ple->endtime);
+ code = 0;
+@@ -308,6 +357,12 @@ gssd_get_single_krb5_cred(krb5_context c
+ /* set a short lifetime (for debugging only!) */
+ printerr(0, "WARNING: Using (debug) short machine cred lifetime!\n");
+ krb5_get_init_creds_opt_set_tkt_life(&options, 5*60);
++#else
++ /* FIXME try to get the ticket with lifetime as long as possible,
++ * to work around ticket-expiry + recovery problem in cmd3-11
++ * remove this!!!
++ */
++ krb5_get_init_creds_opt_set_tkt_life(&options, 30*24*60*60);
+ #endif
+ if ((code = krb5_get_init_creds_keytab(context, &my_creds, ple->princ,
+ kt, 0, NULL, &options))) {
+@@ -319,11 +374,7 @@ gssd_get_single_krb5_cred(krb5_context c
+ "principal '%s' from keytab '%s'\n",
+ error_message(code),
+ pname ? pname : "<unparsable>", kt_name);
+-#ifdef HAVE_KRB5
+- if (pname) krb5_free_unparsed_name(context, pname);
+-#else
+- if (pname) free(pname);
+-#endif
++ if (pname) KRB5_FREE_UNPARSED_NAME(context, pname);
+ goto out;
+ }
+
+@@ -372,15 +423,7 @@ gssd_get_single_krb5_cred(krb5_context c
+ return (code);
+ }
+
+-/*
+- * Determine if we already have a ple for the given realm
+- *
+- * Returns:
+- * 0 => no ple found for given realm
+- * 1 => found ple for given realm
+- */
+-static int
+-gssd_have_realm_ple(void *r)
++static struct gssd_k5_kt_princ * gssd_get_realm_ple(void *r)
+ {
+ struct gssd_k5_kt_princ *ple;
+ #ifdef HAVE_KRB5
+@@ -390,18 +433,76 @@ gssd_have_realm_ple(void *r)
+ #endif
+
+ for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
+-#ifdef HAVE_KRB5
+- if ((realm->length == strlen(ple->realm)) &&
+- (strncmp(realm->data, ple->realm, realm->length) == 0)) {
+-#else
+- if (strcmp(realm, ple->realm) == 0) {
+-#endif
+- return 1;
+- }
++ if (KRB5_STRCMP(realm, ple->realm) == 0)
++ return ple;
++ }
++ return NULL;
++}
++
++static void gssd_free_ple(krb5_context kctx, struct gssd_k5_kt_princ *ple)
++{
++ if (ple->princ)
++ krb5_free_principal(kctx, ple->princ);
++ if (ple->realm)
++ free(ple->realm);
++ if (ple->ccname)
++ free(ple->ccname);
++ free(ple);
++}
++
++static int gssd_remove_ple(krb5_context kctx, struct gssd_k5_kt_princ *ple)
++{
++ struct gssd_k5_kt_princ **prev = &gssd_k5_kt_princ_list;
++ struct gssd_k5_kt_princ *ent = gssd_k5_kt_princ_list;
++
++ for (; ent; prev = &ent->next, ent = ent->next) {
++ if (ent != ple)
++ continue;
++
++ *prev = ent->next;
++ gssd_free_ple(kctx, ent);
++ return 1;
+ }
+ return 0;
+ }
+
++static
++struct gssd_k5_kt_princ *gssd_create_ple(krb5_context kctx,
++ krb5_principal principal)
++{
++ struct gssd_k5_kt_princ *ple;
++ krb5_error_code code;
++
++ ple = malloc(sizeof(*ple));
++ if (ple == NULL) {
++ printerr(0, "ERROR: could not allocate storage "
++ "for principal list entry\n");
++ return NULL;
++ }
++
++ memset(ple, 0, sizeof(*ple));
++
++ ple->realm = KRB5_STRDUP(principal->realm);
++ if (ple->realm == NULL) {
++ printerr(0, "ERROR: not enough memory while copying realm to "
++ "principal list entry\n");
++ goto err_free;
++ }
++
++ code = krb5_copy_principal(kctx, principal, &ple->princ);
++ if (code) {
++ printerr(0, "ERROR: %s while copying principal "
++ "to principal list entry\n",
++ error_message(code));
++ goto err_free;
++ }
++
++ return ple;
++err_free:
++ gssd_free_ple(kctx, ple);
++ return NULL;
++}
++
+ /*
+ * Process the given keytab file and create a list of principals we
+ * might use to perform mount operations.
+@@ -445,82 +546,106 @@ gssd_process_krb5_keytab(krb5_context co
+ }
+ printerr(2, "Processing keytab entry for principal '%s'\n",
+ pname);
+-#ifdef HAVE_KRB5
+- if ( (kte.principal->data[0].length == GSSD_SERVICE_NAME_LEN) &&
+- (strncmp(kte.principal->data[0].data, GSSD_SERVICE_NAME,
+- GSSD_SERVICE_NAME_LEN) == 0) &&
+-#else
+- if ( (strlen(kte.principal->name.name_string.val[0]) == GSSD_SERVICE_NAME_LEN) &&
+- (strncmp(kte.principal->name.name_string.val[0], GSSD_SERVICE_NAME,
+- GSSD_SERVICE_NAME_LEN) == 0) &&
+-
+-#endif
+- (!gssd_have_realm_ple((void *)&kte.principal->realm)) ) {
+- printerr(2, "We will use this entry (%s)\n", pname);
+- ple = malloc(sizeof(struct gssd_k5_kt_princ));
+- if (ple == NULL) {
+- printerr(0, "ERROR: could not allocate storage "
+- "for principal list entry\n");
+-#ifdef HAVE_KRB5
+- krb5_free_unparsed_name(context, pname);
+-#else
+- free(pname);
+-#endif
+- retval = ENOMEM;
+- goto out;
+- }
+- /* These will be filled in later */
+- ple->next = NULL;
+- ple->ccname = NULL;
+- ple->endtime = 0;
+- if ((ple->realm =
+-#ifdef HAVE_KRB5
+- strndup(kte.principal->realm.data,
+- kte.principal->realm.length))
+-#else
+- strdup(kte.principal->realm))
+-#endif
+- == NULL) {
+- printerr(0, "ERROR: %s while copying realm to "
+- "principal list entry\n",
+- "not enough memory");
+-#ifdef HAVE_KRB5
+- krb5_free_unparsed_name(context, pname);
+-#else
+- free(pname);
+-#endif
+- retval = ENOMEM;
+- goto out;
+- }
+- if ((code = krb5_copy_principal(context,
+- kte.principal, &ple->princ))) {
+- printerr(0, "ERROR: %s while copying principal "
+- "to principal list entry\n",
+- error_message(code));
+-#ifdef HAVE_KRB5
+- krb5_free_unparsed_name(context, pname);
+-#else
+- free(pname);
+-#endif
+- retval = code;
+- goto out;
+- }
+- if (gssd_k5_kt_princ_list == NULL)
+- gssd_k5_kt_princ_list = ple;
+- else {
+- ple->next = gssd_k5_kt_princ_list;
+- gssd_k5_kt_princ_list = ple;
++
++ /* mds service entry:
++ * - hostname and realm should match this node
++ * - replace existing non-mds entry of this realm
++ */
++ if (KEYTAB_ENTRY_MATCH(kte, GSSD_SERVICE_MDS)) {
++ krb5_principal princ = kte.principal;
++ krb5_data *princ_host;
++ struct utsname utsbuf;
++ struct hostent *host;
++
++ if (KRB5_STRCASECMP(krb5_princ_realm(context, princ),
++ this_realm) != 0) {
++ printerr(2, "alien mds service entry, skip\n");
++ goto next;
++ }
++
++ princ_host = krb5_princ_component(context, princ, 1);
++ if (princ_host == NULL) {
++ printerr(2, "mds service entry: no hostname in "
++ "principal, skip\n");
++ goto next;
++ }
++
++ if (uname(&utsbuf)) {
++ printerr(2, "mds service entry: unable to get "
++ "UTS name, skip\n");
++ goto next;
++ }
++ host = gethostbyname(utsbuf.nodename);
++ if (host == NULL) {
++ printerr(2, "mds service entry: unable to get "
++ "local hostname, skip\n");
++ goto next;
++ }
++
++ if (KRB5_STRCASECMP(princ_host, host->h_name) != 0) {
++ printerr(2, "mds service entry: hostname "
++ "doesn't match: %s - %.*s, skip\n",
++ host->h_name,
++ princ_host->length, princ_host->data);
++ goto next;
++ }
++
++ ple = gssd_get_realm_ple((void *)&kte.principal->realm);
++ if (ple) {
++ if (ple->fl_mds) {
++ printerr(2,"mds service entry: found a"
++ "duplicated one, it's like a "
++ "mis-configuration, skip\n");
++ goto next;
++ }
++
++ gssd_remove_ple(context, ple);
++ printerr(2, "mds service entry: replace an "
++ "existed non-mds one\n");
++ }
++ } else if (KEYTAB_ENTRY_MATCH(kte, LUSTRE_ROOT_NAME)) {
++ ple = gssd_get_realm_ple((void *)&kte.principal->realm);
++ if (ple) {
++ if (ple->fl_mds || ple->fl_root) {
++ printerr(2, "root entry: found a "
++ "existed %s entry, skip\n",
++ ple->fl_mds ? "mds" : "root");
++ goto next;
++ }
++
++ gssd_remove_ple(context, ple);
++ printerr(2, "root entry: replace an existed "
++ "non-mds non-root one\n");
+ }
+- }
+- else {
++ } else {
+ printerr(2, "We will NOT use this entry (%s)\n",
+ pname);
++ goto next;
+ }
+-#ifdef HAVE_KRB5
+- krb5_free_unparsed_name(context, pname);
+-#else
+- free(pname);
+-#endif
++
++ /* construct ple */
++ printerr(2, "We will use this entry (%s)\n", pname);
++ ple = gssd_create_ple(context, kte.principal);
++ if (ple == NULL) {
++ KRB5_FREE_UNPARSED_NAME(context, pname);
++ goto out;
++ }
++
++ /* add proper flags */
++ if (KEYTAB_ENTRY_MATCH(kte, GSSD_SERVICE_MDS))
++ ple->fl_mds = 1;
++ else if (KEYTAB_ENTRY_MATCH(kte, LUSTRE_ROOT_NAME))
++ ple->fl_root = 1;
++
++ /* enqueue */
++ if (gssd_k5_kt_princ_list == NULL)
++ gssd_k5_kt_princ_list = ple;
++ else {
++ ple->next = gssd_k5_kt_princ_list;
++ gssd_k5_kt_princ_list = ple;
++ }
++ next:
++ KRB5_FREE_UNPARSED_NAME(context, pname);
+ }
+
+ if ((code = krb5_kt_end_seq_get(context, kt, &cursor))) {
+@@ -636,6 +761,7 @@ gssd_setup_krb5_user_gss_ccache(uid_t ui
+ printerr(2, "getting credentials for client with uid %u for "
+ "server %s\n", uid, servername);
+ memset(buf, 0, sizeof(buf));
++
+ if (gssd_find_existing_krb5_ccache(uid, &d)) {
+ snprintf(buf, sizeof(buf), "FILE:%s/%s",
+ ccachedir, d->d_name);
+@@ -696,7 +822,7 @@ gssd_refresh_krb5_machine_creds(void)
+ goto out;
+ }
+
+- printerr(1, "Using keytab file '%s'\n", keytabfile);
++ printerr(2, "Using keytab file '%s'\n", keytabfile);
+
+ if ((code = krb5_kt_resolve(context, keytabfile, &kt))) {
+ printerr(0, "ERROR: %s while resolving keytab '%s'\n",
+@@ -711,12 +837,12 @@ gssd_refresh_krb5_machine_creds(void)
+ if (gssd_k5_kt_princ_list == NULL) {
+ printerr(0, "ERROR: No usable keytab entries found in "
+ "keytab '%s'\n", keytabfile);
+- printerr(0, "Do you have a valid keytab entry for "
+- "%s/<your.host>@<YOUR.REALM> in "
++ printerr(0, "You must have a valid keytab entry for "
++ "%s/<your.host>@<YOUR.REALM> on MDT nodes, "
++ "and %s@<YOUR.REALM> on client nodes, in "
+ "keytab file %s ?\n",
+- GSSD_SERVICE_NAME, keytabfile);
+- printerr(0, "Continuing without (machine) credentials "
+- "- nfs4 mounts with Kerberos will fail\n");
++ GSSD_SERVICE_MDS, LUSTRE_ROOT_NAME,
++ keytabfile);
+ }
+ }
+
+@@ -866,6 +992,7 @@ gssd_destroy_krb5_machine_creds(void)
+ krb5_free_context(context);
+ }
+
++#if 0
+ #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+ /*
+ * this routine obtains a credentials handle via gss_acquire_cred()
+@@ -921,7 +1048,9 @@ limit_krb5_enctypes(struct rpc_gss_sec *
+ return 0;
+ }
+ #endif /* HAVE_SET_ALLOWABLE_ENCTYPES */
++#endif
+
++#if 0
+ /*
+ * Obtain supported enctypes from kernel.
+ * Set defaults if info is not available.
+@@ -988,3 +1117,4 @@ gssd_obtain_kernel_krb5_info(void)
+ code);
+ }
+ }
++#endif
+diff -Nrup nfs-utils-1.0.11/utils/gssd/krb5_util.h nfs-utils-1.0.11.lustre/utils/gssd/krb5_util.h
+--- nfs-utils-1.0.11/utils/gssd/krb5_util.h 2007-05-23 14:35:21.000000000 -0600
++++ nfs-utils-1.0.11.lustre/utils/gssd/krb5_util.h 2007-05-23 14:36:39.000000000 -0600
+@@ -10,6 +10,8 @@
+ struct gssd_k5_kt_princ {
+ struct gssd_k5_kt_princ *next;
+ krb5_principal princ;
++ unsigned int fl_root:1,
++ fl_mds:1;
+ char *ccname;
+ char *realm;
+ krb5_timestamp endtime;
+@@ -25,8 +27,4 @@ void gssd_destroy_krb5_machine_creds(voi
+ void gssd_obtain_kernel_krb5_info(void);
+
+
+-#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+-int limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid);
+-#endif
+-
+ #endif /* KRB5_UTIL_H */
+diff -Nrup nfs-utils-1.0.11/utils/gssd/lsupport.c nfs-utils-1.0.11.lustre/utils/gssd/lsupport.c
+--- nfs-utils-1.0.11/utils/gssd/lsupport.c 1969-12-31 17:00:00.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/lsupport.c 2007-05-23 14:36:40.000000000 -0600
+@@ -0,0 +1,787 @@
++/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
++ * vim:expandtab:shiftwidth=8:tabstop=8:
++ *
++ * Copyright (c) 2005 Cluster File Systems, Inc.
++ *
++ * This file is part of Lustre, http://www.lustre.org.
++ *
++ * Lustre is free software; you can redistribute it and/or
++ * modify it under the terms of version 2 of the GNU General Public
++ * License as published by the Free Software Foundation.
++ *
++ * Lustre is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with Lustre; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++#ifndef _GNU_SOURCE
++#define _GNU_SOURCE
++#endif
++#include "config.h"
++#include <sys/param.h>
++#include <sys/utsname.h>
++#include <sys/stat.h>
++#include <sys/socket.h>
++#include <arpa/inet.h>
++#include <sys/types.h>
++#include <sys/ipc.h>
++#include <sys/sem.h>
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <pwd.h>
++#include <grp.h>
++#include <string.h>
++#include <dirent.h>
++#include <poll.h>
++#include <fcntl.h>
++#include <signal.h>
++#include <unistd.h>
++#include <errno.h>
++#include <assert.h>
++#ifdef HAVE_GETHOSTBYNAME
++# include <netdb.h>
++#endif
++
++#ifdef _NEW_BUILD_
++# include "lgss_utils.h"
++#else
++# include "err_util.h"
++# include "gssd.h"
++#endif
++#include "lsupport.h"
++
++/****************************************
++ * exclusive startup *
++ ****************************************/
++
++static struct __sem_s {
++ char *name;
++ key_t sem_key;
++ int sem_id;
++} sems[2] = {
++ [GSSD_CLI] = { "client", 0x3a92d473, 0 },
++ [GSSD_SVC] = { "server", 0x3b92d473, 0 },
++};
++
++void gssd_init_unique(int type)
++{
++ struct __sem_s *sem = &sems[type];
++ struct sembuf sembuf;
++
++ assert(type == GSSD_CLI || type == GSSD_SVC);
++
++again:
++ sem->sem_id = semget(sem->sem_key, 1, IPC_CREAT | IPC_EXCL | 0700);
++ if (sem->sem_id == -1) {
++ if (errno != EEXIST) {
++ printerr(0, "Create sem: %s\n", strerror(errno));
++ exit(-1);
++ }
++
++ /* already exist. Note there's still a small window racing
++ * with other processes, due to the stupid semaphore semantics.
++ */
++ sem->sem_id = semget(sem->sem_key, 0, 0700);
++ if (sem->sem_id == -1) {
++ if (errno == ENOENT) {
++ printerr(0, "another instance just exit, "
++ "try again\n");
++ goto again;
++ }
++
++ printerr(0, "Obtain sem: %s\n", strerror(errno));
++ exit(-1);
++ }
++ } else {
++ int val = 1;
++
++ if (semctl(sem->sem_id, 0, SETVAL, val) == -1) {
++ printerr(0, "Initialize sem: %s\n",
++ strerror(errno));
++ exit(-1);
++ }
++ }
++
++ sembuf.sem_num = 0;
++ sembuf.sem_op = -1;
++ sembuf.sem_flg = IPC_NOWAIT | SEM_UNDO;
++
++ if (semop(sem->sem_id, &sembuf, 1) != 0) {
++ if (errno == EAGAIN) {
++ printerr(0, "Another instance is running, exit\n");
++ exit(0);
++ }
++ printerr(0, "Grab sem: %s\n", strerror(errno));
++ exit(0);
++ }
++
++ printerr(2, "Successfully created %s global identity\n", sem->name);
++}
++
++void gssd_exit_unique(int type)
++{
++ assert(type == GSSD_CLI || type == GSSD_SVC);
++
++ /*
++ * do nothing. we can't remove the sem here, otherwise the race
++ * window would be much bigger. So it's sad we have to leave the
++ * sem in the system forever.
++ */
++}
++
++/****************************************
++ * client side resolvation: *
++ * lnd/netid/nid => hostname *
++ ****************************************/
++
++char gethostname_ex[PATH_MAX] = GSSD_DEFAULT_GETHOSTNAME_EX;
++
++typedef int lnd_nid2hostname_t(char *lnd, uint32_t net, uint32_t addr,
++ char *buf, int buflen);
++
++/* FIXME what about IPv6? */
++static
++int ipv4_nid2hostname(char *lnd, uint32_t net, uint32_t addr,
++ char *buf, int buflen)
++{
++ struct hostent *ent;
++
++ addr = htonl(addr);
++ ent = gethostbyaddr(&addr, sizeof(addr), AF_INET);
++ if (!ent) {
++ printerr(0, "%s: can't resolve 0x%x\n", lnd, addr);
++ return -1;
++ }
++ if (strlen(ent->h_name) >= buflen) {
++ printerr(0, "%s: name too long: %s\n", lnd, ent->h_name);
++ return -1;
++ }
++ strcpy(buf, ent->h_name);
++
++ printerr(2, "%s: net 0x%x, addr 0x%x => %s\n",
++ lnd, net, addr, buf);
++ return 0;
++}
++
++static
++int lolnd_nid2hostname(char *lnd, uint32_t net, uint32_t addr,
++ char *buf, int buflen)
++{
++ struct utsname uts;
++ struct hostent *ent;
++
++ if (addr) {
++ printerr(0, "%s: addr is 0x%x, we expect 0\n", lnd, addr);
++ return -1;
++ }
++
++ if (uname(&uts)) {
++ printerr(0, "%s: failed obtain local machine name\n", lnd);
++ return -1;
++ }
++
++ ent = gethostbyname(uts.nodename);
++ if (!ent) {
++ printerr(0, "%s: failed obtain canonical name of %s\n",
++ lnd, uts.nodename);
++ return -1;
++ }
++
++ if (strlen(ent->h_name) >= buflen) {
++ printerr(0, "%s: name too long: %s\n", lnd, ent->h_name);
++ return -1;
++ }
++ strcpy(buf, ent->h_name);
++
++ printerr(3, "%s: addr 0x%x => %s\n", lnd, addr, buf);
++ return 0;
++}
++
++static int is_space(char c)
++{
++ return (c == ' ' || c == '\t' || c == '\n');
++}
++
++static
++int external_nid2hostname(char *lnd, uint32_t net, uint32_t addr,
++ char *namebuf, int namebuflen)
++{
++ const int bufsize = PATH_MAX + 256;
++ char buf[bufsize], *head, *tail;
++ FILE *fghn;
++
++ sprintf(buf, "%s %s 0x%x 0x%x", gethostname_ex, lnd, net, addr);
++ printerr(2, "cmd: %s\n", buf);
++
++ fghn = popen(buf, "r");
++ if (fghn == NULL) {
++ printerr(0, "failed to call %s\n", gethostname_ex);
++ return -1;
++ }
++
++ head = fgets(buf, bufsize, fghn);
++ if (head == NULL) {
++ printerr(0, "can't read from %s\n", gethostname_ex);
++ return -1;
++ }
++ if (pclose(fghn) == -1)
++ printerr(1, "pclose failed, continue\n");
++
++ /* trim head/tail space */
++ while (is_space(*head))
++ head++;
++
++ tail = head + strlen(head);
++ if (tail <= head) {
++ printerr(0, "no output from %s\n", gethostname_ex);
++ return -1;
++ }
++ while (is_space(*(tail - 1)))
++ tail--;
++ if (tail <= head) {
++ printerr(0, "output are all space from %s\n", gethostname_ex);
++ return -1;
++ }
++ *tail = '\0';
++
++ /* start with '@' means error msg */
++ if (head[0] == '@') {
++ printerr(0, "error from %s: %s\n", gethostname_ex, &head[1]);
++ return -1;
++ }
++
++ if (tail - head > namebuflen) {
++ printerr(0, "external hostname too long: %s\n", head);
++ return -1;
++ }
++
++ printerr(2, "%s: net 0x%x, addr 0x%x => %s\n",
++ lnd, net, addr, head);
++ strcpy(namebuf, head);
++ return 0;
++}
++
++static struct {
++ char *name;
++ lnd_nid2hostname_t *nid2name;
++} converter[LND_ENUM_END_MARKER] = {
++ {"UNUSED0", NULL},
++ [QSWLND] = { "QSWLND", external_nid2hostname},
++ [SOCKLND] = { "SOCKLND", ipv4_nid2hostname },
++ [GMLND] = { "GMLND", external_nid2hostname},
++ [PTLLND] = { "PTLLND", external_nid2hostname },
++ [O2IBLND] = { "O2IBLND", ipv4_nid2hostname },
++ [CIBLND] = { "CIBLND", external_nid2hostname },
++ [OPENIBLND] = { "OPENIBLND",external_nid2hostname },
++ [IIBLND] = { "IIBLND", external_nid2hostname },
++ [LOLND] = { "LOLND", lolnd_nid2hostname },
++ [RALND] = { "RALND", external_nid2hostname },
++ [VIBLND] = { "VIBLND", external_nid2hostname },
++};
++
++int lnet_nid2hostname(lnet_nid_t nid, char *buf, int buflen)
++{
++ uint32_t lnd, net, addr;
++
++ addr = LNET_NIDADDR(nid);
++ net = LNET_NIDNET(nid);
++ lnd = LNET_NETTYP(net);
++
++ if (lnd >= LND_ENUM_END_MARKER) {
++ printerr(0, "ERROR: Unrecognized LND %u\n", lnd);
++ return -1;
++ }
++
++ if (converter[lnd].nid2name == NULL) {
++ printerr(0, "ERROR: %s converter not ready\n",
++ converter[lnd].name);
++ return -1;
++ }
++
++ return converter[lnd].nid2name(converter[lnd].name, net, addr,
++ buf, buflen);
++}
++
++
++/****************************************
++ * lnet support routine *
++ * (from lnet/libcfs/nidstrings.c *
++ ****************************************/
++
++#define LNET_NIDSTR_SIZE 32 /* size of each one (see below for usage) */
++
++static int libcfs_lo_str2addr(char *str, int nob, uint32_t *addr);
++static void libcfs_ip_addr2str(uint32_t addr, char *str);
++static int libcfs_ip_str2addr(char *str, int nob, uint32_t *addr);
++static void libcfs_decnum_addr2str(uint32_t addr, char *str);
++static void libcfs_hexnum_addr2str(uint32_t addr, char *str);
++static int libcfs_num_str2addr(char *str, int nob, uint32_t *addr);
++
++struct netstrfns {
++ int nf_type;
++ char *nf_name;
++ char *nf_modname;
++ void (*nf_addr2str)(uint32_t addr, char *str);
++ int (*nf_str2addr)(char *str, int nob, uint32_t *addr);
++};
++
++static struct netstrfns libcfs_netstrfns[] = {
++ {/* .nf_type */ LOLND,
++ /* .nf_name */ "lo",
++ /* .nf_modname */ "klolnd",
++ /* .nf_addr2str */ libcfs_decnum_addr2str,
++ /* .nf_str2addr */ libcfs_lo_str2addr},
++ {/* .nf_type */ SOCKLND,
++ /* .nf_name */ "tcp",
++ /* .nf_modname */ "ksocklnd",
++ /* .nf_addr2str */ libcfs_ip_addr2str,
++ /* .nf_str2addr */ libcfs_ip_str2addr},
++ {/* .nf_type */ O2IBLND,
++ /* .nf_name */ "o2ib",
++ /* .nf_modname */ "ko2iblnd",
++ /* .nf_addr2str */ libcfs_ip_addr2str,
++ /* .nf_str2addr */ libcfs_ip_str2addr},
++ {/* .nf_type */ CIBLND,
++ /* .nf_name */ "cib",
++ /* .nf_modname */ "kciblnd",
++ /* .nf_addr2str */ libcfs_ip_addr2str,
++ /* .nf_str2addr */ libcfs_ip_str2addr},
++ {/* .nf_type */ OPENIBLND,
++ /* .nf_name */ "openib",
++ /* .nf_modname */ "kopeniblnd",
++ /* .nf_addr2str */ libcfs_ip_addr2str,
++ /* .nf_str2addr */ libcfs_ip_str2addr},
++ {/* .nf_type */ IIBLND,
++ /* .nf_name */ "iib",
++ /* .nf_modname */ "kiiblnd",
++ /* .nf_addr2str */ libcfs_ip_addr2str,
++ /* .nf_str2addr */ libcfs_ip_str2addr},
++ {/* .nf_type */ VIBLND,
++ /* .nf_name */ "vib",
++ /* .nf_modname */ "kviblnd",
++ /* .nf_addr2str */ libcfs_ip_addr2str,
++ /* .nf_str2addr */ libcfs_ip_str2addr},
++ {/* .nf_type */ RALND,
++ /* .nf_name */ "ra",
++ /* .nf_modname */ "kralnd",
++ /* .nf_addr2str */ libcfs_ip_addr2str,
++ /* .nf_str2addr */ libcfs_ip_str2addr},
++ {/* .nf_type */ QSWLND,
++ /* .nf_name */ "elan",
++ /* .nf_modname */ "kqswlnd",
++ /* .nf_addr2str */ libcfs_decnum_addr2str,
++ /* .nf_str2addr */ libcfs_num_str2addr},
++ {/* .nf_type */ GMLND,
++ /* .nf_name */ "gm",
++ /* .nf_modname */ "kgmlnd",
++ /* .nf_addr2str */ libcfs_hexnum_addr2str,
++ /* .nf_str2addr */ libcfs_num_str2addr},
++ {/* .nf_type */ PTLLND,
++ /* .nf_name */ "ptl",
++ /* .nf_modname */ "kptllnd",
++ /* .nf_addr2str */ libcfs_decnum_addr2str,
++ /* .nf_str2addr */ libcfs_num_str2addr},
++ /* placeholder for net0 alias. It MUST BE THE LAST ENTRY */
++ {/* .nf_type */ -1},
++};
++
++const int libcfs_nnetstrfns = sizeof(libcfs_netstrfns)/sizeof(libcfs_netstrfns[0]);
++
++static int
++libcfs_lo_str2addr(char *str, int nob, uint32_t *addr)
++{
++ *addr = 0;
++ return 1;
++}
++
++static void
++libcfs_ip_addr2str(uint32_t addr, char *str)
++{
++ snprintf(str, LNET_NIDSTR_SIZE, "%u.%u.%u.%u",
++ (addr >> 24) & 0xff, (addr >> 16) & 0xff,
++ (addr >> 8) & 0xff, addr & 0xff);
++}
++
++/* CAVEAT EMPTOR XscanfX
++ * I use "%n" at the end of a sscanf format to detect trailing junk. However
++ * sscanf may return immediately if it sees the terminating '0' in a string, so
++ * I initialise the %n variable to the expected length. If sscanf sets it;
++ * fine, if it doesn't, then the scan ended at the end of the string, which is
++ * fine too :) */
++
++static int
++libcfs_ip_str2addr(char *str, int nob, uint32_t *addr)
++{
++ int a;
++ int b;
++ int c;
++ int d;
++ int n = nob; /* XscanfX */
++
++ /* numeric IP? */
++ if (sscanf(str, "%u.%u.%u.%u%n", &a, &b, &c, &d, &n) >= 4 &&
++ n == nob &&
++ (a & ~0xff) == 0 && (b & ~0xff) == 0 &&
++ (c & ~0xff) == 0 && (d & ~0xff) == 0) {
++ *addr = ((a<<24)|(b<<16)|(c<<8)|d);
++ return 1;
++ }
++
++#ifdef HAVE_GETHOSTBYNAME
++ /* known hostname? */
++ if (('a' <= str[0] && str[0] <= 'z') ||
++ ('A' <= str[0] && str[0] <= 'Z')) {
++ char *tmp;
++
++ tmp = malloc(nob + 1);
++ if (tmp != NULL) {
++ struct hostent *he;
++
++ memcpy(tmp, str, nob);
++ tmp[nob] = 0;
++
++ he = gethostbyname(tmp);
++
++ free(tmp);
++ tmp = NULL;
++
++ if (he != NULL) {
++ uint32_t ip = *(uint32_t *)he->h_addr;
++
++ *addr = ntohl(ip);
++ return 1;
++ }
++ }
++ }
++#endif
++ return 0;
++}
++
++static void
++libcfs_decnum_addr2str(uint32_t addr, char *str)
++{
++ snprintf(str, LNET_NIDSTR_SIZE, "%u", addr);
++}
++
++static void
++libcfs_hexnum_addr2str(uint32_t addr, char *str)
++{
++ snprintf(str, LNET_NIDSTR_SIZE, "0x%x", addr);
++}
++
++static int
++libcfs_num_str2addr(char *str, int nob, uint32_t *addr)
++{
++ int n;
++
++ n = nob;
++ if (sscanf(str, "0x%x%n", addr, &n) >= 1 && n == nob)
++ return 1;
++
++ n = nob;
++ if (sscanf(str, "0X%x%n", addr, &n) >= 1 && n == nob)
++ return 1;
++
++ n = nob;
++ if (sscanf(str, "%u%n", addr, &n) >= 1 && n == nob)
++ return 1;
++
++ return 0;
++}
++
++static struct netstrfns *
++libcfs_lnd2netstrfns(int lnd)
++{
++ int i;
++
++ if (lnd >= 0)
++ for (i = 0; i < libcfs_nnetstrfns; i++)
++ if (lnd == libcfs_netstrfns[i].nf_type)
++ return &libcfs_netstrfns[i];
++
++ return NULL;
++}
++
++static struct netstrfns *
++libcfs_str2net_internal(char *str, uint32_t *net)
++{
++ struct netstrfns *nf;
++ int nob;
++ int netnum;
++ int i;
++
++ for (i = 0; i < libcfs_nnetstrfns; i++) {
++ nf = &libcfs_netstrfns[i];
++ if (nf->nf_type >= 0 &&
++ !strncmp(str, nf->nf_name, strlen(nf->nf_name)))
++ break;
++ }
++
++ if (i == libcfs_nnetstrfns)
++ return NULL;
++
++ nob = strlen(nf->nf_name);
++
++ if (strlen(str) == (unsigned int)nob) {
++ netnum = 0;
++ } else {
++ if (nf->nf_type == LOLND) /* net number not allowed */
++ return NULL;
++
++ str += nob;
++ i = strlen(str);
++ if (sscanf(str, "%u%n", &netnum, &i) < 1 ||
++ i != (int)strlen(str))
++ return NULL;
++ }
++
++ *net = LNET_MKNET(nf->nf_type, netnum);
++ return nf;
++}
++
++lnet_nid_t
++libcfs_str2nid(char *str)
++{
++ char *sep = strchr(str, '@');
++ struct netstrfns *nf;
++ uint32_t net;
++ uint32_t addr;
++
++ if (sep != NULL) {
++ nf = libcfs_str2net_internal(sep + 1, &net);
++ if (nf == NULL)
++ return LNET_NID_ANY;
++ } else {
++ sep = str + strlen(str);
++ net = LNET_MKNET(SOCKLND, 0);
++ nf = libcfs_lnd2netstrfns(SOCKLND);
++ if (!nf)
++ return LNET_NID_ANY;
++ }
++
++ if (!nf->nf_str2addr(str, sep - str, &addr))
++ return LNET_NID_ANY;
++
++ return LNET_MKNID(net, addr);
++}
++
++/****************************************
++ * user mapping database handling *
++ * (very rudiment) *
++ ****************************************/
++
++#define MAPPING_GROW_SIZE 512
++#define MAX_LINE_LEN 256
++
++struct user_map_item {
++ char *principal; /* NULL means match all, will cause multi->single mapped, FORBID */
++ lnet_nid_t nid;
++ uid_t uid;
++};
++
++struct user_mapping {
++ int nitems;
++ struct user_map_item *items;
++};
++
++static struct user_mapping mapping = {0, NULL};
++/* FIXME to be finished: monitor change of mapping database */
++static int mapping_mtime = 0;
++
++void cleanup_mapping(void)
++{
++ if (mapping.items) {
++ for (; mapping.nitems > 0; mapping.nitems--)
++ free(mapping.items[mapping.nitems - 1].principal);
++ free(mapping.items);
++ mapping.items = NULL;
++ }
++}
++
++static int grow_mapping(int nitems)
++{
++ struct user_map_item *new;
++ int oldsize, newsize;
++
++ oldsize = (mapping.nitems * sizeof(struct user_map_item) +
++ MAPPING_GROW_SIZE - 1) / MAPPING_GROW_SIZE;
++ newsize = (nitems * sizeof(struct user_map_item) +
++ MAPPING_GROW_SIZE - 1) / MAPPING_GROW_SIZE;
++ while (newsize <= oldsize)
++ return 0;
++
++ newsize *= MAPPING_GROW_SIZE;
++ new = malloc(newsize);
++ if (!new) {
++ printerr(0, "can't alloc mapping size %d\n", newsize);
++ return -1;
++ }
++
++ if (mapping.items) {
++ memcpy(new, mapping.items, mapping.nitems * sizeof(struct user_map_item));
++ free(mapping.items);
++ }
++ mapping.items = new;
++ return 0;
++}
++
++uid_t parse_uid(char *uidstr)
++{
++ struct passwd *pw;
++ char *p = NULL;
++ long uid;
++
++ pw = getpwnam(uidstr);
++ if (pw)
++ return pw->pw_uid;
++
++ uid = strtol(uidstr, &p, 0);
++ if (*p == '\0')
++ return (uid_t) uid;
++
++ return -1;
++}
++
++static int read_mapping_db(void)
++{
++ char princ[MAX_LINE_LEN];
++ char nid_str[MAX_LINE_LEN];
++ char dest[MAX_LINE_LEN];
++ char linebuf[MAX_LINE_LEN];
++ char *line;
++ lnet_nid_t nid;
++ uid_t dest_uid;
++ FILE *f;
++
++ /* cleanup old mappings */
++ cleanup_mapping();
++
++ f = fopen(MAPPING_DATABASE_FILE, "r");
++ if (!f) {
++ printerr(0, "can't open mapping database: %s\n",
++ MAPPING_DATABASE_FILE);
++ return -1;
++ }
++
++ while ((line = fgets(linebuf, MAX_LINE_LEN, f)) != NULL) {
++ char *name;
++
++ if (strlen(line) >= MAX_LINE_LEN) {
++ printerr(0, "invalid mapping db: line too long (%d)\n",
++ strlen(line));
++ continue;
++ }
++
++ if (sscanf(line, "%s %s %s", princ, nid_str, dest) != 3) {
++ printerr(0, "mapping db: syntax error\n");
++ continue;
++ }
++
++ if (!strcmp(princ, "*")) {
++ printerr(0, "NOT permit \"*\" princ, it will cause multi->single mapped\n");
++ continue;
++ } else {
++ name = strdup(princ);
++ if (!name) {
++ printerr(0, "fail to dup str %s\n", princ);
++ continue;
++ }
++ }
++
++ if (!strcmp(nid_str, "*")) {
++ nid = LNET_NID_ANY;
++ } else {
++ nid = libcfs_str2nid(nid_str);
++ if (nid == LNET_NID_ANY) {
++ printerr(0, "fail to parse nid %s\n", nid_str);
++ free(name);
++ continue;
++ }
++ }
++
++ dest_uid = parse_uid(dest);
++ if (dest_uid == -1) {
++ printerr(0, "no valid user: %s\n", dest);
++ free(name);
++ continue;
++ }
++
++ if (grow_mapping(mapping.nitems + 1)) {
++ printerr(0, "fail to grow mapping to %d\n",
++ mapping.nitems + 1);
++ free(name);
++ fclose(f);
++ return -1;
++ }
++
++ mapping.items[mapping.nitems].principal = name;
++ mapping.items[mapping.nitems].nid = nid;
++ mapping.items[mapping.nitems].uid = dest_uid;
++ mapping.nitems++;
++ printerr(1, "add mapping: %s(%s/0x%llx) ==> %d\n",
++ name, nid_str, nid, dest_uid);
++ }
++
++ fclose(f);
++ return 0;
++}
++
++static inline int mapping_changed(void)
++{
++ struct stat st;
++
++ if (stat(MAPPING_DATABASE_FILE, &st) == -1) {
++ /* stat failed, treat it like doesn't exist or be removed */
++ if (mapping_mtime == 0) {
++ return 0;
++ } else {
++ printerr(0, "Warning: stat %s failed: %s\n",
++ MAPPING_DATABASE_FILE, strerror(errno));
++
++ mapping_mtime = 0;
++ return 1;
++ }
++ }
++
++ if (st.st_mtime != mapping_mtime) {
++ mapping_mtime = st.st_mtime;
++ return 1;
++ }
++
++ return 0;
++}
++
++int lookup_mapping(char *princ, lnet_nid_t nid, uid_t *uid)
++{
++ int n;
++
++ *uid = -1;
++
++ /* FIXME race condition here */
++ if (mapping_changed()) {
++ if (read_mapping_db())
++ printerr(0, "all remote users will be denied\n");
++ }
++
++ for (n = 0; n < mapping.nitems; n++) {
++ struct user_map_item *entry = &mapping.items[n];
++
++ if (entry->nid != LNET_NID_ANY && entry->nid != nid)
++ continue;
++ if (!strcasecmp(entry->principal, princ)) {
++ printerr(1, "found mapping: %s ==> %d\n",
++ princ, entry->uid);
++ *uid = entry->uid;
++ return 0;
++ }
++ }
++
++ printerr(2, "no mapping for %s/%#Lx\n", princ, nid);
++ return -1;
++}
+diff -Nrup nfs-utils-1.0.11/utils/gssd/lsupport.h nfs-utils-1.0.11.lustre/utils/gssd/lsupport.h
+--- nfs-utils-1.0.11/utils/gssd/lsupport.h 1969-12-31 17:00:00.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/lsupport.h 2007-05-23 14:36:41.000000000 -0600
+@@ -0,0 +1,89 @@
++/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
++ * vim:expandtab:shiftwidth=8:tabstop=8:
++ */
++
++#ifndef __LIBCFS_H__
++#define __LIBCFS_H__
++
++#include <unistd.h>
++#include <stdint.h>
++
++#define GSSD_CLI (0)
++#define GSSD_SVC (1)
++
++void gssd_init_unique(int type);
++void gssd_exit_unique(int type);
++
++/*
++ * copied from lustre source
++ */
++
++#define LUSTRE_GSS_SVC_MDS 0
++#define LUSTRE_GSS_SVC_OSS 1
++
++struct lgssd_upcall_data {
++ uint32_t seq;
++ uint32_t uid;
++ uint32_t gid;
++ uint32_t svc;
++ uint64_t nid;
++ char obd[64];
++};
++
++#define GSSD_INTERFACE_VERSION (1)
++
++struct lgssd_ioctl_param {
++ int version; /* in */
++ char *uuid; /* in */
++ int lustre_svc; /* in */
++ uid_t uid; /* in */
++ gid_t gid; /* in */
++ long send_token_size;/* in */
++ char *send_token; /* in */
++ long reply_buf_size; /* in */
++ char *reply_buf; /* in */
++ long status; /* out */
++ long reply_length; /* out */
++};
++
++#define GSSD_DEFAULT_GETHOSTNAME_EX "/etc/lustre/nid2hostname"
++#define MAPPING_DATABASE_FILE "/etc/lustre/idmap.conf"
++
++typedef uint64_t lnet_nid_t;
++typedef uint32_t lnet_netid_t;
++
++#define LNET_NID_ANY ((lnet_nid_t) -1)
++#define LNET_PID_ANY ((lnet_pid_t) -1)
++
++enum {
++ /* Only add to these values (i.e. don't ever change or redefine them):
++ * network addresses depend on them... */
++ QSWLND = 1,
++ SOCKLND = 2,
++ GMLND = 3,
++ PTLLND = 4,
++ O2IBLND = 5,
++ CIBLND = 6,
++ OPENIBLND = 7,
++ IIBLND = 8,
++ LOLND = 9,
++ RALND = 10,
++ VIBLND = 11,
++ LND_ENUM_END_MARKER
++};
++
++int lnet_nid2hostname(lnet_nid_t nid, char *buf, int buflen);
++void cleanup_mapping(void);
++int lookup_mapping(char *princ, uint64_t nid, uid_t *uid);
++lnet_nid_t libcfs_str2nid(char *str);
++
++/* how an LNET NID encodes net:address */
++#define LNET_NIDADDR(nid) ((uint32_t)((nid) & 0xffffffff))
++#define LNET_NIDNET(nid) ((uint32_t)(((nid) >> 32)) & 0xffffffff)
++#define LNET_MKNID(net,addr) ((((uint64_t)(net))<<32)|((uint64_t)(addr)))
++/* how net encodes type:number */
++#define LNET_NETNUM(net) ((net) & 0xffff)
++#define LNET_NETTYP(net) (((net) >> 16) & 0xffff)
++#define LNET_MKNET(typ,num) ((((uint32_t)(typ))<<16)|((uint32_t)(num)))
++
++#endif /* __LIBCFS_H__ */
+diff -Nrup nfs-utils-1.0.11/utils/gssd/Makefile.am nfs-utils-1.0.11.lustre/utils/gssd/Makefile.am
+--- nfs-utils-1.0.11/utils/gssd/Makefile.am 2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/Makefile.am 2007-05-23 14:35:45.000000000 -0600
+@@ -1,17 +1,11 @@
+ ## Process this file with automake to produce Makefile.in
+
+-man8_MANS = gssd.man svcgssd.man
+-
+-RPCPREFIX = rpc.
++RPCPREFIX =
+ KPREFIX = @kprefix@
+-sbin_PREFIXED = gssd svcgssd
+-sbin_PROGRAMS = $(sbin_PREFIXED) gss_clnt_send_err
++sbin_PREFIXED = lgssd lsvcgssd
++sbin_PROGRAMS = $(sbin_PREFIXED)
+ sbin_SCRIPTS = gss_destroy_creds
+
+-EXTRA_DIST = \
+- gss_destroy_creds \
+- $(man8_MANS)
+-
+ COMMON_SRCS = \
+ context.c \
+ context_mit.c \
+@@ -21,13 +15,15 @@ COMMON_SRCS = \
+ gss_util.c \
+ gss_oids.c \
+ err_util.c \
++ lsupport.c \
+ \
+ context.h \
+ err_util.h \
+ gss_oids.h \
+- gss_util.h
++ gss_util.h \
++ lsupport.h
+
+-gssd_SOURCES = \
++lgssd_SOURCES = \
+ $(COMMON_SRCS) \
+ gssd.c \
+ gssd_main_loop.c \
+@@ -38,13 +34,12 @@ gssd_SOURCES = \
+ krb5_util.h \
+ write_bytes.h
+
+-gssd_LDADD = $(RPCSECGSS_LIBS) $(KRBLIBS)
+-gssd_LDFLAGS = $(KRBLDFLAGS)
++lgssd_LDADD = $(GSSAPI_LIBS) $(KRBLIBS)
++lgssd_LDFLAGS = $(KRBLDFLAGS)
+
+-gssd_CFLAGS = $(AM_CFLAGS) $(CFLAGS) \
+- $(RPCSECGSS_CFLAGS) $(KRBCFLAGS)
++lgssd_CFLAGS = $(AM_CFLAGS) $(CFLAGS) $(GSSAPI_CFLAGS) $(KRBCFLAGS)
+
+-svcgssd_SOURCES = \
++lsvcgssd_SOURCES = \
+ $(COMMON_SRCS) \
+ cacheio.c \
+ svcgssd.c \
+@@ -55,20 +50,11 @@ svcgssd_SOURCES = \
+ cacheio.h \
+ svcgssd.h
+
+-svcgssd_LDADD = \
+- ../../support/nfs/libnfs.a \
+- $(RPCSECGSS_LIBS) -lnfsidmap \
+- $(KRBLIBS)
+-
+-svcgssd_LDFLAGS = $(KRBLDFLAGS)
+-
+-svcgssd_CFLAGS = $(AM_CFLAGS) $(CFLAGS) \
+- $(RPCSECGSS_CFLAGS) $(KRBCFLAGS)
++lsvcgssd_LDADD = $(GSSAPI_LIBS) $(KRBLIBS)
+
+-gss_clnt_send_err_SOURCES = gss_clnt_send_err.c
++lsvcgssd_LDFLAGS = $(KRBLDFLAGS)
+
+-gss_clnt_send_err_CFLAGS = $(AM_CFLAGS) $(CFLAGS) \
+- $(RPCSECGSS_CFLAGS) $(KRBCFLAGS)
++lsvcgssd_CFLAGS = $(AM_CFLAGS) $(CFLAGS) $(GSSAPI_CFLAGS) $(KRBCFLAGS)
+
+ MAINTAINERCLEANFILES = Makefile.in
+
+@@ -92,23 +78,3 @@ uninstall-hook:
+ done)
+
+
+-# XXX This makes some assumptions about what automake does.
+-# XXX But there is no install-man-hook or install-man-local.
+-install-man: install-man8 install-man-links
+-uninstall-man: uninstall-man8 uninstall-man-links
+-
+-install-man-links:
+- (cd $(DESTDIR)$(man8dir) && \
+- for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+- inst=`echo $$m | sed -e 's/man$$/8/'`; \
+- rm -f $(RPCPREFIX)$$inst ; \
+- $(LN_S) $$inst $(RPCPREFIX)$$inst ; \
+- done)
+-
+-uninstall-man-links:
+- (cd $(DESTDIR)$(man8dir) && \
+- for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+- inst=`echo $$m | sed -e 's/man$$/8/'`; \
+- rm -f $(RPCPREFIX)$$inst ; \
+- done)
+-
+diff -Nrup nfs-utils-1.0.11/utils/gssd/svcgssd.c nfs-utils-1.0.11.lustre/utils/gssd/svcgssd.c
+--- nfs-utils-1.0.11/utils/gssd/svcgssd.c 2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/svcgssd.c 2007-05-23 14:36:41.000000000 -0600
+@@ -43,7 +43,6 @@
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <sys/socket.h>
+-#include <rpc/rpc.h>
+ #include <fcntl.h>
+ #include <errno.h>
+
+@@ -54,11 +53,33 @@
+ #include <stdlib.h>
+ #include <string.h>
+ #include <signal.h>
+-#include <nfsidmap.h>
+-#include "nfslib.h"
++#include <dirent.h>
+ #include "svcgssd.h"
+ #include "gss_util.h"
+ #include "err_util.h"
++#include "lsupport.h"
++
++void
++closeall(int min)
++{
++ DIR *dir = opendir("/proc/self/fd");
++ if (dir != NULL) {
++ int dfd = dirfd(dir);
++ struct dirent *d;
++
++ while ((d = readdir(dir)) != NULL) {
++ char *endp;
++ long n = strtol(d->d_name, &endp, 10);
++ if (*endp != '\0' && n >= min && n != dfd)
++ (void) close(n);
++ }
++ closedir(dir);
++ } else {
++ int fd = sysconf(_SC_OPEN_MAX);
++ while (--fd >= min)
++ (void) close(fd);
++ }
++}
+
+ /*
+ * mydaemon creates a pipe between the partent and child
+@@ -140,6 +161,7 @@ void
+ sig_die(int signal)
+ {
+ /* destroy krb5 machine creds */
++ cleanup_mapping();
+ printerr(1, "exiting on signal %d\n", signal);
+ exit(1);
+ }
+@@ -166,9 +188,8 @@ main(int argc, char *argv[])
+ int get_creds = 1;
+ int fg = 0;
+ int verbosity = 0;
+- int rpc_verbosity = 0;
+- int idmap_verbosity = 0;
+ int opt;
++ int must_srv_mds = 0, must_srv_oss = 0;
+ extern char *optarg;
+ char *progname;
+
+@@ -177,17 +198,19 @@ main(int argc, char *argv[])
+ case 'f':
+ fg = 1;
+ break;
+- case 'i':
+- idmap_verbosity++;
+- break;
+ case 'n':
+ get_creds = 0;
+ break;
+ case 'v':
+ verbosity++;
+ break;
+- case 'r':
+- rpc_verbosity++;
++ case 'm':
++ get_creds = 1;
++ must_srv_mds = 1;
++ break;
++ case 'o':
++ get_creds = 1;
++ must_srv_oss = 1;
+ break;
+ default:
+ usage(argv[0]);
+@@ -201,34 +224,18 @@ main(int argc, char *argv[])
+ progname = argv[0];
+
+ initerr(progname, verbosity, fg);
+-#ifdef HAVE_AUTHGSS_SET_DEBUG_LEVEL
+- authgss_set_debug_level(rpc_verbosity);
+-#else
+- if (rpc_verbosity > 0)
+- printerr(0, "Warning: rpcsec_gss library does not "
+- "support setting debug level\n");
+-#endif
+-#ifdef HAVE_NFS4_SET_DEBUG
+- nfs4_set_debug(idmap_verbosity, NULL);
+-#else
+- if (idmap_verbosity > 0)
+- printerr(0, "Warning: your nfsidmap library does not "
+- "support setting debug level\n");
+-#endif
+
+ if (gssd_check_mechs() != 0) {
+ printerr(0, "ERROR: Problem with gssapi library\n");
+ exit(1);
+ }
+
+- if (!fg)
+- mydaemon(0, 0);
+-
+- signal(SIGINT, sig_die);
+- signal(SIGTERM, sig_die);
+- signal(SIGHUP, sig_hup);
+-
+- if (get_creds && !gssd_acquire_cred(GSSD_SERVICE_NAME)) {
++ if (gssd_get_local_realm()) {
++ printerr(0, "ERROR: Can't get Local Kerberos realm\n");
++ exit(1);
++ }
++
++ if (get_creds && gssd_prepare_creds(must_srv_mds, must_srv_oss)) {
+ printerr(0, "unable to obtain root (machine) credentials\n");
+ printerr(0, "do you have a keytab entry for "
+ "nfs/<your.host>@<YOUR.REALM> in "
+@@ -237,9 +244,23 @@ main(int argc, char *argv[])
+ }
+
+ if (!fg)
++ mydaemon(0, 0);
++
++ /*
++ * XXX: There is risk of memory leak for missing call
++ * cleanup_mapping() for SIGKILL and SIGSTOP.
++ */
++ signal(SIGINT, sig_die);
++ signal(SIGTERM, sig_die);
++ signal(SIGHUP, sig_hup);
++
++ if (!fg)
+ release_parent();
+
+- gssd_run();
++ gssd_init_unique(GSSD_SVC);
++
++ svcgssd_run();
++ cleanup_mapping();
+ printerr(0, "gssd_run returned!\n");
+ abort();
+ }
+diff -Nrup nfs-utils-1.0.11/utils/gssd/svcgssd.h nfs-utils-1.0.11.lustre/utils/gssd/svcgssd.h
+--- nfs-utils-1.0.11/utils/gssd/svcgssd.h 2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/svcgssd.h 2007-05-23 14:36:42.000000000 -0600
+@@ -35,9 +35,20 @@
+ #include <sys/queue.h>
+ #include <gssapi/gssapi.h>
+
+-void handle_nullreq(FILE *f);
+-void gssd_run(void);
++int handle_nullreq(FILE *f);
++void svcgssd_run(void);
++int gssd_prepare_creds(int must_srv_mds, int must_srv_oss);
++gss_cred_id_t gssd_select_svc_cred(int lustre_svc);
+
+-#define GSSD_SERVICE_NAME "nfs"
++extern char *mds_local_realm;
++extern char *oss_local_realm;
++
++#define GSSD_SERVICE_NAME "lustre"
++
++/* XXX */
++#define GSSD_SERVICE_MDS "lustre_mds"
++#define GSSD_SERVICE_OSS "lustre_oss"
++#define LUSTRE_ROOT_NAME "lustre_root"
++#define LUSTRE_ROOT_NAMELEN 11
+
+ #endif /* _RPC_SVCGSSD_H_ */
+diff -Nrup nfs-utils-1.0.11/utils/gssd/svcgssd_main_loop.c nfs-utils-1.0.11.lustre/utils/gssd/svcgssd_main_loop.c
+--- nfs-utils-1.0.11/utils/gssd/svcgssd_main_loop.c 2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/svcgssd_main_loop.c 2007-05-23 14:36:42.000000000 -0600
+@@ -46,46 +46,66 @@
+ #include "svcgssd.h"
+ #include "err_util.h"
+
++/*
++ * nfs4 in-kernel cache implementation make upcall failed directly
++ * if there's no listener detected. so here we should keep the init
++ * channel file open as possible as we can.
++ *
++ * unfortunately the proc doesn't support dir change notification.
++ * and when an entry get unlinked, we only got POLLIN event once,
++ * it's the only oppotunity we can close the file and startover.
++ */
+ void
+-gssd_run()
++svcgssd_run()
+ {
+ int ret;
+- FILE *f;
++ FILE *f = NULL;
+ struct pollfd pollfd;
++ struct timespec halfsec = { .tv_sec = 0, .tv_nsec = 500000000 };
+
+-#define NULLRPC_FILE "/proc/net/rpc/auth.rpcsec.init/channel"
++#define NULLRPC_FILE "/proc/net/rpc/auth.ptlrpcs.init/channel"
+
+- f = fopen(NULLRPC_FILE, "rw");
+-
+- if (!f) {
+- printerr(0, "failed to open %s: %s\n",
+- NULLRPC_FILE, strerror(errno));
+- exit(1);
+- }
+- pollfd.fd = fileno(f);
+- pollfd.events = POLLIN;
+ while (1) {
+ int save_err;
+
++ while (f == NULL) {
++ f = fopen(NULLRPC_FILE, "rw");
++ if (f == NULL) {
++ printerr(4, "failed to open %s: %s\n",
++ NULLRPC_FILE, strerror(errno));
++ nanosleep(&halfsec, NULL);
++ } else {
++ printerr(1, "successfully open %s\n",
++ NULLRPC_FILE);
++ break;
++ }
++ }
++ pollfd.fd = fileno(f);
++ pollfd.events = POLLIN;
++
+ pollfd.revents = 0;
+- printerr(1, "entering poll\n");
+- ret = poll(&pollfd, 1, -1);
++ ret = poll(&pollfd, 1, 1000);
+ save_err = errno;
+- printerr(1, "leaving poll\n");
++
+ if (ret < 0) {
+- if (save_err != EINTR)
+- printerr(0, "error return from poll: %s\n",
+- strerror(save_err));
++ printerr(0, "error return from poll: %s\n",
++ strerror(save_err));
++ fclose(f);
++ f = NULL;
+ } else if (ret == 0) {
+- /* timeout; shouldn't happen. */
++ printerr(3, "poll timeout\n");
+ } else {
+ if (ret != 1) {
+ printerr(0, "bug: unexpected poll return %d\n",
+ ret);
+ exit(1);
+ }
+- if (pollfd.revents & POLLIN)
+- handle_nullreq(f);
++ if (pollfd.revents & POLLIN) {
++ if (handle_nullreq(f) < 0) {
++ fclose(f);
++ f = NULL;
++ }
++ }
+ }
+ }
+ }
+diff -Nrup nfs-utils-1.0.11/utils/gssd/svcgssd_proc.c nfs-utils-1.0.11.lustre/utils/gssd/svcgssd_proc.c
+--- nfs-utils-1.0.11/utils/gssd/svcgssd_proc.c 2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/svcgssd_proc.c 2007-05-23 14:36:44.000000000 -0600
+@@ -35,7 +35,6 @@
+
+ #include <sys/param.h>
+ #include <sys/stat.h>
+-#include <rpc/rpc.h>
+
+ #include <pwd.h>
+ #include <stdio.h>
+@@ -44,25 +43,28 @@
+ #include <string.h>
+ #include <fcntl.h>
+ #include <errno.h>
+-#include <nfsidmap.h>
++#include <netdb.h>
+
+ #include "svcgssd.h"
+ #include "gss_util.h"
+ #include "err_util.h"
+ #include "context.h"
+ #include "cacheio.h"
++#include "lsupport.h"
+
+ extern char * mech2file(gss_OID mech);
+-#define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.rpcsec.context/channel"
+-#define SVCGSSD_INIT_CHANNEL "/proc/net/rpc/auth.rpcsec.init/channel"
++#define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.ptlrpcs.context/channel"
++#define SVCGSSD_INIT_CHANNEL "/proc/net/rpc/auth.ptlrpcs.init/channel"
+
+ #define TOKEN_BUF_SIZE 8192
+
+ struct svc_cred {
+- uid_t cr_uid;
+- gid_t cr_gid;
+- int cr_ngroups;
+- gid_t cr_groups[NGROUPS];
++ uint32_t cr_remote;
++ uint32_t cr_usr_root;
++ uint32_t cr_usr_mds;
++ uid_t cr_uid;
++ uid_t cr_mapped_uid;
++ uid_t cr_gid;
+ };
+
+ static int
+@@ -70,11 +72,10 @@ do_svc_downcall(gss_buffer_desc *out_han
+ gss_OID mech, gss_buffer_desc *context_token)
+ {
+ FILE *f;
+- int i;
+ char *fname = NULL;
+ int err;
+
+- printerr(1, "doing downcall\n");
++ printerr(2, "doing downcall\n");
+ if ((fname = mech2file(mech)) == NULL)
+ goto out_err;
+ f = fopen(SVCGSSD_CONTEXT_CHANNEL, "w");
+@@ -87,11 +88,12 @@ do_svc_downcall(gss_buffer_desc *out_han
+ qword_printhex(f, out_handle->value, out_handle->length);
+ /* XXX are types OK for the rest of this? */
+ qword_printint(f, 0x7fffffff); /*XXX need a better timeout */
++ qword_printint(f, cred->cr_remote);
++ qword_printint(f, cred->cr_usr_root);
++ qword_printint(f, cred->cr_usr_mds);
++ qword_printint(f, cred->cr_mapped_uid);
+ qword_printint(f, cred->cr_uid);
+ qword_printint(f, cred->cr_gid);
+- qword_printint(f, cred->cr_ngroups);
+- for (i=0; i < cred->cr_ngroups; i++)
+- qword_printint(f, cred->cr_groups[i]);
+ qword_print(f, fname);
+ qword_printhex(f, context_token->value, context_token->length);
+ err = qword_eol(f);
+@@ -120,7 +122,7 @@ send_response(FILE *f, gss_buffer_desc *
+ /* XXXARG: */
+ int g;
+
+- printerr(1, "sending null reply\n");
++ printerr(2, "sending null reply\n");
+
+ qword_addhex(&bp, &blen, in_handle->value, in_handle->length);
+ qword_addhex(&bp, &blen, in_token->value, in_token->length);
+@@ -160,6 +162,7 @@ send_response(FILE *f, gss_buffer_desc *
+ #define rpcsec_gsserr_credproblem 13
+ #define rpcsec_gsserr_ctxproblem 14
+
++#if 0
+ static void
+ add_supplementary_groups(char *secname, char *name, struct svc_cred *cred)
+ {
+@@ -183,7 +186,9 @@ add_supplementary_groups(char *secname,
+ }
+ }
+ }
++#endif
+
++#if 0
+ static int
+ get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred)
+ {
+@@ -249,7 +254,9 @@ out_free:
+ out:
+ return res;
+ }
++#endif
+
++#if 0
+ void
+ print_hexl(int pri, unsigned char *cp, int length)
+ {
+@@ -286,12 +293,121 @@ print_hexl(int pri, unsigned char *cp, i
+ printerr(pri,"\n");
+ }
+ }
++#endif
+
+-void
++static int
++get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred,
++ lnet_nid_t nid, uint32_t lustre_svc)
++{
++ u_int32_t maj_stat, min_stat;
++ gss_buffer_desc name;
++ char *sname, *realm, *slash;
++ int res = -1;
++ gss_OID name_type = GSS_C_NO_OID;
++ struct passwd *pw;
++
++ cred->cr_remote = cred->cr_usr_root = cred->cr_usr_mds = 0;
++ cred->cr_uid = cred->cr_mapped_uid = cred->cr_gid = -1;
++
++ maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
++ if (maj_stat != GSS_S_COMPLETE) {
++ pgsserr("get_ids: gss_display_name",
++ maj_stat, min_stat, mech);
++ return -1;
++ }
++ if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
++ !(sname = calloc(name.length + 1, 1))) {
++ printerr(0, "WARNING: get_ids: error allocating %d bytes "
++ "for sname\n", name.length + 1);
++ gss_release_buffer(&min_stat, &name);
++ return -1;
++ }
++ memcpy(sname, name.value, name.length);
++ printerr(1, "authenticated %s from %016llx\n", sname, nid);
++ gss_release_buffer(&min_stat, &name);
++
++ if (lustre_svc == LUSTRE_GSS_SVC_MDS)
++ lookup_mapping(sname, nid, &cred->cr_mapped_uid);
++ else
++ cred->cr_mapped_uid = -1;
++
++ realm = strchr(sname, '@');
++ if (!realm) {
++ printerr(0, "WARNNING: principal %s contains no realm name\n",
++ sname);
++ cred->cr_remote = (mds_local_realm != NULL);
++ } else {
++ *realm++ = '\0';
++ if (!mds_local_realm)
++ cred->cr_remote = 1;
++ else
++ cred->cr_remote =
++ (strcasecmp(mds_local_realm, realm) != 0);
++ }
++
++ if (cred->cr_remote) {
++ if (cred->cr_mapped_uid != -1)
++ res = 0;
++ else if (lustre_svc == LUSTRE_GSS_SVC_OSS &&
++ strcmp(sname, "lustre_root") == 0)
++ res = 0;
++ else
++ printerr(0, "principal %s is remote without mapping\n",
++ sname);
++ goto out_free;
++ }
++
++ slash = strchr(sname, '/');
++ if (slash)
++ *slash = '\0';
++
++ if (!(pw = getpwnam(sname))) {
++ /* If client use machine credential, we map it to root, which
++ * will subject to further mapping by root-squash in kernel.
++ *
++ * MDS service keytab is treated as special user, also mapped
++ * to root. OSS service keytab can't be used as a user.
++ */
++ if (!strcmp(sname, LUSTRE_ROOT_NAME)) {
++ printerr(2, "lustre_root principal, resolve to uid 0\n");
++ cred->cr_uid = 0;
++ cred->cr_usr_root = 1;
++ } else if (!strcmp(sname, GSSD_SERVICE_MDS)) {
++ printerr(2, "mds service principal, resolve to uid 0\n");
++ cred->cr_uid = 0;
++ cred->cr_usr_mds = 1;
++ } else {
++ cred->cr_uid = -1;
++ if (cred->cr_mapped_uid == -1) {
++ printerr(0, "invalid user %s\n", sname);
++ goto out_free;
++ }
++ printerr(2, "user %s mapped to %u\n",
++ sname, cred->cr_mapped_uid);
++ }
++ } else {
++ cred->cr_uid = pw->pw_uid;
++ printerr(2, "%s resolve to uid %u\n", sname, cred->cr_uid);
++ }
++
++ res = 0;
++out_free:
++ free(sname);
++ return res;
++}
++
++typedef struct gss_union_ctx_id_t {
++ gss_OID mech_type;
++ gss_ctx_id_t internal_ctx_id;
++} gss_union_ctx_id_desc, *gss_union_ctx_id_t;
++
++/*
++ * return -1 only if we detect error during reading from upcall channel,
++ * all other cases return 0.
++ */
++int
+ handle_nullreq(FILE *f) {
+- /* XXX initialize to a random integer to reduce chances of unnecessary
+- * invalidation of existing ctx's on restarting svcgssd. */
+- static u_int32_t handle_seq = 0;
++ uint64_t handle_seq;
+ char in_tok_buf[TOKEN_BUF_SIZE];
+ char in_handle_buf[15];
+ char out_handle_buf[15];
+@@ -303,10 +419,13 @@ handle_nullreq(FILE *f) {
+ ignore_out_tok = {.value = NULL},
+ /* XXX isn't there a define for this?: */
+ null_token = {.value = NULL};
++ uint32_t lustre_svc;
++ lnet_nid_t nid;
+ u_int32_t ret_flags;
+ gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
+ gss_name_t client_name;
+ gss_OID mech = GSS_C_NO_OID;
++ gss_cred_id_t svc_cred;
+ u_int32_t maj_stat = GSS_S_FAILURE, min_stat = 0;
+ u_int32_t ignore_min_stat;
+ struct svc_cred cred;
+@@ -314,25 +433,31 @@ handle_nullreq(FILE *f) {
+ static int lbuflen = 0;
+ static char *cp;
+
+- printerr(1, "handling null request\n");
++ printerr(2, "handling null request\n");
+
+ if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
+ printerr(0, "WARNING: handle_nullreq: "
+ "failed reading request\n");
+- return;
++ return -1;
+ }
+
+ cp = lbuf;
+
++ qword_get(&cp, (char *) &lustre_svc, sizeof(lustre_svc));
++ qword_get(&cp, (char *) &nid, sizeof(nid));
++ qword_get(&cp, (char *) &handle_seq, sizeof(handle_seq));
++ printerr(1, "handling req: svc %u, nid %016llx, idx %llx\n",
++ lustre_svc, nid, handle_seq);
++
+ in_handle.length = (size_t) qword_get(&cp, in_handle.value,
+ sizeof(in_handle_buf));
+- printerr(2, "in_handle: \n");
+- print_hexl(2, in_handle.value, in_handle.length);
++ printerr(3, "in_handle: \n");
++ print_hexl(3, in_handle.value, in_handle.length);
+
+ in_tok.length = (size_t) qword_get(&cp, in_tok.value,
+ sizeof(in_tok_buf));
+- printerr(2, "in_tok: \n");
+- print_hexl(2, in_tok.value, in_tok.length);
++ printerr(3, "in_tok: \n");
++ print_hexl(3, in_tok.value, in_tok.length);
+
+ if (in_tok.length < 0) {
+ printerr(0, "WARNING: handle_nullreq: "
+@@ -352,7 +477,13 @@ handle_nullreq(FILE *f) {
+ memcpy(&ctx, in_handle.value, in_handle.length);
+ }
+
+- maj_stat = gss_accept_sec_context(&min_stat, &ctx, gssd_creds,
++ svc_cred = gssd_select_svc_cred(lustre_svc);
++ if (!svc_cred) {
++ printerr(0, "no service credential for svc %u\n", lustre_svc);
++ goto out_err;
++ }
++
++ maj_stat = gss_accept_sec_context(&min_stat, &ctx, svc_cred,
+ &in_tok, GSS_C_NO_CHANNEL_BINDINGS, &client_name,
+ &mech, &out_tok, &ret_flags, NULL, NULL);
+
+@@ -370,7 +501,8 @@ handle_nullreq(FILE *f) {
+ maj_stat, min_stat, mech);
+ goto out_err;
+ }
+- if (get_ids(client_name, mech, &cred)) {
++
++ if (get_ids(client_name, mech, &cred, nid, lustre_svc)) {
+ /* get_ids() prints error msg */
+ maj_stat = GSS_S_BAD_NAME; /* XXX ? */
+ gss_release_name(&ignore_min_stat, &client_name);
+@@ -378,10 +510,8 @@ handle_nullreq(FILE *f) {
+ }
+ gss_release_name(&ignore_min_stat, &client_name);
+
+-
+ /* Context complete. Pass handle_seq in out_handle to use
+ * for context lookup in the kernel. */
+- handle_seq++;
+ out_handle.length = sizeof(handle_seq);
+ memcpy(out_handle.value, &handle_seq, sizeof(handle_seq));
+
+@@ -405,8 +535,7 @@ out:
+ free(ctx_token.value);
+ if (out_tok.value != NULL)
+ gss_release_buffer(&ignore_min_stat, &out_tok);
+- printerr(1, "finished handling null request\n");
+- return;
++ return 0;
+
+ out_err:
+ if (ctx != GSS_C_NO_CONTEXT)
+diff -Nrup nfs-utils-1.0.11/utils/Makefile.am nfs-utils-1.0.11.lustre/utils/Makefile.am
+--- nfs-utils-1.0.11/utils/Makefile.am 2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/Makefile.am 2007-05-23 14:35:45.000000000 -0600
+@@ -2,30 +2,6 @@
+
+ OPTDIRS =
+
+-if CONFIG_RQUOTAD
+-OPTDIRS += rquotad
+-endif
+-
+-if CONFIG_NFSV4
+-OPTDIRS += idmapd
+-endif
+-
+-if CONFIG_GSS
+-OPTDIRS += gssd
+-endif
+-
+-if CONFIG_MOUNT
+-OPTDIRS += mount
+-endif
+-
+-SUBDIRS = \
+- exportfs \
+- lockd \
+- mountd \
+- nfsd \
+- nfsstat \
+- showmount \
+- statd \
+- $(OPTDIRS)
++SUBDIRS = gssd
+
+ MAINTAINERCLEANFILES = Makefile.in