Added support for library initialization and finalization, and verification
authorKen Raeburn <raeburn@mit.edu>
Sat, 24 Apr 2004 21:09:44 +0000 (21:09 +0000)
committerKen Raeburn <raeburn@mit.edu>
Sat, 24 Apr 2004 21:09:44 +0000 (21:09 +0000)
that the initializer completed successfully.  Delay initialization on POSIX
until the first "verification" call.  Currently specific to a few platforms,
but should still build on others without thread support enabled.

Use it to finish creating (if necessary) and destroy mutexes, and free some
other storage "permanently" allocated by libraries (currently, libkrb5
cache/keytab type registries only).  Change initialization of static mutexes to
a two-step operation, a static "partial" initializer and a "finish_init"
routine called from a thread-safe environment like library initialization is
assumed to be.  POSIX will use the former, Windows will use the latter, and the
debug support will check that *both* have been used.

Added init/fini functions to com_err, profile, krb5, and gssapi libraries.
(The profile library one may need to be removed later.)  The existing ones, not
thread-safe, are still around.

Use weak symbol support if available to figure out if the pthread library has
been linked in, and avoid calling certain routines if the C library stubs are
known not to exist or work.

Stub declarations for thread-specific data.
Minor bugfixes, whitespace changes.

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@16268 dc483132-0cff-0310-8789-dd5450dbe970

29 files changed:
src/ChangeLog
src/aclocal.m4
src/include/ChangeLog
src/include/Makefile.in
src/include/k5-platform.h
src/include/k5-thread.h
src/lib/gssapi/ChangeLog
src/lib/gssapi/generic/ChangeLog
src/lib/gssapi/generic/gssapiP_generic.h
src/lib/gssapi/gss_libinit.c
src/lib/gssapi/krb5/ChangeLog
src/lib/gssapi/krb5/krb5_gss_glue.c
src/lib/krb5/ChangeLog
src/lib/krb5/ccache/ChangeLog
src/lib/krb5/ccache/ccbase.c
src/lib/krb5/keytab/ChangeLog
src/lib/krb5/keytab/ktbase.c
src/lib/krb5/krb/ChangeLog
src/lib/krb5/krb/init_ctx.c
src/lib/krb5/krb5_libinit.c
src/lib/krb5/rcache/ChangeLog
src/lib/krb5/rcache/rc_base.c
src/util/et/ChangeLog
src/util/et/Makefile.in
src/util/et/error_message.c
src/util/et/error_table.h
src/util/profile/ChangeLog
src/util/profile/prof_file.c
src/util/profile/prof_int.h

index 8b7e7aa4ae8107e119944e076618ce18cdae76d0..4667b492b976933b17fca2f58a987fc8a616fbc5 100644 (file)
@@ -1,6 +1,16 @@
 2004-04-24  Ken Raeburn  <raeburn@mit.edu>
 
        * aclocal.m4 (KRB5_BUILD_LIBRARY_WITH_DEPS): Look for perl.
+       (KRB5_AC_PRAGMA_WEAK_REF): New macro, tests support for weak
+       references using "#pragma weak".
+       (CONFIG_RULES): Invoke it.
+       (KRB5_AC_ENABLE_THREADS): Test enableval, not withval.  If
+       ACX_PTHREAD can't determine thread support options and thread
+       support was requested, report an error.  Display the options
+       selected by ACX_PTHREAD.  Test for pthread_once without pthread
+       options, and pthread_mutexattr_setrobust_np both with and without
+       pthread options.
+       (KRB5_AC_GCC_ATTRS): New macro, dummy for now.
 
 2004-04-22  Ken Raeburn  <raeburn@mit.edu>
 
index cf1fd80026780eccbf4d04dcb4bfc37fe02b00aa..6dd69452ed6f0e71d5f30a363db3b1e8648f6c5a 100644 (file)
@@ -100,7 +100,8 @@ AC_SUBST_FILE(lib_frag)
 libobj_frag=$srcdir/$ac_config_fragdir/libobj.in
 AC_SUBST_FILE(libobj_frag)
 dnl
-KRB5_AC_ENABLE_THREADS dnl
+KRB5_AC_PRAGMA_WEAK_REF
+KRB5_AC_ENABLE_THREADS
 ])dnl
 
 dnl Maintainer mode, akin to what automake provides, 'cept we don't
@@ -128,16 +129,38 @@ dnl Hack for now.
 AC_DEFUN([KRB5_AC_ENABLE_THREADS],[
 AC_ARG_ENABLE([thread-support],
 AC_HELP_STRING([--enable-thread-support],use PRELIMINARY EXPERIMENTAL UNFINISHED POSIX-only thread support @<:@disabled@:>@),
-[  if test "$withval" = yes ; then
+[  if test "$enableval" = yes ; then
     AC_MSG_NOTICE(enabling PRELIMINARY EXPERIMENTAL UNFINISHED POSIX-only thread support)
     AC_DEFINE(ENABLE_THREADS,1,[Define if thread support enabled])
   fi
 ])
 dnl Maybe this should be inside the conditional above?  Doesn't cache....
-ACX_PTHREAD
+if test "$enable_thread_support" = yes; then
+ACX_PTHREAD(,[AC_MSG_ERROR([cannot determine options for enabling thread support])])
+AC_MSG_NOTICE(PTHREAD_CC = $PTHREAD_CC)
+AC_MSG_NOTICE(PTHREAD_CFLAGS = $PTHREAD_CFLAGS)
+AC_MSG_NOTICE(PTHREAD_LIBS = $PTHREAD_LIBS)
 dnl Not really needed -- if pthread.h isn't found, ACX_PTHREAD will fail.
 AC_CHECK_HEADERS(pthread.h)
+fi
+dnl We want to know where these routines live, so on systems with weak
+dnl reference support we can figure out whether or not the pthread library
+dnl has been linked in.
+dnl If we don't add any libraries for thread support, don't bother.
+AC_CHECK_FUNCS(pthread_once pthread_mutexattr_setrobust_np)
+old_CC="$CC"
+test "$PTHREAD_CC" != "" && CC=$PTHREAD_CC
+old_CFLAGS="$CFLAGS"
+CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+old_LIBS="$LIBS"
+LIBS="$PTHREAD_LIBS $LIBS"
+AC_MSG_NOTICE(rechecking with PTHREAD_... options)
+AC_CHECK_LIB(c, pthread_mutexattr_setrobust_np,
+  [AC_DEFINE(HAVE_PTHREAD_MUTEXATTR_SETROBUST_NP_IN_THREAD_LIB,1,[Define if pthread_mutexattr_setrobust_np is provided in the thread library.])])
 ])
+LIBS="$old_LIBS"
+CC="$old_CC"
+fi
 
 dnl This is somewhat gross and should go away when the build system
 dnl is revamped. -- tlyu
@@ -1042,7 +1065,7 @@ AC_REQUIRE([AC_PROG_ARCHIVE])dnl
 AC_REQUIRE([AC_PROG_ARCHIVE_ADD])dnl
 AC_REQUIRE([AC_PROG_INSTALL])dnl
 AC_CHECK_PROG(AR, ar, ar, false)
-AC_CHECK_PROG(PERL, perl, false)
+AC_CHECK_PROG(PERL, perl, perl, false)
 AC_SUBST(LIBLIST)
 AC_SUBST(LIBLINKS)
 AC_SUBST(MAKE_SHLIB_COMMAND)
@@ -1523,4 +1546,22 @@ esac
 AC_SUBST(PRIOCNTL_HACK)])
 dnl
 dnl
+dnl KRB5_AC_GCC_ATTRS
+AC_DEFUN([KRB5_AC_GCC_ATTRS],
+[
+])
+dnl
+dnl
+dnl KRB5_AC_PRAGMA_WEAK_REF
+AC_DEFUN([KRB5_AC_PRAGMA_WEAK_REF],
+[AC_CACHE_CHECK([whether pragma weak references are supported],
+krb5_cv_pragma_weak_ref,
+[AC_TRY_LINK([#pragma weak flurbl
+extern int flurbl(void);],[if (&flurbl != 0) return 1;],
+krb5_cv_pragma_weak_ref=yes,krb5_cv_pragma_weak_ref=no)])
+if test $krb5_cv_pragma_weak_ref = yes ; then
+  AC_DEFINE(HAVE_PRAGMA_WEAK_REF,1,[Define if #pragma weak references work])
+fi])
+dnl
+dnl
 m4_include(config/ac-archive/acx_pthread.m4)
index cb87feb7a6bf017b24876c78bfcf9d5b7fc050e7..279990cb30241f142dfa7c42805e7135cb1350c7 100644 (file)
@@ -1,3 +1,46 @@
+2004-04-24  Ken Raeburn  <raeburn@mit.edu>
+
+       * k5-platform.h (DELAY_INITIALIZER): New macro, defined always.
+       (CONSTRUCTOR_ATTR_WORKS, DESTRUCTOR_ATTR_WORKS): New macro,
+       defined for Linux and NetBSD, and Solaris with gcc.
+       (USE_LINKER_FINI_OPTION): New macro, defined on IRIX, and on
+       Solaris with native compiler.
+       (JOIN2, JOIN2_2, JOIN3, JOIN3_2, JOIN4, JOIN4_2): New utility
+       macros.
+       (k5_init_t): New typedef, for some platforms.
+       (MAKE_INIT_FUNCTION, MAKE_FINI_FUNCTION, CALL_INIT_FUNCTION,
+       INITIALIZER_RAN, PROGRAM_EXITING): New macros for library
+       initialization and finalization support.
+
+       * k5-thread.h: Use k5_mutex_init instead of K5_MUTEX_INITIALIZER
+       for multiple-inclusion protection.
+       (K5_MUTEX_DEBUG_INITIALIZER): Change initial initialized flag to
+       2.
+       (k5_mutex_debug_finish_init): New macro, verifies value 2 and
+       changes it to 1.
+       (k5_mutex_debug_lock): Test various values of initialized and
+       locked flags separately, so assertion failure message is more
+       immediately informative.
+       (K5_MUTEX_PARTIAL_INITIALIZER): Rename from K5_MUTEX_INITIALIZER.
+       (k5_mutex_finish_init): New macro.
+       (pthread_once, pthread_mutexattr_setrobust_np): Consider
+       declaring weak based on availability.
+       (K5_PTHREADS_LOADED): New macro, tests to see if pthread
+       functions are available, if weak references are supported.
+       (k5_mutex_lock, k5_mutex_unlock): On IRIX, redefine to bypass
+       pthread code if pthread library isn't loaded.
+       (k5_once_t): New typedef.
+       (K5_ONCE_INIT, k5_once): New macros.
+
+       * Makefile.in (autoconf.stmp): Depend on aclocal.m4.
+
+       * k5-platform.h: Include limits.h before testing for SIZE_MAX.
+
+       * k5-thread.h (k5_key_t): New enumerator typedef.
+       (k5_key_register, k5_getspecific, k5_setspecific): New macros.
+       (krb5int_key_register, krb5int_getspecific, krb5int_setspecific,
+       krb5int_key_delete): Declare.
+
 2004-04-16  Sam Hartman  <hartmans@mit.edu>
 
        * k5-int.h: krb5int_populate_gic_opt now takes credentials  so it
index a54505982d10a82cfcf57f56da6d2f50f3896b9c..346b4a15535b63ba9a8094ea91095e21138fb4b3 100644 (file)
@@ -27,7 +27,7 @@ maybe-make-db.h-redirect:
 krb5/autoconf.h: $(srcdir)/krb5/autoconf.h.in
        (cd krb5; $(MAKE) autoconf.h)
 $(srcdir)/krb5/autoconf.h.in: @MAINT@ $(srcdir)/krb5/autoconf.stmp
-$(srcdir)/krb5/autoconf.stmp: $(srcdir)/configure.in 
+$(srcdir)/krb5/autoconf.stmp: $(srcdir)/configure.in $(SRCTOP)/aclocal.m4
        cd $(srcdir) && ($(AUTOHEADER) --include=$(CONFIG_RELTOPDIR) $(AUTOHEADERFLAGS) || $(AUTOHEADER) --localdir=$(CONFIG_RELTOPDIR) $(AUTOHEADERFLAGS))
        touch $(srcdir)/krb5/autoconf.stmp
 
index c4cc7bb7562581da76bc96a54c8cd1e621a76c58..22b38f4986f642e0d8ae5fe50ec7212074c86f66 100644 (file)
  * Some platform-dependent definitions to sync up the C support level.
  * Some to a C99-ish level, some related utility code.
  *
- * Currently: make "static inline" work; 64-bit types and load/store
- * code; SIZE_MAX.
+ * Currently:
+ * + make "static inline" work
+ * + 64-bit types and load/store code
+ * + SIZE_MAX
+ * + shared library init/fini hooks
  */
 
 #ifndef K5_PLATFORM_H
 
 #include "autoconf.h"
 
+
+/* Initialization and finalization function support for libraries.
+
+   At top level, before the functions are defined or even declared:
+   MAKE_INIT_FUNCTION(init_fn);
+   MAKE_FINI_FUNCTION(fini_fn);
+   int init_fn(void) { ... }
+   void fini_fn(void) { if (INITIALIZER_RAN(init_fn)) ... }
+
+   In code, in the same file:
+   err = CALL_INIT_FUNCTION(init_fn);
+
+   To trigger or verify the initializer invocation from another file,
+   an additional function must be created.
+
+   The init_fn and fini_fn names should be chosen such that any
+   exported names staring with those names, and optionally followed by
+   additional characters, fits in with any namespace constraints on
+   the library in question.
+
+
+   Implementation outline:
+
+   Windows: MAKE_FINI_FUNCTION creates a symbol with a magic name that
+   is sought at library build time, and code is added to invoke the
+   function when the library is unloaded.  MAKE_INIT_FUNCTION does
+   likewise, but the function is invoked when the library is loaded,
+   and an extra variable is declared to hold an error code and a "yes
+   the initializer ran" flag.  CALL_INIT_FUNCTION blows up if the flag
+   isn't set, otherwise returns the error code.
+
+   UNIX: MAKE_INIT_FUNCTION creates and initializes a variable with a
+   name derived from the function name, containing a k5_once_t
+   (pthread_once_t or int), an error code, and a pointer to the
+   function.  The function itself is declared static, but the
+   associated variable has external linkage.  CALL_INIT_FUNCTION
+   ensures thath the function is called exactly once (pthread_once or
+   just check the flag) and returns the stored error code (or the
+   pthread_once error).
+
+   UNIX, with compiler support: MAKE_FINI_FUNCTION declares the
+   function as a destructor, and the run time linker support or
+   whatever will cause it to be invoked when the library is unloaded,
+   the program ends, etc.
+
+   UNIX, with linker support: MAKE_FINI_FUNCTION creates a symbol with
+   a magic name that is sought at library build time, and linker
+   options are used to mark it as a finalization function for the
+   library.  The symbol must be exported.
+
+   UNIX, no library finalization support: The finalization function
+   never runs, and we leak memory.  Tough.
+
+
+
+   For maximum flexibility in defining the macros, the function name
+   parameter should be a simple name, not even a macro defined as
+   another name.  The function should have a unique name, and should
+   conform to whatever namespace is used by the library in question.
+
+   If the macro expansion needs the function to have been declared, it
+   must include a declaration.  If it is not necessary for the symbol
+   name to be exported from the object file, the macro should declare
+   it as "static".  Hence the signature must exactly match "void
+   foo(void)".  (ANSI C allows a static declaration followed by a
+   non-static one; the result is internal linkage.)  The macro
+   expansion has to come before the function, because gcc apparently
+   won't act on "__attribute__((constructor))" if it comes after the
+   function definition.
+
+   This is going to be compiler- and environment-specific, and may
+   require some support at library build time, and/or "asm"
+   statements.
+
+   It's okay for this code to require that the library be built
+   with the same compiler and compiler options throughout, but
+   we shouldn't require that the library and application use the
+   same compiler.
+
+   For static libraries, we don't really care about cleanup too much,
+   since it's all memory handling and mutex allocation which will all
+   be cleaned up when the program exits.  Thus, it's okay if gcc-built
+   static libraries don't play nicely with cc-built executables when
+   it comes to static constructors, just as long as it doesn't cause
+   linking to fail.
+
+   For dynamic libraries on UNIX, we'll use pthread_once-type support
+   to do delayed initialization, so if finalization can't be made to
+   work, we'll only have memory leaks in a load/use/unload cycle.  If
+   anyone (like, say, the OS vendor) complains about this, they can
+   tell us how to get a shared library finalization function invoked
+   automatically.  */
+
+#if !defined(_WIN32xxx)
+# define DELAY_INITIALIZER
+#endif
+
+/* These should be turned into feature tests or otherwise driven from
+   the configure script.  */
+#if defined(__linux__) || defined(__NetBSD__) \
+       || (defined(__sun__) && defined(__svr4__) && defined(__GNUC__))
+# define CONSTRUCTOR_ATTR_WORKS
+# define DESTRUCTOR_ATTR_WORKS
+#endif
+#if defined(__sgi) && defined(__mips) && defined(_SYSTYPE_SVR4) /* IRIX? */
+# define USE_LINKER_FINI_OPTION
+#endif
+#if !defined(__GNUC__) && defined(__sun) && defined(__SVR4) && defined(__SUNPRO_C) /* Solaris ld -z finiarray */
+# define USE_LINKER_FINI_OPTION
+#endif
+
+# define JOIN4_2(A,B,C,D) A ## B ## C ## D
+# define JOIN4(A,B,C,D) JOIN4_2(A,B,C,D)
+# define JOIN3_2(A,B,C) A ## B ## C
+# define JOIN3(A,B,C) JOIN3_2(A,B,C)
+# define JOIN2_2(A,B) A ## B
+# define JOIN2(A,B) JOIN2_2(A,B)
+
+/* XXX Should test USE_LINKER_INIT_OPTION early, and if it's set,
+   always provide a function by the expected name, even if we're
+   delaying initialization.  */
+
+#if defined(DELAY_INITIALIZER)
+
+/* Run the initialization code during program execution, at the latest
+   possible moment.  This means multiple threads may be active.  */
+# include "k5-thread.h"
+typedef struct { k5_once_t once; int error, did_run; void (*fn)(void); } k5_init_t;
+# define MAKE_INIT_FUNCTION(NAME)                              \
+       static int NAME(void);                                  \
+       /* forward declaration for use in initializer */        \
+       static void JOIN2(NAME, __aux) (void);                  \
+       static k5_init_t JOIN2(NAME, __once) =                  \
+               { K5_ONCE_INIT, 0, 0, JOIN2(NAME, __aux) };     \
+       static void JOIN2(NAME, __aux) (void)                   \
+       {                                                       \
+           JOIN2(NAME, __once).did_run = 1;                    \
+           JOIN2(NAME, __once).error = NAME();                 \
+       }                                                       \
+       /* so ';' following macro use won't get error */        \
+       static int NAME(void)
+# define CALL_INIT_FUNCTION(NAME)      \
+       k5_call_init_function(& JOIN2(NAME, __once))
+static inline int k5_call_init_function(k5_init_t *i)
+{
+    int err;
+    err = k5_once(&i->once, i->fn);
+    if (err)
+       return err;
+    assert (i->did_run != 0);
+    return i->error;
+}
+/* This should be called in finalization only, so we shouldn't have
+   multiple active threads mucking around in our library at this
+   point.  So ignore the once_t object and just look at the flag.
+
+   XXX Could we have problems with memory coherence between
+   processors if we don't invoke mutex/once routines?  */
+# define INITIALIZER_RAN(NAME) \
+       (JOIN2(NAME, __once).did_run && JOIN2(NAME, __once).error == 0)
+
+# define PROGRAM_EXITING()             (0)
+
+#elif defined(__GNUC__) && !defined(_WIN32) && defined(CONSTRUCTOR_ATTR_WORKS)
+
+/* Run initializer at load time, via GCC/C++ hook magic.  */
+typedef struct { int error; unsigned char did_run; } k5_init_t;
+# define MAKE_INIT_FUNCTION(NAME)              \
+       static k5_init_t JOIN2(NAME, __ran)     \
+               = { 0, 2 };                     \
+       static void JOIN2(NAME, __aux)(void)    \
+           __attribute__((constructor));       \
+       static int NAME(void);                  \
+       static void JOIN2(NAME, __aux)(void)    \
+       {                                       \
+           JOIN2(NAME, __ran).error = NAME();  \
+           JOIN2(NAME, __ran).did_run = 3;     \
+       }                                       \
+       static int NAME(void)
+# define CALL_INIT_FUNCTION(NAME)              \
+       (JOIN2(NAME, __ran).did_run == 3        \
+        ? JOIN2(NAME, __ran).error             \
+        : (abort(),0))
+# define INITIALIZER_RAN(NAME) (JOIN2(NAME, __ran).error == 0)
+
+#elif defined(LINKER_HAS_INIT_OPTION)
+
+/* Run initializer at load time, via linker magic.  */
+typedef struct { int error; unsigned char did_run; } k5_init_t;
+# define MAKE_INIT_FUNCTION(NAME)              \
+       static k5_init_t JOIN2(NAME, __ran)     \
+               = { 0, 2 };                     \
+       static int NAME(void);                  \
+       void JOIN2(NAME, __aux)                 \
+       {                                       \
+           JOIN2(NAME, __ran).error = NAME();  \
+           JOIN2(NAME, __ran).did_run = 3;     \
+       }                                       \
+       static int NAME(void)
+# define CALL_INIT_FUNCTION(NAME)              \
+       (JOIN2(NAME, __ran).did_run == 3        \
+        ? JOIN2(NAME, __ran).error             \
+        : (abort(),0))
+# define INITIALIZER_RAN(NAME) \
+       (JOIN2(NAME, __ran).error == 0)
+
+# define PROGRAM_EXITING()             (0)
+
+#else
+
+# error "Don't know how to do load-time initializers for this configuration."
+
+# define PROGRAM_EXITING()             (0)
+
+#endif
+
+
+
+#ifdef USE_LINKER_FINI_OPTION
+/* If we're told the linker option will be used, it doesn't really
+   matter what compiler we're using.  Do it the same way
+   regardless.  */
+
+# define MAKE_FINI_FUNCTION(NAME)      \
+       void NAME(void)
+
+#elif defined(__GNUC__) && !defined(_WIN32) && defined(DESTRUCTOR_ATTR_WORKS)
+/* If we're using gcc, if the C++ support works, the compiler should
+   build executables and shared libraries that support the use of
+   static constructors and destructors.  The C compiler supports a
+   function attribute that makes use of the same facility as C++.
+
+   XXX How do we know if the C++ support actually works?  */
+# define MAKE_FINI_FUNCTION(NAME)      \
+       static void NAME(void) __attribute__((destructor))
+
+#else
+
+# error "Don't know how to do unload-time finalization for this configuration."
+
+#endif
+
+
 /* 64-bit support: krb5_ui_8 and krb5_int64.
 
    This should move to krb5.h eventually, but without the namespace
 # define UINT64_TYPE unsigned long long
 #endif
 
+#include <limits.h>
 #ifndef SIZE_MAX
 # define SIZE_MAX ((size_t)((size_t)0 - 1))
 #endif
index 27d705f79b38209299482361da2134fa177ef2b0..d596e482728e1cab80e70b07a6250ea1c09e9ab3 100644 (file)
  * Preliminary thread support.
  */
 
-#ifndef K5_MUTEX_INITIALIZER /* handle multiple inclusion */
+#ifndef k5_mutex_init /* handle multiple inclusion */
 
 #include "autoconf.h"
 
 /* Interface (tentative):
 
-   k5_mutex_t foo_mutex = K5_MUTEX_INITIALIZER;
+   Mutex support:
+
+   // Between these two, we should be able to do pure compile-time
+   // and pure run-time initialization.
+   //   POSIX:   partial initializer is PTHREAD_MUTEX_INITIALIZER,
+   //            finish does nothing
+   //   Windows: partial initializer is an invalid handle,
+   //            finish does the real initialization work
+   //   debug:   partial initializer sets one magic value,
+   //            finish verifies and sets a new magic value for
+   //              lock/unlock to check
+   k5_mutex_t foo_mutex = K5_MUTEX_PARTIAL_INITIALIZER;
+   int k5_mutex_finish_init(k5_mutex_t *);
+   // for dynamic allocation
    int k5_mutex_init(k5_mutex_t *);
+   // Must work for both kinds of alloc, even if it means adding flags.
    int k5_mutex_destroy(k5_mutex_t *);
+
+   // As before.
    int k5_mutex_lock(k5_mutex_t *);
    int k5_mutex_unlock(k5_mutex_t *);
 
-   k5_key_t key;
-   int k5_key_create(k5_key_t *, void (*destructor)(void *));
+
+   In each library, one new function to finish the static mutex init,
+   and any other library-wide initialization that might be desired.
+   On POSIX, this function would be called via the second support
+   function (see below).  On Windows, it would be called at library
+   load time.  These functions, or functions they calls, should be the
+   only places that k5_mutex_finish_init gets called.
+
+   A second function or macro called at various possible "first" entry
+   points which either calls pthread_once on the first function
+   (POSIX), or checks some flag set by the first function (Windows,
+   debug support), and possibly returns an error.  (In the
+   non-threaded case, a simple flag can be used to avoid multiple
+   invocations, and the mutexes don't need run-time initialization
+   anyways.)
+
+   A third function for library termination calls mutex_destroy on
+   each mutex for the library.  This function would be called
+   automatically at library unload time.  If it turns out to be needed
+   at exit time for libraries that don't get unloaded, perhaps we
+   should also use atexit().  Any static mutexes should be cleaned up
+   with k5_mutex_destroy here.
+
+
+   How does that second support function invoke the first support
+   function only once?  Through something modelled on pthread_once
+   that I haven't written up yet.  Probably:
+
+   k5_once_t foo_once = K5_ONCE_INIT;
+   k5_once(k5_once_t *, void (*)(void));
+
+   For POSIX: Map onto pthread_once facility.
+   For non-threaded case: A simple flag.
+   For Windows: Not needed; library init code takes care of it.
+
+
+   Thread-specific data:
+
+   // TSD keys are limited in number in gssapi/krb5/com_err; enumerate
+   // them all.  This allows support code init to allocate the
+   // necessary storage for pointers all at once, and avoids any
+   // possible error in key creation.
+   enum { ... } k5_key_t;
+   // Register destructor function.  Called in library init code.
+   int k5_key_register(k5_key_t, void (*destructor)(void *));
+   // Returns NULL or data.
    void *k5_getspecific(k5_key_t);
-   int k5_setspecific(k5_key_t, const void *);
-   ... stuff to signal library termination ...
+   // Returns error if key out of bounds, or the pointer table can't
+   // be allocated.  A call to k5_key_register must have happened first.
+   // This may trigger the calling of pthread_setspecific on POSIX.
+   int k5_setspecific(k5_key_t, void *);
+   // Called in library termination code.
+   // Trashes data in all threads, calling the registered destructor
+   // (but calling it from the current thread).
+   int k5_key_delete(k5_key_t);
+
+   For the non-threaded version, the support code will have a static
+   array indexed by k5_key_t values, and get/setspecific simply access
+   the array elements.
+
+   The TSD destructor table is global state, protected by a mutex if
+   threads are enabled.
+
+   Debug support: Not much.  Might check if k5_key_register has been
+   called and abort if not.
+
+
+   Any actual external symbols will use the krb5int_ prefix.  The k5_
+   names will be simple macros or inline functions to rename the
+   external symbols, or slightly more complex ones to expand the
+   implementation inline (e.g., map to POSIX versions and/or debug
+   code using __FILE__ and the like).
 
-   More to be added, probably.  */
+
+   More to be added, perhaps.  */
 
 #ifndef HAVE_PTHREAD_H
 # undef ENABLE_THREADS
@@ -64,9 +148,11 @@ typedef struct {
     short lineno;
     const char *filename;
 } k5_mutex_debug_info;
-#define K5_MUTEX_DEBUG_INITIALIZER     { 1, K5_MUTEX_DEBUG_UNLOCKED, 0, 0 }
+#define K5_MUTEX_DEBUG_INITIALIZER     { 2, K5_MUTEX_DEBUG_UNLOCKED, 0, 0 }
 #define K5_MUTEX_DEBUG_LOCKED          4
 #define K5_MUTEX_DEBUG_UNLOCKED                3
+#define k5_mutex_debug_finish_init(M)          \
+       (assert((M)->initialized == 2), (M)->initialized = 1, 0)
 #define k5_mutex_debug_init(M)                 \
        ((M)->initialized = 1,                  \
         (M)->locked = K5_MUTEX_DEBUG_UNLOCKED, \
@@ -76,8 +162,12 @@ typedef struct {
                && (M)->locked == K5_MUTEX_DEBUG_UNLOCKED),     \
         (M)->initialized = 0)
 #define k5_mutex_debug_lock(M)                                 \
-       (assert((M)->initialized == 1                           \
-               && (M)->locked == K5_MUTEX_DEBUG_UNLOCKED),     \
+       (assert((M)->initialized != 2),                         \
+        assert((M)->initialized != 0),                         \
+        assert((M)->initialized == 1),                         \
+        assert((M)->locked != 0),                              \
+        assert((M)->locked != K5_MUTEX_DEBUG_LOCKED),          \
+        assert((M)->locked == K5_MUTEX_DEBUG_UNLOCKED),        \
         (M)->locked = K5_MUTEX_DEBUG_LOCKED,                   \
         (M)->lineno = __LINE__, (M)->filename = __FILE__, 0)
 #define k5_mutex_debug_unlock(M)                               \
@@ -86,6 +176,13 @@ typedef struct {
         (M)->locked = K5_MUTEX_DEBUG_UNLOCKED,                 \
         (M)->lineno = __LINE__, (M)->filename = __FILE__, 0)
 
+
+typedef enum {
+    K5_KEY_COM_ERR,
+    K5_KEY_MAX
+} k5_key_t;
+
+
 #ifdef ENABLE_THREADS
 
 #include <pthread.h>
@@ -98,8 +195,9 @@ typedef struct {
 #ifndef DEBUG_THREADS
 
 typedef pthread_mutex_t k5_mutex_t;
-#define K5_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
+#define K5_MUTEX_PARTIAL_INITIALIZER PTHREAD_MUTEX_INITIALIZER
 
+#define k5_mutex_finish_init(M)        ((void)(M),0)
 #define k5_mutex_init(M)       pthread_mutex_init(M, 0)
 #define k5_mutex_destroy(M)    pthread_mutex_destroy(M)
 #define k5_mutex_lock(M)       pthread_mutex_lock(M)
@@ -111,12 +209,14 @@ typedef struct {
     k5_mutex_debug_info debug;
     pthread_mutex_t lock;
 } k5_mutex_t;
-#define K5_MUTEX_INITIALIZER   { K5_MUTEX_DEBUG_INITIALIZER, PTHREAD_MUTEX_INITIALIZER }
+#define K5_MUTEX_PARTIAL_INITIALIZER   \
+               { K5_MUTEX_DEBUG_INITIALIZER, PTHREAD_MUTEX_INITIALIZER }
+#define k5_mutex_finish_init(M)        (k5_mutex_debug_finish_init(&(M)->debug))
 #define k5_mutex_init(M)       (k5_mutex_debug_init(&(M)->debug),            \
                                 assert(0==pthread_mutex_init(&(M)->lock,0)), \
                                 0)
 #define k5_mutex_destroy(M)    (k5_mutex_debug_init(&(M)->debug),            \
-                                assert(0==pthread_mutex_destroy(&(M)->lock))
+                                assert(0==pthread_mutex_destroy(&(M)->lock)))
 #define k5_mutex_lock(M)       (k5_mutex_debug_lock(&(M)->debug),          \
                                 assert(0==pthread_mutex_lock(&(M)->lock)), \
                                 0)
@@ -124,19 +224,69 @@ typedef struct {
                                 assert(0==pthread_mutex_unlock(&(M)->lock)), \
                                 0)
 
+#if defined(__mips) && defined(__sgi) && defined(_SYSTYPE_SVR4)
+/* IRIX 6.5 stub pthread support in libc is really annoying.
+   The pthread_mutex_lock function returns ENOSYS for a program
+   not linked against -lpthread.  No link-time failure, no weak
+   symbols, etc.
+
+   The C library doesn't provide pthread_once; we can use weak
+   reference support for that.  */
+#undef k5_mutex_lock
+#undef k5_mutex_unlock
+#define k5_mutex_lock(M)                       \
+       (k5_mutex_debug_lock(&(M)->debug),      \
+        (K5_PTHREADS_LOADED                    \
+         ? pthread_mutex_lock(&(M)->lock)      \
+         : 0))
+#define k5_mutex_unlock(M)                     \
+       (k5_mutex_debug_unlock(&(M)->debug),    \
+        (K5_PTHREADS_LOADED                    \
+         ? pthread_mutex_unlock(&(M)->lock)    \
+         : 0))
+#endif
+
 #endif /* DEBUG_THREADS ? */
 
-#if 0
-/* *** This will need to change.
-   We'd prefer to use only one POSIX data key.
-
-   And we need to do some additional bookkeeping for dealing with
-   unloading libraries (free storage, destroy the key), such that we
-   can't just map the functions to POSIX in the long term.  */
-typedef pthread_key_t k5_key_t;
-#define k5_key_create(K,D)     pthread_key_create(K,D)
-#define k5_getspecific(K)      pthread_getspecific(K)
-#define k5_setspecific(K,P)    pthread_setspecific(K,P)
+/* Linux with weak reference support:
+   Stub mutex routines exist, but pthread_once does not.
+
+   Solaris: In libc there's a pthread_once that doesn't seem
+   to do anything.  Bleah.  But pthread_mutexattr_setrobust_np
+   is defined only in libpthread.
+ */
+
+#ifdef HAVE_PRAGMA_WEAK_REF
+# pragma weak pthread_once
+# ifdef HAVE_PTHREAD_MUTEXATTR_SETROBUST_NP_IN_THREAD_LIB
+#  pragma weak pthread_mutexattr_setrobust_np
+# endif
+# if !defined HAVE_PTHREAD_ONCE
+#  define K5_PTHREADS_LOADED   (&pthread_once != 0)
+# elif !defined HAVE_PTHREAD_MUTEXATTR_SETROBUST_NP \
+       && defined HAVE_PTHREAD_MUTEXATTR_SETROBUST_NP_IN_THREAD_LIB
+#  define K5_PTHREADS_LOADED   (&pthread_mutexattr_setrobust_np != 0)
+# else
+#  define K5_PTHREADS_LOADED   (1)
+# endif
+#else
+/* no pragma weak support */
+# define K5_PTHREADS_LOADED    (1)
+#endif
+
+/* Would be nice to use a union, but we need to initialize both.  */
+#ifdef HAVE_PRAGMA_WEAK_REF
+typedef struct { pthread_once_t o; int i; } k5_once_t;
+#define K5_ONCE_INIT   { PTHREAD_ONCE_INIT, 2 }
+#define k5_once(O,F)   (K5_PTHREADS_LOADED             \
+                        ? pthread_once(&(O)->o,F)      \
+                        : (O)->i == 2                  \
+                        ? ((O)->i = 3, (*F)(), 0)      \
+                        : 0)
+#else
+typedef pthread_once_t k5_once_t;
+#define K5_ONCE_INIT   PTHREAD_ONCE_INIT
+#define k5_once                pthread_once
 #endif
 
 #else /* ! ENABLE_THREADS */
@@ -149,25 +299,48 @@ typedef pthread_key_t k5_key_t;
    we're pairing up lock and unlock calls properly.  */
 
 #define k5_mutex_t             k5_mutex_debug_info
-#define K5_MUTEX_INITIALIZER   K5_MUTEX_DEBUG_INITIALIZER
+#define K5_MUTEX_PARTIAL_INITIALIZER   K5_MUTEX_DEBUG_INITIALIZER
+#define k5_mutex_finish_init   k5_mutex_debug_finish_init
 #define k5_mutex_init          k5_mutex_debug_init
 #define k5_mutex_destroy       k5_mutex_debug_destroy
 #define k5_mutex_lock          k5_mutex_debug_lock
 #define k5_mutex_unlock                k5_mutex_debug_unlock
 
+#define k5_once_t      unsigned char
+#define K5_ONCE_INIT   2
+#define k5_once(O,F)                                   \
+       (assert(*(O) == 2 || *(O) == 3),                \
+        (*(O) == 3 ? 0 : ((F)(), *(O) = 3, 0)))
+
 #else /* ! DEBUG_THREADS */
 
 /* no-op versions */
 
 typedef char k5_mutex_t;
-#define K5_MUTEX_INITIALIZER   0
+#define K5_MUTEX_PARTIAL_INITIALIZER   0
+#define k5_mutex_finish_init(M)        (0)
 #define k5_mutex_init(M)       (*(M) = 0, *(M) = *(M))
 #define k5_mutex_destroy(M)    (0)
 #define k5_mutex_lock(M)       (0)
 #define k5_mutex_unlock(M)     (0)
 
+#define k5_once_t      unsigned char
+#define K5_ONCE_INIT   2
+#define k5_once(F,O)   \
+       (*(O) == 3 ? 0 : ((F)(), *(O) = 3, 0))
+
 #endif /* DEBUG_THREADS ? */
 
 #endif /* ENABLE_THREADS ? */
 
-#endif /* K5_MUTEX_INITIALIZER for multiple inclusion */
+/* rename shorthand symbols for export */
+#define k5_key_register        krb5int_key_register
+#define k5_getspecific krb5int_getspecific
+#define k5_setspecific krb5int_setspecific
+#define k5_key_delete  krb5int_key_delete
+extern int k5_key_register(k5_key_t, void (*)(void *));
+extern void *k5_getspecific(k5_key_t);
+extern int k5_setspecific(k5_key_t, void *);
+extern int k5_key_delete(k5_key_t);
+
+#endif /* multiple inclusion? */
index e335415746d182566a296b8e1b63ce4f0d3c047b..0c2d0ec1630ba6e37047e0bdcc42381383d7a48e 100644 (file)
@@ -1,3 +1,11 @@
+2004-04-24  Ken Raeburn  <raeburn@mit.edu>
+
+       * gss_libinit.c: Include k5-platform.h.
+       (gssint_lib_init, gssint_lib_fini): New init/fini functions.
+       Create and clean up the mutex in kg_vdb.
+       (gssint_initialize_library): Verify the library initializer has
+       run successfully.
+
 2004-04-22  Ken Raeburn  <raeburn@mit.edu>
 
        * libgssapi_krb5.exports: New file.
index 563eb2f061d682416b7f58e268703275fb12cbc8..a9f93eb14bc1b2067f5b54c769d60deda89d57f9 100644 (file)
@@ -1,3 +1,8 @@
+2004-04-24  Ken Raeburn  <raeburn@mit.edu>
+
+       * gssapiP_generic.h (G_SET_INIT): Use the new mutex partial
+       initializer now.
+
 2004-03-14  Ken Raeburn  <raeburn@mit.edu>
 
        * gssapiP_generic.h (struct _g_set_elt, g_set_elt): Renamed from
index 30afc5264341736524a9a1621fdab46d2011a84d..e297862fe90301d4b891f95dfc8e2ca44040cb82 100644 (file)
@@ -139,7 +139,7 @@ typedef struct {
     k5_mutex_t mutex;
     void *data;
 } g_set;
-#define G_SET_INIT { K5_MUTEX_INITIALIZER, 0 }
+#define G_SET_INIT { K5_MUTEX_PARTIAL_INITIALIZER, 0 }
 
 int g_set_init (g_set_elt *s);
 int g_set_destroy (g_set_elt *s);
index 0568f29640626e9c49eebe65b4f71ae0a06efb1d..fb80c894636ade85d7fcc20754a102f13f91a36d 100644 (file)
@@ -5,6 +5,7 @@
 #include "gssapiP_krb5.h"
 
 #include "gss_libinit.h"
+#include "k5-platform.h"
 
 static int             initialized = 0;
 
@@ -12,19 +13,33 @@ static      int             initialized = 0;
  * Initialize the GSSAPI library.
  */
 
+MAKE_INIT_FUNCTION(gssint_lib_init);
+MAKE_FINI_FUNCTION(gssint_lib_fini);
+
+int gssint_lib_init(void)
+{
+    return k5_mutex_finish_init(&kg_vdb.mutex);
+}
+
+void gssint_lib_fini(void)
+{
+    if (!INITIALIZER_RAN(gssint_lib_init) || PROGRAM_EXITING())
+       return;
+    k5_mutex_destroy(&kg_vdb.mutex);
+}
+
 OM_uint32 gssint_initialize_library (void)
 {
-       
        if (!initialized) {
 #if !USE_BUNDLE_ERROR_STRINGS
            add_error_table(&et_k5g_error_table);
            add_error_table(&et_ggss_error_table);
 #endif
 
-               initialized = 1;
+           initialized = 1;
        }
-       
-       return 0;
+
+       return CALL_INIT_FUNCTION(gssint_lib_init);
 }
 
 /*
@@ -33,13 +48,12 @@ OM_uint32 gssint_initialize_library (void)
 
 void gssint_cleanup_library (void)
 {
-
        assert (initialized);
-       
+
 #if !USE_BUNDLE_ERROR_STRINGS
        remove_error_table(&et_k5g_error_table);
        remove_error_table(&et_ggss_error_table);
 #endif
-       
+
        initialized = 0;
 }
index f55b779da3f305159395b039660fa839af13d906..1be0a67a7e693260e1c54f06574acdf352f71afe 100644 (file)
@@ -5,6 +5,9 @@
        (kg_set_ccache_name): Likewise.  Return after an error rather
        than continuing.
 
+       * krb5_gss_glue.c (gss_import_name): Call
+       gssint_initialize_library and check the return status.
+
 2004-04-13  Jeffrey Altman <jaltman@mit.edu>
  
     * k5unseal.c: gss_krb5int_unseal_token_v3() takes a pointer to 
index 540652ad8dfd8c8112a2e38e5dab8114eded9c4f..583881d8e443cc5f1377d80f771b02715a127bdc 100644 (file)
@@ -211,8 +211,14 @@ gss_import_name(minor_status, input_name_buffer, input_name_type, output_name)
      gss_OID input_name_type;
      gss_name_t *output_name;
 {
-   return(krb5_gss_import_name(minor_status, input_name_buffer,
-                              input_name_type, output_name));
+    OM_uint32 err;
+    err = gssint_initialize_library();
+    if (err) {
+       *minor_status = err;
+       return GSS_S_FAILURE;
+    }
+    return(krb5_gss_import_name(minor_status, input_name_buffer,
+                               input_name_type, output_name));
 }
 
 /* V2 */
index 35be9e43743321f5f7f35da6efa030deb4236847..7695a5e329ae838b806301ff1d72f796f810d8ee 100644 (file)
@@ -1,3 +1,14 @@
+2004-04-24  Ken Raeburn  <raeburn@mit.edu>
+
+       * krb5_libinit.c: Include k5-platform.h.
+       (krb5int_lib_init, krb5int_lib_fini): New init/fini functions.
+       Call the corresponding functions for the ccache, keytab, and
+       rcache code.  Incorporate the finalization code from
+       krb5int_cleanup_library.
+       (krb5int_initialize_library): Make sure the init function runs
+       successfully.
+       (krb5int_cleanup_library): Now empty.
+
 2004-04-22  Ken Raeburn  <raeburn@mit.edu>
 
        * libkrb5.exports: New file.
index 5ab9d75a3013801725fa5e3d7479249192b194e4..aacadc5ae5da9a8d4a9d7105909d07008cf0fce4 100644 (file)
@@ -1,3 +1,11 @@
+2004-04-24  Ken Raeburn  <raeburn@mit.edu>
+
+       * ccbase.c: Include ctype.h.
+       (cc_typelist_lock): Use the new partial initializer.
+       (krb5int_cc_initialize): New function; finish the initialization.
+       (krb5int_cc_finalize): New function; destroy the mutex and free
+       any storage for registered types.
+
 2004-04-13  Jeffrey Altman <jaltman@mit.edu>
 
     * ccbase.c:
index 4fb5f8360d95ed6ba0b9895a3764cb759716af70..f635147c26b00976e1de39b01baafb9c42c16a2e 100644 (file)
@@ -50,7 +50,24 @@ static struct krb5_cc_typelist cc_fcc_entry = { &krb5_cc_file_ops,
                                                &cc_mcc_entry };
 
 static struct krb5_cc_typelist *cc_typehead = &cc_fcc_entry;
-static k5_mutex_t cc_typelist_lock = K5_MUTEX_INITIALIZER;
+static k5_mutex_t cc_typelist_lock = K5_MUTEX_PARTIAL_INITIALIZER;
+
+int
+krb5int_cc_initialize(void)
+{
+    return k5_mutex_finish_init(&cc_typelist_lock);
+}
+
+void
+krb5int_cc_finalize(void)
+{
+    struct krb5_cc_typelist *t, *t_next;
+    k5_mutex_destroy(&cc_typelist_lock);
+    for (t = cc_typehead; t != &cc_fcc_entry; t = t_next) {
+       t_next = t->next;
+       free(t);
+    }
+}
 
 
 /*
@@ -100,6 +117,7 @@ krb5_cc_register(krb5_context context, krb5_cc_ops *ops, krb5_boolean override)
  * particular cache type.
  */
 
+#include <ctype.h>
 krb5_error_code KRB5_CALLCONV
 krb5_cc_resolve (krb5_context context, const char *name, krb5_ccache *cache)
 {
index a261bf9019f4b0948920c9da83ed6abf3c5aa6a9..48855a6b7b7118f07dcc45f6493c4e706fe281b6 100644 (file)
@@ -1,3 +1,12 @@
+2004-04-24  Ken Raeburn  <raeburn@mit.edu>
+
+       * ktbase.c: Include ctype.h.
+       (k5_typehead_lock): Use new partial initializer.
+       (krb5int_kt_initialize): New function; finish mutex
+       initialization.
+       (krb5int_kt_finalize): New function; destroy the mutex and free
+       storage associated with registered types.
+
 2004-04-13  Jeffrey Altman <jaltman@mit.edu>
 
     * ktbase.c:
index 79c20ece91d445c247f3e2071d5929916f31fcb9..74ff20cf66b5b40ca37042726fbf679836ae9692 100644 (file)
@@ -52,7 +52,23 @@ const static struct krb5_kt_typelist krb5_kt_typelist_srvtab = {
 };
 static const struct krb5_kt_typelist *kt_typehead = &krb5_kt_typelist_srvtab;
 /* Lock for protecting the type list.  */
-static k5_mutex_t kt_typehead_lock = K5_MUTEX_INITIALIZER;
+static k5_mutex_t kt_typehead_lock = K5_MUTEX_PARTIAL_INITIALIZER;
+
+int krb5int_kt_initialize(void)
+{
+    return k5_mutex_finish_init(&kt_typehead_lock);
+}
+
+void
+krb5int_kt_finalize(void)
+{
+    struct krb5_kt_typelist *t, *t_next;
+    k5_mutex_destroy(&kt_typehead_lock);
+    for (t = kt_typehead; t != &krb5_kt_typelist_srvtab; t = t_next) {
+       t_next = t->next;
+       free(t);
+    }
+}
 
 
 /*
@@ -97,6 +113,7 @@ krb5_kt_register(krb5_context context, const krb5_kt_ops *ops)
  * particular keytab type.
  */
 
+#include <ctype.h>
 krb5_error_code KRB5_CALLCONV
 krb5_kt_resolve (krb5_context context, const char *name, krb5_keytab *ktid)
 {
index e36fced37d00f343f7a9dd4821aa80672da2df45..2d4ef8befdcd7bf6ffd182c9ce02f87ffb736482 100644 (file)
@@ -1,7 +1,12 @@
-2004-04-16  Sam Hartman  <hartmans@mit.edu>
+2004-04-24  Ken Raeburn  <raeburn@mit.edu>
+
+       * init_ctx.c (init_common): In UNIX case, check the return value
+       from krb5int_initialize_library.
 
+2004-04-16  Sam Hartman  <hartmans@mit.edu>
 
-       * gic_pwd.c (krb5int_populate_gic_opt): Take credentials and populate lifetime options based on them.
+       * gic_pwd.c (krb5int_populate_gic_opt): Take credentials and
+       populate lifetime options based on them.
 
        * gic_keytab.c gic_pwd.c :  update callers
 
index 2740d83618b5ce7cff0eaa8a39ac214ac639c85e..de6b22d506cba20f03d2c5d5d47af1be2cbc127c 100644 (file)
@@ -128,7 +128,9 @@ init_common (krb5_context *context, krb5_boolean secure)
        if (retval)
                return retval;
 #else /* assume UNIX for now */
-       krb5int_initialize_library ();
+       retval = krb5int_initialize_library ();
+       if (retval)
+           return retval;
 #endif
 
        *context = 0;
index 3e28908960f08585b24805e0fd4ecb1e0c083b0c..6b7bf23ef8c905855cb83088fe21de862fdd6656 100644 (file)
@@ -13,6 +13,7 @@
 #endif
 
 #include "krb5_libinit.h"
+#include "k5-platform.h"
 
 static int             initialized = 0;
 
@@ -20,41 +21,74 @@ static      int             initialized = 0;
  * Initialize the Kerberos v5 library.
  */
 
+MAKE_INIT_FUNCTION(krb5int_lib_init);
+MAKE_FINI_FUNCTION(krb5int_lib_fini);
+
+/* Possibly load-time initialization -- mutexes, etc.  */
+int krb5int_lib_init(void)
+{
+    int err;
+    err = krb5int_rc_finish_init();
+    if (err)
+       return err;
+    err = krb5int_kt_initialize();
+    if (err)
+       return err;
+    err = krb5int_cc_initialize();
+    if (err)
+       return err;
+    return 0;
+}
+
+/* Always-delayed initialization -- error table linkage, etc.  */
 krb5_error_code krb5int_initialize_library (void)
 {
-       
-       if (!initialized) {
+    int err;
+
+    if (!initialized) {
 #if !USE_BUNDLE_ERROR_STRINGS
-           add_error_table(&et_krb5_error_table);
-           add_error_table(&et_kv5m_error_table);
-           add_error_table(&et_kdb5_error_table);
-           add_error_table(&et_asn1_error_table);
+       add_error_table(&et_krb5_error_table);
+       add_error_table(&et_kv5m_error_table);
+       add_error_table(&et_kdb5_error_table);
+       add_error_table(&et_asn1_error_table);
 #endif
+       initialized = 1;
+    }
 
-               initialized = 1;
-       }
-       
-       return 0;
+    return CALL_INIT_FUNCTION(krb5int_lib_init);
 }
 
 /*
- * Clean up the Kerberos v5 lirbary state
+ * Clean up the Kerberos v5 library state
  */
 
-void krb5int_cleanup_library (void)
+void krb5int_lib_fini(void)
 {
-       assert (initialized);
+    if (!INITIALIZER_RAN(krb5int_lib_init) || PROGRAM_EXITING())
+       return;
+
+    krb5int_rc_terminate();
+    krb5int_kt_finalize();
+    krb5int_cc_finalize();
+
+    if (!initialized)
+       return;
 
 #if defined(_WIN32) || defined(USE_CCAPI)
-       krb5_stdcc_shutdown();
+    krb5_stdcc_shutdown();
 #endif
-       
+
 #if !USE_BUNDLE_ERROR_STRINGS
-       remove_error_table(&et_krb5_error_table);
-       remove_error_table(&et_kv5m_error_table);
-       remove_error_table(&et_kdb5_error_table);
-       remove_error_table(&et_asn1_error_table);
+    remove_error_table(&et_krb5_error_table);
+    remove_error_table(&et_kv5m_error_table);
+    remove_error_table(&et_kdb5_error_table);
+    remove_error_table(&et_asn1_error_table);
 #endif
-       
-       initialized = 0;
+}
+
+/* Still exists because it went into the export list on Windows.  But
+   since the above function should be invoked at unload time, we don't
+   actually want to do anything here.  */
+void krb5int_cleanup_library (void)
+{
 }
index a096114f28d753d7b2ea0f516cb97eb86233c81a..8700544777e55fd616ba1f8c43b6e0ddbcd76e1c 100644 (file)
@@ -1,3 +1,11 @@
+2004-04-24  Ken Raeburn  <raeburn@mit.edu>
+
+       * rc_base.c (rc_typelist_lock): Use new partial initializer.
+       (krb5int_rc_finish_init): New function, finish the mutex
+       initialization.
+       (krb5int_rc_terminate): New function, destroy the mutex and free
+       storage associated with registered types.
+
 2004-03-05  Ken Raeburn  <raeburn@mit.edu>
 
        * rc_base.c: Include k5-thread.h.
index 5758c20bcb5092165eeba429ec8aa38076bad5ff..f26b359a9b06ea68538448f12adad1da555b01de 100644 (file)
@@ -22,7 +22,21 @@ struct krb5_rc_typelist {
 };
 static struct krb5_rc_typelist krb5_rc_typelist_dfl = { &krb5_rc_dfl_ops, 0 };
 static struct krb5_rc_typelist *typehead = &krb5_rc_typelist_dfl;
-static k5_mutex_t rc_typelist_lock = K5_MUTEX_INITIALIZER;
+static k5_mutex_t rc_typelist_lock = K5_MUTEX_PARTIAL_INITIALIZER;
+
+int krb5int_rc_finish_init(void)
+{
+    return k5_mutex_finish_init(&rc_typelist_lock);
+}
+void krb5int_rc_terminate(void)
+{
+    struct krb5_rc_typelist *t, *t_next;
+    k5_mutex_destroy(&rc_typelist_lock);
+    for (t = typehead; t != &krb5_rc_typelist_dfl; t = t_next) {
+       t_next = t->next;
+       free(t);
+    }
+}
 
 krb5_error_code krb5_rc_register_type(krb5_context context,
                                      const krb5_rc_ops *ops)
index 358d9246d0197e7030d170489c16725444ef7c74..4df5b54cf0d26c306f90f9f87af530070aa6a0c7 100644 (file)
@@ -1,3 +1,16 @@
+2004-04-24  Ken Raeburn  <raeburn@mit.edu>
+
+       Delete support for old globally-visible linked list, necessary
+       for thread support.
+       * Makefile.in (STLIBOBJS, LINTFILES, LIBOBJS, SRCS): Drop
+       init_et.c.
+       * error_message.c: Include k5-platform.h.
+       (et_list_lock): Use new partial initializer.
+       (com_err_initialize, com_err_terminate): New init/fini functions.
+       (error_message, add_error_table, remove_error_table): Ensure the
+       initializer ran successfully.
+       * error_table.h (_et_list) [!_WIN32]: Delete declaration.
+
 2004-04-22  Ken Raeburn  <raeburn@mit.edu>
 
        * libcom_err.exports: New file.
index ce2cec44d5ceb5883b4d9971ba44fe09901c7e35..50fd0c2ac93ba1b6a4fa9b39d1e0459355bef3d5 100644 (file)
@@ -14,7 +14,7 @@ SED = sed
 ##DOS##XTRA=
 ##DOS##OBJFILE=$(OUTPRE)comerr.lst
 
-STLIBOBJS=error_message.o et_name.o init_et.o com_err.o
+STLIBOBJS=error_message.o et_name.o com_err.o
 STOBJLISTS=OBJS.ST
 LIB=com_err
 LIBMAJOR=3
@@ -25,11 +25,10 @@ clean-unix:: clean-liblinks clean-libs clean-libobjs clean-lclint
 install-unix:: install-libs
 
 LINTFLAGS=-uhvb 
-LINTFILES= error_message.c et_name.c init_et.c com_err.c
+LINTFILES= error_message.c et_name.c com_err.c
 LIBOBJS=$(OUTPRE)com_err.$(OBJEXT) \
        $(OUTPRE)error_message.$(OBJEXT) \
-       $(OUTPRE)et_name.$(OBJEXT) \
-       $(OUTPRE)init_et.$(OBJEXT)
+       $(OUTPRE)et_name.$(OBJEXT)
 # for et_lex.lex.c include in error_table.y
 LOCALINCLUDES=-I. -I$(srcdir)
 
@@ -45,7 +44,6 @@ FILES=        Makefile et_name.c error_message.c compile_et.c \
 #      init_et.c com_err.c
 SRCS= $(srcdir)/error_message.c \
        $(srcdir)/et_name.c \
-       $(srcdir)/init_et.c \
        $(srcdir)/com_err.c
 
 #
index 67b62ee66851842517c4f9a92870ae0c246e17a6..cf8fe53aa72a5d77fec5c7bc64c5f69ef443cd35 100644 (file)
@@ -27,6 +27,7 @@
 #include "com_err.h"
 #include "error_table.h"
 #include "k5-thread.h"
+#include "k5-platform.h"
 
 #if defined(_WIN32)
 #define HAVE_STRERROR
@@ -41,7 +42,22 @@ static char buffer[ET_EBUFSIZ];
 
 /*@null@*/ static struct et_list * _et_list = (struct et_list *) NULL;
 /*@null@*//*@only@*/static struct dynamic_et_list * et_list_dynamic;
-static k5_mutex_t et_list_lock = K5_MUTEX_INITIALIZER;
+static k5_mutex_t et_list_lock = K5_MUTEX_PARTIAL_INITIALIZER;
+
+MAKE_INIT_FUNCTION(com_err_initialize);
+MAKE_FINI_FUNCTION(com_err_terminate);
+
+int com_err_initialize(void)
+{
+    return k5_mutex_finish_init(&et_list_lock);
+}
+
+void com_err_terminate(void)
+{
+    if (! INITIALIZER_RAN(com_err_initialize) || PROGRAM_EXITING())
+       return;
+    k5_mutex_destroy(&et_list_lock);
+}
 
 #ifndef DEBUG_TABLE_LIST
 #define dprintf(X)
@@ -101,6 +117,8 @@ error_message(long code)
            goto system_error_code;
 #endif
 
+       if (CALL_INIT_FUNCTION(com_err_initialize))
+           return 0;
        merr = k5_mutex_lock(&et_list_lock);
        if (merr)
            goto oops;
@@ -230,6 +248,9 @@ add_error_table(/*@dependent@*/ const struct error_table * et)
     struct dynamic_et_list *del;
     int merr;
 
+    if (CALL_INIT_FUNCTION(com_err_initialize))
+       return 0;
+
     del = (struct dynamic_et_list *)malloc(sizeof(struct dynamic_et_list));
     if (del == NULL)
        return errno;
@@ -256,6 +277,8 @@ remove_error_table(const struct error_table * et)
     struct et_list **el;
     int merr;
 
+    if (CALL_INIT_FUNCTION(com_err_initialize))
+       return 0;
     merr = k5_mutex_lock(&et_list_lock);
     if (merr)
        return merr;
index 9e1d26fa5c825a8eb6e24505756cc8c2bfed24ca..41453587c4f264975dfcb8e2a94321a0bb3e33da 100644 (file)
@@ -15,9 +15,6 @@ struct et_list {
     /*@dependent@*//*@null@*/ struct et_list *next;
     /*@dependent@*//*@null@*/ const struct error_table *table;
 };
-#if !defined(_WIN32)
-/*@null@*//*@dependent@*/ extern struct et_list * _et_list;
-#endif
 
 struct dynamic_et_list {
     /*@only@*//*@null@*/ struct dynamic_et_list *next;
index 13828007c9211cc4d157ab6001601ef0e91ecb03..76a984a7efa32d3e798fbfd7928a74782e79c69e 100644 (file)
@@ -1,3 +1,14 @@
+2004-04-24  Ken Raeburn  <raeburn@mit.edu>
+
+       * prof_file.c: Include k5-platform.h.
+       (krb5int_profile_shared_data): Now static.  Use new partial mutex
+       initializer.
+       (struct global_shared_profile_data, g_shared_trees,
+       g_shared_trees_mutex): Moved here from prof_int.h.
+       (profile_library_initializer, profile_library_finalizer): New
+       init/fini functions.  Deal with mutex.
+       (profile_open_file): Verify initializer ran successfully.
+
 2004-04-22  Ken Raeburn  <raeburn@mit.edu>
 
        * libprofile.exports: New file.
index 1141127a1c03bd432164ab4c8cc57de8c3917d1a..12771d234e1e5e7895a162534b57170a5a7cb30e 100644 (file)
 #define stat _stat
 #endif
 
+#include "k5-platform.h"
+
 #ifdef SHARE_TREE_DATA
-struct global_shared_profile_data krb5int_profile_shared_data = {
+struct global_shared_profile_data {
+       /* This is the head of the global list of shared trees */
+       prf_data_t trees;
+       /* Lock for above list.  */
+       k5_mutex_t mutex;
+};
+#define g_shared_trees         (krb5int_profile_shared_data.trees)
+#define g_shared_trees_mutex   (krb5int_profile_shared_data.mutex)
+
+static struct global_shared_profile_data krb5int_profile_shared_data = {
     0,
-    K5_MUTEX_INITIALIZER
+    K5_MUTEX_PARTIAL_INITIALIZER
 };
 #endif
 
+MAKE_INIT_FUNCTION(profile_library_initializer);
+MAKE_FINI_FUNCTION(profile_library_finalizer);
+
+int profile_library_initializer(void)
+{
+#ifdef SHARE_TREE_DATA
+    return k5_mutex_finish_init(&g_shared_trees_mutex);
+#else
+    return 0;
+#endif
+}
+void profile_library_finalizer(void)
+{
+    if (! INITIALIZER_RAN(profile_library_initializer) || PROGRAM_EXITING())
+       return;
+#ifdef SHARE_TREE_DATA
+    k5_mutex_destroy(&g_shared_trees_mutex);
+#endif
+}
+
 static void profile_free_file_data(prf_data_t);
 
 static int rw_access(profile_filespec_t filespec)
@@ -96,6 +127,10 @@ errcode_t profile_open_file(const_profile_filespec_t filespec,
        prf_data_t      data;
        char            *expanded_filename;
 
+       retval = CALL_INIT_FUNCTION(profile_library_initializer);
+       if (retval)
+               return retval;
+
        prf = malloc(sizeof(struct _prf_file_t));
        if (!prf)
                return ENOMEM;
index a0d90b5e206460ee9feef8aa185d05bbe5c68377..c6ec667accc589d56afa6b285804077a7c329f88 100644 (file)
@@ -54,19 +54,6 @@ struct _prf_file_t {
 
 typedef struct _prf_file_t *prf_file_t;
 
-#ifdef SHARE_TREE_DATA
-struct global_shared_profile_data {
-       /* This is the head of the global list of shared trees */
-       prf_data_t trees;
-       /* Lock for above list.  */
-       k5_mutex_t mutex;
-};
-extern struct global_shared_profile_data krb5int_profile_shared_data;
-#define g_shared_trees         (krb5int_profile_shared_data.trees)
-#define g_shared_trees_mutex   (krb5int_profile_shared_data.mutex)
-
-#endif /* SHARE_TREE_DATA */
-
 /*
  * The profile flags
  */