diff --git a/net/xrootd/src/xrootd/config/ARCHS b/net/xrootd/src/xrootd/config/ARCHS index 25ed6ac3ecfe0b321c64a9bd46b0c44f477bbe35..5a7889a470325d42df07f15f7dd18997aa76efd5 100644 --- a/net/xrootd/src/xrootd/config/ARCHS +++ b/net/xrootd/src/xrootd/config/ARCHS @@ -73,4 +73,5 @@ sun4x_5 sungcc all for Solaris 5.x with gcc and glibc sun4x_4 sungcc all for Solaris 4.x with gcc and glibc sunx86_510 sunCCi86pc all for Solaris 5.x on i86 platform sunx86_510 sunCCamd510 all for Solaris 5.10 on x86-64 platform +sunx86_510 sunCCamd all for Solaris 5.10 on x86-64 platform sunx86_511 sunCCi86pc all for Solaris 5.x > 5.10 on x86 platform diff --git a/net/xrootd/src/xrootd/config/GNUmake.env.in b/net/xrootd/src/xrootd/config/GNUmake.env.in index 3b27aa1226bb0b6d68194db71177232dc2f4d404..6345212533cee098c92821b2d15cd690edf0cc11 100644 --- a/net/xrootd/src/xrootd/config/GNUmake.env.in +++ b/net/xrootd/src/xrootd/config/GNUmake.env.in @@ -1,4 +1,4 @@ -# $Id: GNUmake.env.in,v 1.31 2009/07/28 20:01:44 ganis Exp $ +# $Id: GNUmake.env.in,v 1.32 2010/01/13 11:41:22 ganis Exp $ #------------------------------------------------------------------------------# # C o m m o n V a r i a b l e s # @@ -71,6 +71,10 @@ PWDEXTRACFLAGS = @pwdextracflags@ @afsextracflags@ @libssl@ SSLEXTRACFLAGS = @sslextracflags@ +# XML relevant variables +@incxml@ +@libxml@ + # perl relevant variables PERLBIN = @perlbin@ PERLINC = @perlincdir@ diff --git a/net/xrootd/src/xrootd/config/GNUmake.rules.gccx8664 b/net/xrootd/src/xrootd/config/GNUmake.rules.gccx8664 index 7fa184b03a7ef1f87794230aac0a38d6744d3da6..5835b02acba23edc16d082ae5069cf6280c3acc4 100644 --- a/net/xrootd/src/xrootd/config/GNUmake.rules.gccx8664 +++ b/net/xrootd/src/xrootd/config/GNUmake.rules.gccx8664 @@ -1,4 +1,4 @@ -# $Id: GNUmake.rules.gccx8664,v 1.5 2006/10/19 14:49:29 ganis Exp $ +# $Id: GNUmake.rules.gccx8664,v 1.6 2009/12/01 14:11:24 ganis Exp $ #------------------------------------------------------------------------------# # R u l e s f o r g e n e r i c g c c # diff --git a/net/xrootd/src/xrootd/config/GNUmake.rules.icc b/net/xrootd/src/xrootd/config/GNUmake.rules.icc index bcf65e861aadbbb070dc9d959b9f37176b6c7cef..4d8e24fe08a6be96741a1f9bfb56b44c0e80f1ea 100644 --- a/net/xrootd/src/xrootd/config/GNUmake.rules.icc +++ b/net/xrootd/src/xrootd/config/GNUmake.rules.icc @@ -1,4 +1,4 @@ -# $Id: GNUmake.rules.icc,v 1.8 2009/06/11 08:31:30 ganis Exp $ +# $Id: GNUmake.rules.icc,v 1.10 2009/12/01 14:11:01 ganis Exp $ #------------------------------------------------------------------------------# # R u l e s f o r g e n e r i c i c c # diff --git a/net/xrootd/src/xrootd/config/GNUmake.rules.iccx8664 b/net/xrootd/src/xrootd/config/GNUmake.rules.iccx8664 index 06fe3a7ebf2b8d0ac7c92b18c762a99cb9a75f32..50383c9f6ef8df55197b5ae3e10d41c840cbad75 100644 --- a/net/xrootd/src/xrootd/config/GNUmake.rules.iccx8664 +++ b/net/xrootd/src/xrootd/config/GNUmake.rules.iccx8664 @@ -1,4 +1,4 @@ -# $Id: GNUmake.rules.iccx8664,v 1.5 2009/10/15 10:44:45 ganis Exp $ +# $Id: GNUmake.rules.iccx8664,v 1.7 2009/12/01 14:11:15 ganis Exp $ #------------------------------------------------------------------------------# # R u l e s f o r g e n e r i c i c c # diff --git a/net/xrootd/src/xrootd/config/GNUmake.rules.sunCCamd b/net/xrootd/src/xrootd/config/GNUmake.rules.sunCCamd index e68d4eb3ef41ced2a0785dd4ef89353ee9c505e5..497a27f977d9530d5a9905549976a34768790577 100644 --- a/net/xrootd/src/xrootd/config/GNUmake.rules.sunCCamd +++ b/net/xrootd/src/xrootd/config/GNUmake.rules.sunCCamd @@ -1,4 +1,4 @@ -# $Id: GNUmake.rules.sunCCamd,v 1.7 2009/08/17 14:15:14 ganis Exp $ +# $Id: GNUmake.rules.sunCCamd,v 1.8 2009/12/16 00:14:55 abh Exp $ #------------------------------------------------------------------------------# # R u l e s f o r g e n e r i c s u n C C # @@ -11,12 +11,12 @@ TYPEMISC = -KPIC -DSUNCC -D__solaris__ -DSUNX86 $(CFTRACE) -library=stlport4 SUNMT = -D_REENTRANT -mt -D_POSIX_PTHREAD_SEMANTICS TYPECF32 = TYPECF64 = -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -TYPEOPT = $(TYPEMISC) $(SUNMT) -O -m64 -TYPEDBG = $(TYPEMISC) $(SUNMT) -g -xs -m64 -TYPELDSO = -G -m64 +TYPEOPT = $(TYPEMISC) $(SUNMT) -fast -xtarget=opteron -xarch=amd64 +TYPEDBG = $(TYPEMISC) $(SUNMT) -g -xs -xtarget=opteron -xarch=amd64 +TYPELDSO = -G -xtarget=opteron -xarch=amd64 TYPESHLIB = so -TYPELIBS = -L/usr/lib/amd64 -L/lib/amd64 \ +TYPELIBS = -R/usr/lib/amd64 -L/usr/lib/amd64 -L/lib/amd64 \ -lposix4 -lsocket -lnsl $(PTHREAD) -ldl TYPELIBMT = -lmtmalloc TYPELIBSF = -lsendfile diff --git a/net/xrootd/src/xrootd/config/GNUmake.rules.sunCCamd510 b/net/xrootd/src/xrootd/config/GNUmake.rules.sunCCamd510 index 8365ce3283bed4256a9b2c2f19fa59fd7d3d4abf..9f8b6bdfc72e6e8f14207ebdf840549001cc9f9a 100644 --- a/net/xrootd/src/xrootd/config/GNUmake.rules.sunCCamd510 +++ b/net/xrootd/src/xrootd/config/GNUmake.rules.sunCCamd510 @@ -1,4 +1,4 @@ -# $Id: GNUmake.rules.sunCCamd510,v 1.1 2009/09/16 14:22:42 ganis Exp $ +# $Id: GNUmake.rules.sunCCamd510,v 1.2 2009/12/11 01:57:01 abh Exp $ #------------------------------------------------------------------------------# # R u l e s f o r g e n e r i c s u n C C # @@ -16,7 +16,7 @@ TYPEDBG = $(TYPEMISC) $(SUNMT) -g -xs -xtarget=opteron -xarch=amd64 TYPELDSO = -G -xtarget=opteron -xarch=amd64 TYPESHLIB = so -TYPELIBS = -L/usr/lib/amd64 -L/lib/amd64 \ +TYPELIBS = -R/usr/lib/amd64 -L/usr/lib/amd64 -L/lib/amd64 \ -lposix4 -lsocket -lnsl $(PTHREAD) -ldl TYPELIBMT = -lmtmalloc TYPELIBSF = -lsendfile diff --git a/net/xrootd/src/xrootd/config/GNUmakefile.in b/net/xrootd/src/xrootd/config/GNUmakefile.in index 147a9935f13adbe3678b6abd9445c8439eac3c3c..a9c638632597dbb9bddec1f2468285d2b433601c 100644 --- a/net/xrootd/src/xrootd/config/GNUmakefile.in +++ b/net/xrootd/src/xrootd/config/GNUmakefile.in @@ -1,4 +1,4 @@ -# $Id: GNUmakefile.in,v 1.36 2009/11/04 09:54:47 furano Exp $ +# $Id: GNUmakefile.in,v 1.37 2010/01/13 11:41:22 ganis Exp $ #------------------------------------------------------------------------------# # C o m p o n e n t s # @@ -230,6 +230,11 @@ XrdSecpwd: FORCE $(ECHO)cd src/XrdSecpwd;\ $(MAKE) $(MAKEXEQ) ARCH=$(ARCH) --no-print-directory +XrdSecssl: FORCE + @echo $(XMSG) secssl $(XDBG) component... + $(ECHO)cd src/XrdSecssl;\ + $(MAKE) $(MAKEXEQ) ARCH=$(ARCH) --no-print-directory + XrdSecsss: FORCE @echo $(XMSG) secsss $(XDBG) component... $(ECHO)cd src/XrdSecsss;\ diff --git a/net/xrootd/src/xrootd/configure.classic b/net/xrootd/src/xrootd/configure.classic index d6a8135548207ebc351b7ba49caa51e645ee26d9..b50a79a13c377cd9ba8be3be849627dc64288c0d 100755 --- a/net/xrootd/src/xrootd/configure.classic +++ b/net/xrootd/src/xrootd/configure.classic @@ -1,7 +1,7 @@ #! /bin/sh ################################################################################ # -# $Id: configure.classic,v 1.99 2009/10/15 10:46:58 ganis Exp $ +# $Id: configure.classic,v 1.104 2010/01/13 11:41:21 ganis Exp $ # # # c o n f i g u r e . c l a s s i c # # (previously called configure) # @@ -65,6 +65,7 @@ options=" \ enable_afs \ enable_echo \ enable_gsi \ + enable_secssl \ enable_krb4 \ enable_krb5 \ enable_mon \ @@ -91,6 +92,7 @@ enable_mon=yes enable_perlint=no enable_shadowpw=no enable_shared=yes +enable_secssl= enable_ssl= enable_thread=yes enable_javaint=no @@ -684,6 +686,7 @@ enable/disable options, prefix with either --enable- or --disable- afs AFS support, requires AFS libs and objects echo Enable/disable echoing of commands during building gsi GSI authentication support, requires OpenSSL + secssl SSL authentication support, requires OpenSSL javaint support for the Java Native Interface for XrdClientAdmin krb4 Kerberos4 support, requires Kerberos libs krb5 Kerberos5 support, requires Kerberos libs @@ -718,6 +721,9 @@ with options, prefix with --with-, enables corresponding support ssl-libdir OpenSSL support, location of libssl, libcrypto (has priority over ssl-dir) ssl-shared OpenSSL support, controls usage of shared linkage SSL libraries {yes/no} thread-libdir Path to libpthread + xml SSL authentication support, location of XML2 + xml-incdir SSL authentication support, location of libxml/tree.h, ... (has priority over xml-dir) + xml-libdir SSL authentication support, location of libxml2 (has priority over xml-dir) with compiler options, prefix with --with-, overrides default value @@ -729,7 +735,8 @@ Advanced options: --no-arch-subdirs Do not use architecture dependent names for bin/lib paths --build-client-only Build only the client related stuff --build-sec-only Build only security modules - --use-xrd-strlcpy Force use of the xrd implementation of strlcpy + --use-xrd-strlcpy Force use of the xrd implementation of strlcpy + --use-libtcmalloc Use the thread-caching malloc implementation if available --has-getpbynr Do not check for getprotobyname_r --with-gethbynr=dir Do not check for gethostbyname_r, take prototype from 'dir'/netdb.h --with-nameinfo=dir Do not check for getnameinfo, take prototype from 'dir'/netdb.h @@ -856,6 +863,7 @@ if test $# -gt 0 ; then --has-sigwaitinfo) hassigwti="yes" ;; --has-fstatat) hasfstatat="yes" ;; --has-readline) hasreadline="yes" ;; + --use-libtcmalloc) enable_libtcmalloc="yes" ;; --with-afs=*) afsdir=$optarg ; enable_afs="yes" ;; --with-afs-incdir=*) afsincdir=$optarg ; enable_afs="yes" ;; --with-afs-libdir=*) afslibdir=$optarg ; enable_afs="yes" ;; @@ -873,6 +881,9 @@ if test $# -gt 0 ; then --with-ssl-libdir=*) ssllibdir=$optarg ; enable_ssl="yes" ;; --with-ssl-shared=*) sslshared=$optarg ; enable_ssl="yes" ;; --with-thread-libdir=*) threadlibdir=$optarg ; enable_thread="yes" ;; + --with-xml=*) xmldir=$optarg ; enable_secssl="yes" ;; + --with-xml-incdir=*) xmlincdir=$optarg ; enable_secssl="yes" ;; + --with-xml-libdir=*) xmllibdir=$optarg ; enable_secssl="yes" ;; --prefix=*) inst_prefix=$optarg ;; --libdir=*) inst_libdir=$optarg ;; --etcdir=*) inst_etcdir=$optarg ;; @@ -892,6 +903,7 @@ if test $# -gt 0 ; then f=`echo $1 | sed -e 's/--//' -e 's/-/_/'` if test "x$f" = "xenable_sec" ; then enable_gsi=yes + enable_secssl=no enable_krb4=yes enable_krb5=yes enable_pwd=yes @@ -913,6 +925,7 @@ fi f=`echo $1 | sed -e 's/--disable/enable/' -e 's/-/_/'` if test "x$f" = "xenable_sec" ; then enable_gsi=no + enable_secssl=no enable_krb4=no enable_krb5=no enable_pwd=no @@ -1312,6 +1325,39 @@ if test "x$haslibz" != "x"; then fi fi +###################################################################### +# +### echo %%% Check for libtcmalloc +# + + +libtcmalloc="" +if test "x$enable_libtcmalloc" = "xyes"; then + if test "x$syslibs" = "x" ; then + what2look4="/usr/lib /usr/local/lib /opt/lib $finkdir/lib" + else + what2look4="$syslibs" + fi + + if test "x$haslibz" != "x"; then + check_library "libtcmalloc" "$enable_shared" "" $what2look4 + if test "x$found_lib" = "x" ; then + libtcmalloc="" + else + libtcmalloc="$found_dir $found_lib" + fi + + if test "x$libtcmalloc" = "x"; then + check_library "libtcmalloc_minimal" "$enable_shared" "" $what2look4 + if test "x$found_lib" = "x" ; then + libtcmalloc="" + else + libtcmalloc="$found_dir $found_lib" + fi + fi + fi + +fi ###################################################################### # @@ -2277,6 +2323,96 @@ if test ! "x$enable_gsi" = "xno" ; then # fi +###################################################################### +# +### echo %%% Support for SSL authentication +# +# +if test "x$enable_secssl" = "x" ; then + if test "x$platform" = "xlinux" || test "x$platform" = "xmacosx" ; then + # Default 'yes' for linux and macosx only, for the time being + enable_secssl="yes" + else + enable_secssl="no" + fi +fi +secsslextracflags="" +havesecssl="" +if test ! "x$enable_secssl" = "xno" ; then + # + # Requires XML2 + if test ! "x$xmldir" = "x" ; then + if test "x$xmlincdir" = "x" ; then + xmlincdir=$xmldir/include + fi + if test "x$xmllibdir" = "x" ; then + xmllibdir=$xmldir/lib + fi + fi + # Check for xml include and library + check_header "libxml/tree.h" "$xmlincdir" $XMLDIR $XMLDIR/include \ + /opt/libxml2/include /usr/local/include/libxml2 /usr/include/libxml2 + if test ! "x$found_dir" = "x" ; then + xmlincdir=$found_dir + # Check the other headers + if test -f "$found_dir/libxml/xmlmemory.h" ; then + echo "Checking for libxml/xmlmemory.h ... $found_dir" + if test -f "$found_dir/libxml/parser.h" ; then + echo "Checking for libxml/parser.h ... $found_dir" + else + echo "Checking for libxml/parser.h ... no" + enable_secssl="no" + fi + else + echo "Checking for libxml/xmlmemory.h ... no" + enable_secssl="no" + fi + else + enable_secssl="no" + fi + + if test "x$enable_secssl" = "xyes" ; then + check_library "libxml2" "$enable_shared" "$xmllibdir" $XMLDIR \ + $XMLDIR/lib $XMLDIR/.libs /opt/libxml2/lib /usr/local/lib /usr/lib + if test ! "x$found_lib" = "x" ; then + xmllib=$found_lib + xmllibdir=$found_dir + else + enable_secssl="no" + xmlincdir= + xmllib= + xmllibdir= + fi + fi + + # + message "Enabling SSL authentication module" + # + if test "x$enable_secssl" = "xyes" ; then + buildsec="yes" + # plug-in name + havesecssl="XrdSecssl" + # Require OpenSSL + if test ! "x$enable_ssl" = "xno" ; then + enable_ssl="yes" + incxml="INCXML = -I$xmlincdir" + libxml="LIBXML = $xmllibdir $xmllib" + echo "done" + else + # explicitely disabled: warn + incxml="INCXML = " + libxml="LIBXML = " + echo "no: SSL authentication requires OpenSSL" + fi + else + # explicitely disabled: warn + incxml="INCXML = " + libxml="LIBXML = " + echo "no: SSL authentication requires XML2 dev package" + fi + # +fi + ###################################################################### # ### echo %%% OpenSSL support - Third party libraries @@ -2467,6 +2603,9 @@ if test "x$enable_ssl" = "xyes" ; then # GSI needs OpenSSL echo "Disabling GSI ... (requires OpenSSL)" havegsi="" + # SSL needs OpenSSL + echo "Disabling SSL ... (requires OpenSSL)" + havesecssl="" if test "x$havepwd" = "x" ; then xrdsut="" xrdcrypto="" @@ -2496,7 +2635,7 @@ fi xrdsec= hasxrdcrypto= if test "x$buildsec" = "xyes" ; then - xrdsec="$xrdsut $xrdcrypto $havekrb4 $havekrb5 $havepwd $havesss $havegsi XrdSecunix" + xrdsec="$xrdsut $xrdcrypto $havekrb4 $havekrb5 $havepwd $havesss $havesecssl $havegsi XrdSecunix" if test ! "x$xrdcrypto" = "x" ; then hasxrdcrypto="-DHAVE_XRDCRYPTO" fi @@ -2707,10 +2846,13 @@ sed -e "s|@arch@|$arch|" \ -e "s|@incssl@|$incssl|" \ -e "s|@libssl@|$libssl|" \ -e "s|@sslextracflags@|$sslextracflags|" \ + -e "s|@incxml@|$incxml|" \ + -e "s|@libxml@|$libxml|" \ -e "s|@hasdevpoll@|$hasdevpoll|" \ -e "s|@hasepoll@|$hasepoll|" \ -e "s|@haslibz@|$haslibz|" \ -e "s|@libz@|$libz|" \ + -e "s|@libtcmalloc|$libtcmalloc@|" \ -e "s|@hassendfile@|$hassendfile|" \ -e "s|@hassigwti@|$hassigwti|" \ -e "s|@hassetresuid@|$hassetresuid|" \ diff --git a/net/xrootd/src/xrootd/src/Xrd/XrdLink.cc b/net/xrootd/src/xrootd/src/Xrd/XrdLink.cc index ac0723edb6a814d55c8accdda614b1c5dbb60cf2..1e6ca2fb2c330bc78cfda6d2d24986dadb9f013f 100644 --- a/net/xrootd/src/xrootd/src/Xrd/XrdLink.cc +++ b/net/xrootd/src/xrootd/src/Xrd/XrdLink.cc @@ -363,9 +363,12 @@ int XrdLink::Close(int defer) // At this point we can have no lock conflicts, so if someone is waiting for // us to terminate let them know about it. Note that we will get the condvar -// mutex while we hold the opMutex. This is the required order! +// mutex while we hold the opMutex. This is the required order! We will also +// zero out the pointer to the condvar while holding the opmutex. // - if (KillcvP) {KillcvP->Lock(); KillcvP->Signal(); KillcvP->UnLock();} + if (KillcvP) {KillcvP-> Lock(); KillcvP->Signal(); + KillcvP->UnLock(); KillcvP = 0; + } // Remove ourselves from the poll table and then from the Link table. We may // not hold on to the opMutex when we acquire the LTMutex. However, the link @@ -1203,6 +1206,16 @@ int XrdLink::Terminate(const XrdLink *owner, int fdnum, unsigned int inst) if (killDone.Wait(int(killWait))) {wTime += killWait; KillCnt |= KillXwt;} else wTime = -EPIPE; killDone.UnLock(); + +// Reobtain the opmutex so that we can zero out the pointer the condvar pntr +// This is really stupid code but because we don't have a way of associating +// an arbitrary mutex with a condvar. But since this code is rarely executed +// the ugliness is sort of tolerable. +// + lp->opMutex.Lock(); KillcvP = 0; lp->opMutex.UnLock(); + +// Do some tracing +// TRACEI(DEBUG,"Terminate " << (wTime <= 0 ? "complete ":"timeout ") <<wTime); return wTime; } diff --git a/net/xrootd/src/xrootd/src/Xrd/XrdScheduler.cc b/net/xrootd/src/xrootd/src/Xrd/XrdScheduler.cc index f1c89f796096b58070d063c0cb745f243585f8dc..8b8b946fc00b05cb0f1109b6af83517dddccfa21 100644 --- a/net/xrootd/src/xrootd/src/Xrd/XrdScheduler.cc +++ b/net/xrootd/src/xrootd/src/Xrd/XrdScheduler.cc @@ -279,7 +279,7 @@ void XrdScheduler::Run() } else { num_JobsinQ = 0; if (num_Layoffs > 0) - {num_Layoffs--; num_TDestroy--; num_Workers--; + {num_Layoffs--; num_TDestroy++; num_Workers--; idl_Workers--; TRACE(SCHED, "terminating thread; workers=" <<num_Workers); SchedMutex.UnLock(); return; diff --git a/net/xrootd/src/xrootd/src/XrdApps/GNUmakefile b/net/xrootd/src/xrootd/src/XrdApps/GNUmakefile index 14cdbec5ea75e8cc50da6ffc3f08cc0163f083e9..991b2bdfc7ca72016a6b04b4ef88a0c4c87abc06 100644 --- a/net/xrootd/src/xrootd/src/XrdApps/GNUmakefile +++ b/net/xrootd/src/xrootd/src/XrdApps/GNUmakefile @@ -1,4 +1,4 @@ -# $Id: GNUmakefile,v 1.5 2009/08/26 22:22:28 abh Exp $ +# $Id: GNUmakefile,v 1.6 2010/01/07 13:59:43 ganis Exp $ #-----------------------------------------------------------------------------# # E n v i r o n m e n t # diff --git a/net/xrootd/src/xrootd/src/XrdApps/Xrdadler32.cc b/net/xrootd/src/xrootd/src/XrdApps/Xrdadler32.cc index 42b2da90d7532cb43cbb1727a6f509abc7c0039b..a796cdd7fc88b23d5ab1128b8dc67ec21d73728f 100644 --- a/net/xrootd/src/xrootd/src/XrdApps/Xrdadler32.cc +++ b/net/xrootd/src/xrootd/src/XrdApps/Xrdadler32.cc @@ -15,11 +15,16 @@ const char *Xrdadler32CVSID = "$Id$"; #define _FILE_OFFSET_BITS 64 #include <stdio.h> +#include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <fcntl.h> +#include <errno.h> +#ifdef __linux__ + #include <sys/xattr.h> +#endif #include <zlib.h> #include "XrdPosix/XrdPosixExtern.hh" @@ -31,6 +36,77 @@ const char *Xrdadler32CVSID = "$Id$"; #include "XrdClient/XrdClientAdmin.hh" #include "XrdOuc/XrdOucString.hh" +void fSetXattrAdler32(int fd, const char* attr, const char *value) +{ + struct stat st; + char mtime[12], attr_val[25]; + int rc; + + rc = fstat(fd, &st); + if (rc < 0 || strlen(value) != 8) + return; + else + sprintf(mtime, "%ld", st.st_mtime); + + strcpy(attr_val, value); + strcat(attr_val, ":"); + strcat(attr_val, mtime); + +#if defined(__linux__) + rc = fsetxattr(fd, attr, attr_val, strlen(attr_val), 0x0); +#elif defined(__solaris__) + int attrfd; + attrfd = openat(fd, attr, O_XATTR|O_CREAT|O_TRUNC|O_WRONLY); + if (attrfd < 0) return; + + rc = write(attrfd, attr_val, strlen(attr_val)); +/* + Solaris extended attributes are files in orthogonal namespace. + Their permission wont' change according to real files. + */ + fchmod(attrfd, S_IRWXU|S_IRGRP|S_IROTH); + close(attrfd); +#endif + return; +} + +int fGetXattrAdler32(int fd, const char* attr, char *value) +{ + struct stat st; + char mtime[12], attr_val[25], *p; + int rc; + + rc = fstat(fd, &st); + if (rc < 0) + return(0); + else + sprintf(mtime, "%ld", st.st_mtime); + + +#if defined(__linux__) + rc = fgetxattr(fd, attr, attr_val, 25); +#elif defined(__solaris__) + int attrfd; + attrfd = openat(fd, attr, O_XATTR|O_RDONLY); + if (attrfd < 0) return(0); + + rc = read(attrfd, attr_val, 25); + close(attrfd); +#else + return(0); +#endif + + if (rc == -1 || attr_val[8] != ':') return(0); + attr_val[8] = '\0'; + attr_val[rc] = '\0'; + p = attr_val + 9; + + if (strcmp(p, mtime)) return(0); + + strcpy(value, attr_val); + return(strlen(value)); +} + /* get the actual root url pointing to the data server */ char get_current_url(const char *oldurl, char *newurl) { @@ -90,7 +166,8 @@ char getchksum(const char *rooturl, char *chksum) int main(int argc, char *argv[]) { - char path[2048], chksum[128], buf[N]; + char path[2048], chksum[128], buf[N], adler_str[9]; + const char attr[] = "user.checksum.adler32"; struct stat stbuf; int fd, len, rc; uLong adler; @@ -111,7 +188,6 @@ int main(int argc, char *argv[]) else XrdPosix_URL(argv[1], path, sizeof(path)); } - if (argc == 1 || path[0] == '\0') { /* this is a local file */ if (argc > 1) @@ -124,6 +200,12 @@ int main(int argc, char *argv[]) printf("Error_accessing %s\n", path); return 1; } + else /* see if the adler32 is saved in attribute already */ + if (fGetXattrAdler32(fd, attr, adler_str) == 8) + { + printf("%s %s\n", adler_str, path); + return 0; + } } else { @@ -133,7 +215,12 @@ int main(int argc, char *argv[]) while ( (len = read(fd, buf, N)) > 0 ) adler = adler32(adler, (const Bytef*)buf, len); - if (fd != STDIN_FILENO) close(fd); + if (fd != STDIN_FILENO) + { /* try saving adler32 to attribute before close() */ + sprintf(adler_str, "%08lx", adler); + fSetXattrAdler32(fd, attr, adler_str); + close(fd); + } printf("%08lx %s\n", adler, path); return 0; } diff --git a/net/xrootd/src/xrootd/src/XrdBwm/XrdBwmHandle.cc b/net/xrootd/src/xrootd/src/XrdBwm/XrdBwmHandle.cc index 9156da40d48fbd9316949f9c85679922e434c309..8111e083ad5d351a0b720628b5bcdaeb6bdbba90 100644 --- a/net/xrootd/src/xrootd/src/XrdBwm/XrdBwmHandle.cc +++ b/net/xrootd/src/xrootd/src/XrdBwm/XrdBwmHandle.cc @@ -39,37 +39,40 @@ extern XrdSysError BwmEroute; /* L o c a l C l a s s e s */ /******************************************************************************/ -class XrdBwmHandleCB : public XrdOucErrInfo +class XrdBwmHandleCB : public XrdOucEICB, public XrdOucErrInfo { public: -void *operator new(size_t size) - {void *mP; +static +XrdBwmHandleCB *Alloc() + {XrdBwmHandleCB *mP; xMutex.Lock(); - if (!(mP = Free)) mP = malloc(size); - else memcpy(&Free, mP, sizeof(mP)); + if (!(mP = Free)) mP = new XrdBwmHandleCB; + else Free = mP->Next; xMutex.UnLock(); return mP; } -void operator delete(void *p) +void Done(int &Results, XrdOucErrInfo *eInfo) {xMutex.Lock(); - memcpy(p, &Free, sizeof(p)); - Free = p; + Next = Free; + Free = this; xMutex.UnLock(); } - XrdBwmHandleCB() {} +int Same(unsigned long long arg1, unsigned long long arg2) {return 0;} + + XrdBwmHandleCB() : Next(0) {} ~XrdBwmHandleCB() {} private: - -static XrdSysMutex xMutex; -static void *Free; + XrdBwmHandleCB *Next; +static XrdSysMutex xMutex; +static XrdBwmHandleCB *Free; }; -XrdSysMutex XrdBwmHandleCB::xMutex; -void *XrdBwmHandleCB::Free = 0; +XrdSysMutex XrdBwmHandleCB::xMutex; +XrdBwmHandleCB *XrdBwmHandleCB::Free = 0; /******************************************************************************/ /* E x t e r n a l L i n k a g e s */ @@ -206,7 +209,7 @@ XrdBwmHandle *XrdBwmHandle::Alloc(XrdBwmHandle *old_hP) void *XrdBwmHandle::Dispatch() { EPNAME("Dispatch"); - XrdBwmHandleCB *erP = new XrdBwmHandleCB; + XrdBwmHandleCB *erP = XrdBwmHandleCB::Alloc(); XrdBwmHandle *hP; char *RespBuff; int RespSize, readyH, Result, Err; @@ -244,7 +247,7 @@ void *XrdBwmHandle::Dispatch() if (!Err) Policy->Done(readyH); } else { hP->myEICB.Wait(); hP->rTime = time(0); - erP->setErrCB(hP->ErrCB, hP->ErrCBarg); + erP->setErrCB((XrdOucEICB *)erP, hP->ErrCBarg); if (Err) {hP->Status = Idle; Result = SFS_ERROR;} else {hP->Status = Dispatched; erP->setErrCode(strlen(RespBuff)); @@ -254,7 +257,7 @@ void *XrdBwmHandle::Dispatch() <<(hP->Parms.Direction == XrdBwmPolicy::Incomming ? " <- ":" -> ") <<hP->Parms.RmtNode); hP->ErrCB->Done(Result, (XrdOucErrInfo *)erP); - erP = new XrdBwmHandleCB; + erP = XrdBwmHandleCB::Alloc(); } hP->hMutex.UnLock(); } while(1); diff --git a/net/xrootd/src/xrootd/src/XrdClient/GNUmakefile b/net/xrootd/src/xrootd/src/XrdClient/GNUmakefile index 096cec059ce8c2172f7515d75e9c206f0ad84ea9..624549a7a1c1fbba1dcf6cab37beef42220b54c6 100644 --- a/net/xrootd/src/xrootd/src/XrdClient/GNUmakefile +++ b/net/xrootd/src/xrootd/src/XrdClient/GNUmakefile @@ -1,4 +1,4 @@ -# $Id: GNUmakefile,v 1.65 2009/09/18 14:46:48 furano Exp $ +# $Id: GNUmakefile,v 1.67 2010/01/07 14:27:38 ganis Exp $ #-----------------------------------------------------------------------------# # E n v i r o n m e n t # @@ -204,7 +204,7 @@ $(XRDSTAGETOOL): $(OBJECTS) $(OBJECT_STAGETOOL) $(LIBDEPS) $(XRDCLI): $(OBJECTS) $(OBJECT_CLI) $(LIBDEPS) @echo Creating executable $(XRDCLI) - $(ECHO)$(LD) $(LDOP) $(OBJECT_CLI) $(BINLIBS) $(LIBM) $(LIBS) -o $(XRDCLI) + $(ECHO)$(LD) $(LDOP) $(OBJECT_CLI) $(BINLIBS) $(LIBM) $(LIBS) $(LIBREADLINE) -o $(XRDCLI) $(XRDADMIN): $(PERLMOD) $(ECHO)if [ "$(PERLBIN)" != "" ]; then \ @@ -583,13 +583,13 @@ $(OBJDIR)/XrdCommandLine.o: XrdCommandLine.cc ../XrdClient/XrdClientUrlInfo.hh \ ../XrdClient/XrdClientSock.hh ../XrdClient/XrdClientConn.hh \ ../XrdClient/XrdClientConst.hh ../XrdClient/XrdClientReadCache.hh \ ../XrdClient/XrdClientInputBuffer.hh ../XrdSys/XrdSysSemWait.hh \ - ../XrdOuc/XrdOucHash.hh ../XrdOuc/XrdOucHash.icc ../XrdOuc/XrdOucString.hh \ + ../XrdOuc/XrdOucHash.hh ../XrdOuc/XrdOucHash.icc ../XrdOuc/XrdOucString.hh \ ../XrdClient/XrdClientVector.hh ../XrdClient/XrdClientPSock.hh \ ../XrdClient/XrdClientDebug.hh ../XrdClient/XrdClientEnv.hh \ ../XrdOuc/XrdOucEnv.hh ../XrdSys/XrdSysHeaders.hh ../XrdSys/XrdSysLogger.hh \ ../XrdClient/XrdClientAdmin.hh @echo Compiling XrdCommandLine.cc - $(ECHO)$(CC) -g -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdCommandLine.o XrdCommandLine.cc + $(ECHO)$(CC) -g -c $(CFLAGS) $(CFREADLINE) $(INCLUDE) -o $(OBJDIR)/XrdCommandLine.o XrdCommandLine.cc $(OBJDIR)/XrdClientEnv.o: XrdClientEnv.cc ../XrdClient/XrdClientConst.hh \ ../XrdClient/XrdClientEnv.hh ../XrdOuc/XrdOucEnv.hh \ diff --git a/net/xrootd/src/xrootd/src/XrdClient/TestXrdClient_read.cc b/net/xrootd/src/xrootd/src/XrdClient/TestXrdClient_read.cc index 9cb132d434fbf823532f7ae8b3ec81830fa5984e..a8dbdcf93464dea8444e6f85abeb08418bf18b5d 100644 --- a/net/xrootd/src/xrootd/src/XrdClient/TestXrdClient_read.cc +++ b/net/xrootd/src/xrootd/src/XrdClient/TestXrdClient_read.cc @@ -4,12 +4,28 @@ #include "XrdClient/XrdClient.hh" #include "XrdClient/XrdClientEnv.hh" #include "XrdSys/XrdSysHeaders.hh" +#include "XrdClient/XrdClientCallback.hh" #include <fstream> #include <vector> #include <string> #include <sys/time.h> #include <math.h> + + + +class MyXrdClientCallback: public XrdClientCallback { + + virtual void OpenComplete(XrdClientAbs *clientP, void *cbArg, bool res) { + cout << "OpenComplete! res:" << res << endl; + } + +}; + + + + + kXR_unt16 open_mode = (kXR_ur | kXR_uw); kXR_unt16 open_opts = (0); @@ -199,7 +215,8 @@ int main(int argc, char **argv) { kXR_int32 v_lens[20480]; if (isrooturl) { - XrdClient *cli = new XrdClient(argv[1]); + MyXrdClientCallback mycb; + XrdClient *cli = new XrdClient(argv[1], &mycb, (void *)1234); cli->Open(open_mode, open_opts | ( (vectored_style > 4) ? kXR_delete : 0 ) ); filezcount = 1; diff --git a/net/xrootd/src/xrootd/src/XrdClient/XrdClient.cc b/net/xrootd/src/xrootd/src/XrdClient/XrdClient.cc index 88887af80151cadfd00261f7cb425e28fa321ae6..b70c5c9905852d60776e0689239f221869ee4d9f 100644 --- a/net/xrootd/src/xrootd/src/XrdClient/XrdClient.cc +++ b/net/xrootd/src/xrootd/src/XrdClient/XrdClient.cc @@ -26,6 +26,7 @@ const char *XrdClientCVSID = "$Id$"; #include "XrdClient/XrdClientReadV.hh" #include "XrdOuc/XrdOucCRC.hh" #include "XrdClient/XrdClientReadAhead.hh" +#include "XrdClient/XrdClientCallback.hh" #include <stdio.h> #ifndef WIN32 @@ -50,14 +51,19 @@ void *FileOpenerThread(void *arg, XrdClientThread *thr) { thr->SetCancelDeferred(); thr->SetCancelOn(); - thisObj->TryOpen(thisObj->fOpenPars.mode, thisObj->fOpenPars.options, false); + + bool res = thisObj->TryOpen(thisObj->fOpenPars.mode, thisObj->fOpenPars.options, false); + if (thisObj->fXrdCcb) thisObj->fXrdCcb->OpenComplete(thisObj, thisObj->fXrdCcbArg, res); return 0; } //_____________________________________________________________________________ -XrdClient::XrdClient(const char *url) { +XrdClient::XrdClient(const char *url, + XrdClientCallback *XrdCcb, + void *XrdCcbArg) : XrdClientAbs(XrdCcb, XrdCcbArg) { + fReadAheadMgr = 0; fReadTrimBlockSize = 0; fOpenerTh = 0; @@ -74,7 +80,7 @@ XrdClient::XrdClient(const char *url) { if (!ConnectionManager) Info(XrdClientDebug::kUSERDEBUG, "Create", - "(C) 2004-2010 by the Xrootd group. XrdClient $Revision: 1.152 $ - Xrootd version: " << XrdVSTRING); + "(C) 2004-2010 by the Xrootd group. XrdClient $Revision: 1.154 $ - Xrootd version: " << XrdVSTRING); #ifndef WIN32 signal(SIGPIPE, SIG_IGN); @@ -361,6 +367,9 @@ bool XrdClient::Open(kXR_unt16 mode, kXR_unt16 options, bool doitparallel) { fUrl.File << " on host " << fUrl.Host << ":" << fUrl.Port); + if (fXrdCcb && !doitparallel) + fXrdCcb->OpenComplete(this, fXrdCcbArg, false); + return FALSE; } else { @@ -368,8 +377,11 @@ bool XrdClient::Open(kXR_unt16 mode, kXR_unt16 options, bool doitparallel) { if (doitparallel) { Info(XrdClientDebug::kUSERDEBUG, "Open", "File open in progress."); } - else + else { Info(XrdClientDebug::kUSERDEBUG, "Open", "File opened succesfully."); + if (fXrdCcb) + fXrdCcb->OpenComplete(this, fXrdCcbArg, true); + } } @@ -986,6 +998,7 @@ bool XrdClient::TryOpen(kXR_unt16 mode, kXR_unt16 options, bool doitparallel) { // otherwise return FALSE if (fConnModule->LastServerResp.status != kXR_NotFound) { TerminateOpenAttempt(); + return FALSE; } @@ -1018,12 +1031,14 @@ bool XrdClient::TryOpen(kXR_unt16 mode, kXR_unt16 options, bool doitparallel) { XrdClientMStream::EstablishParallelStreams(fConnModule); TerminateOpenAttempt(); + return TRUE; } else { Error("Open", "Error opening the file."); TerminateOpenAttempt(); + return FALSE; } @@ -1180,7 +1195,10 @@ bool XrdClient::Close() { } ClientRequest closeFileRequest; - + + // Set the max transaction duration + fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT)); + memset(&closeFileRequest, 0, sizeof(closeFileRequest) ); fConnModule->SetSID(closeFileRequest.header.streamid); diff --git a/net/xrootd/src/xrootd/src/XrdClient/XrdClient.hh b/net/xrootd/src/xrootd/src/XrdClient/XrdClient.hh index bac4dfb5cad3cc2f6ae90df4cf9ba3d39828b9c5..60354c18d78c9bd0ac4d21278f22b745db967b68 100644 --- a/net/xrootd/src/xrootd/src/XrdClient/XrdClient.hh +++ b/net/xrootd/src/xrootd/src/XrdClient/XrdClient.hh @@ -177,7 +177,7 @@ protected: public: - XrdClient(const char *url); + XrdClient(const char *url, XrdClientCallback *XrdCcb = 0, void *XrdCcbArg = 0); virtual ~XrdClient(); UnsolRespProcResult ProcessUnsolicitedMsg(XrdClientUnsolMsgSender *sender, diff --git a/net/xrootd/src/xrootd/src/XrdClient/XrdClientAbs.hh b/net/xrootd/src/xrootd/src/XrdClient/XrdClientAbs.hh index b211b0d63504a6fe29a82c8efa353a454a9d16d5..74cd9f1220e8f33e2a53ec69f7f9880302a49e4f 100644 --- a/net/xrootd/src/xrootd/src/XrdClient/XrdClientAbs.hh +++ b/net/xrootd/src/xrootd/src/XrdClient/XrdClientAbs.hh @@ -19,6 +19,8 @@ #include "XrdClient/XrdClientUnsolMsg.hh" #include "XrdClient/XrdClientConn.hh" +class XrdClientCallback; + class XrdClientAbs: public XrdClientAbsUnsolMsgHandler { // Do NOT abuse of this @@ -30,6 +32,11 @@ protected: char fHandle[4]; // The file handle returned by the server, // to be used for successive requests + + + XrdClientCallback* fXrdCcb; + void * fXrdCcbArg; + // After a redirection the file must be reopened. virtual bool OpenFileWhenRedirected(char *newfhandle, bool &wasopen) = 0; @@ -40,8 +47,12 @@ protected: public: - XrdClientAbs() { + XrdClientAbs(XrdClientCallback *XrdCcb = 0, void *XrdCcbArg = 0) { memset( fHandle, 0, sizeof(fHandle) ); + + // Set the callback object, if any + fXrdCcb = XrdCcb; + fXrdCcbArg = XrdCcbArg; } virtual bool IsOpen_wait() { diff --git a/net/xrootd/src/xrootd/src/XrdClient/XrdClientAdmin.cc b/net/xrootd/src/xrootd/src/XrdClient/XrdClientAdmin.cc index ed54b3ed1993ec8255f16a0407f51cc60b5def07..15ef41e836c4789aeeeedc213887a5b710fae436 100644 --- a/net/xrootd/src/xrootd/src/XrdClient/XrdClientAdmin.cc +++ b/net/xrootd/src/xrootd/src/XrdClient/XrdClientAdmin.cc @@ -871,7 +871,166 @@ bool XrdClientAdmin::Prepare(const char *buf, kXR_char option, kXR_char prty) } //_____________________________________________________________________________ -bool XrdClientAdmin::DirList(const char *dir, vecString &entries) { +bool XrdClientAdmin::DirList(const char *dir, vecString &entries, bool askallservers) { + // Get an ls-like output with respect to the specified dir + + // If this is a redirector, we will be given the list of the servers + // which host this directory + // If askallservers is true then we will just ask for the whole list of servers. + // the query will always be the same, and this will likely skip the 5s delay after the first shot + // The danger is to be forced to contact a huge number of servers in very big clusters + // + bool ret = true; + XrdClientVector<XrdClientLocate_Info> hosts; + if (askallservers && (fConnModule->GetServerProtocol() >= 0x291)) { + char str[1024]; + strcpy(str, "*"); + strncat(str, dir, 1023); + if (!Locate((kXR_char *)str, hosts)) return false; + } + else { + XrdClientLocate_Info nfo; + memset(&nfo, 0, sizeof(nfo)); + strcpy((char *)nfo.Location, GetCurrentUrl().HostWPort.c_str()); + hosts.Push_back(nfo); + } + + + // Then we cycle among them asking everyone + bool foundsomething = false; + for (int i = 0; i < hosts.GetSize(); i++) { + + fConnModule->Disconnect(false); + XrdClientUrlInfo url((const char *)hosts[i].Location); + + url.Proto = "root"; + + if (fConnModule->GoToAnotherServer(url) != kOK) { + ret = false; + break; + } + + fConnModule->ClearLastServerError(); + if (!DirList_low(dir, entries)) { + if (fConnModule->LastServerError.errnum != kXR_NotFound) { + ret = false; + break; + } + } + else foundsomething = true; + + + } + + // At the end we want to rewind to the main redirector in any case + GoBackToRedirector(); + + if (!foundsomething) ret = false; + return ret; +} + +//_____________________________________________________________________________ +bool XrdClientAdmin::DirList(const char *dir, + XrdClientVector<XrdClientAdmin::DirListInfo> &dirlistinfo, + bool askallservers) { + // Get an ls-like output with respect to the specified dir + // Here we are also interested in the stat information for each file + + // If this is a redirector, we will be given the list of the servers + // which host this directory + // If askallservers is true then we will just ask for the whole list of servers. + // the query will always be the same, and this will likely skip the 5s delay after the first shot + // The danger is to be forced to contact a huge number of servers in very big clusters + // If this is a concern, one should set askallservers to false + // + bool ret = true; + vecString entries; + XrdClientVector<XrdClientLocate_Info> hosts; + XrdOucString fullpath; + + if (askallservers && (fConnModule->GetServerProtocol() >= 0x291)) { + char str[1024]; + strcpy(str, "*"); + strncat(str, dir, 1023); + if (!Locate((kXR_char *)str, hosts)) return false; + } + else { + XrdClientLocate_Info nfo; + memset(&nfo, 0, sizeof(nfo)); + strcpy((char *)nfo.Location, GetCurrentUrl().HostWPort.c_str()); + hosts.Push_back(nfo); + } + + + // Then we cycle among them asking everyone + bool foundsomething = false; + for (int i = 0; i < hosts.GetSize(); i++) { + + fConnModule->Disconnect(false); + XrdClientUrlInfo url((const char *)hosts[i].Location); + url.Proto = "root"; + + if (fConnModule->GoToAnotherServer(url) != kOK) { + ret = false; + break; + } + + fConnModule->ClearLastServerError(); + + int precentries = entries.GetSize(); + if (!DirList_low(dir, entries)) { + if ((fConnModule->LastServerError.errnum != kXR_NotFound) && (fConnModule->LastServerError.errnum != kXR_noErrorYet)) { + ret = false; + break; + } + } + else foundsomething = true; + + int newentries = entries.GetSize(); + + DirListInfo info; + dirlistinfo.Resize(newentries); + + // Here we have the entries. We want to accumulate the stat information for each of them + // We are still connected to the same server which gave the last dirlist response + info.host = GetCurrentUrl().HostWPort; + for (int k = precentries; k < newentries; k++) { + info.fullpath = dir; + if (info.fullpath[info.fullpath.length()-1] != '/') info.fullpath += "/"; + info.fullpath += entries[k]; + info.size = 0; + info.id = 0; + info.flags = 0; + info.modtime = 0; + + if (!Stat(info.fullpath.c_str(), + info.id, + info.size, + info.flags, + info.modtime)) { + ret = false; + //break; + } + + dirlistinfo[k] = info; + + } + + } + + // At the end we want to rewind to the main redirector in any case + GoBackToRedirector(); + + if (!foundsomething) ret = false; + return ret; + } + + + + + +//_____________________________________________________________________________ +bool XrdClientAdmin::DirList_low(const char *dir, vecString &entries) { bool ret; // asks the server for the content of a directory ClientRequest DirListFileRequest; @@ -893,25 +1052,26 @@ bool XrdClientAdmin::DirList(const char *dir, vecString &entries) { // Now parse the answer building the entries vector if (ret) { - kXR_char *entry, *startp = dl, *endp = dl; + kXR_char *startp = dl, *endp = dl; + char entry[1024]; + XrdOucString e; - while (endp) { + while (startp) { if ( (endp = (kXR_char *)strchr((const char*)startp, '\n')) ) { - entry = (kXR_char *)malloc(endp-startp+1); - memset((char *)entry, 0, endp-startp+1); - strncpy((char *)entry, (char *)startp, endp-startp); + strncpy(entry, (char *)startp, endp-startp); + entry[endp-startp] = 0; endp++; } else - entry = (kXR_char *)strdup((char *)startp); + strcpy(entry, (char *)startp); - if (entry && strlen((char *)entry)) { - XrdOucString e((const char *)entry); + if (strlen(entry) && strcmp((char *)entry, ".") && strcmp((char *)entry, "..")) { + e = entry; entries.Push_back(e); - free(entry); - } + } + startp = endp; } @@ -925,7 +1085,6 @@ bool XrdClientAdmin::DirList(const char *dir, vecString &entries) { } - //_____________________________________________________________________________ long XrdClientAdmin::GetChecksum(kXR_char *path, kXR_char **chksum) { @@ -1078,7 +1237,7 @@ bool XrdClientAdmin::Locate(kXR_char *path, XrdClientLocate_Info &resp, bool wri resp.CanWrite = 1; strcpy((char *)resp.Location, fConnModule->GetCurrentUrl().HostWPort.c_str()); } - fConnModule->GoBackToRedirector(); + GoBackToRedirector(); return ok; } @@ -1164,7 +1323,7 @@ bool XrdClientAdmin::Locate(kXR_char *path, XrdClientLocate_Info &resp, bool wri } // At the end we want to rewind to the main redirector in any case - fConnModule->GoBackToRedirector(); + GoBackToRedirector(); return found; } @@ -1201,7 +1360,8 @@ bool XrdClientAdmin::Locate(kXR_char *path, XrdClientVector<XrdClientLocate_Info strcpy((char *)resp.Location, fConnModule->GetCurrentUrl().HostWPort.c_str()); hosts.Push_back(resp); } - fConnModule->GoBackToRedirector(); + GoBackToRedirector(); + return ok; } @@ -1258,7 +1418,7 @@ bool XrdClientAdmin::Locate(kXR_char *path, XrdClientVector<XrdClientLocate_Info } // At the end we want to rewind to the main redirector in any case - fConnModule->GoBackToRedirector(); + GoBackToRedirector(); return (hosts.GetSize() > 0); } @@ -1295,9 +1455,137 @@ bool XrdClientAdmin::Truncate(const char *path, long long newsize) { // Quickly jump to the former redirector. Useful after having been redirected. void XrdClientAdmin::GoBackToRedirector() { - if (fConnModule) - fConnModule->GoBackToRedirector(); + if (fConnModule) { + fConnModule->GoBackToRedirector(); + + if (!fConnModule->IsConnected()) { + XrdClientUrlInfo u(fInitialUrl); + fConnModule->GoToAnotherServer(u); + } + + } + + + +} + + + +// Compute an estimation of the available free space in the given cachefs partition +// The estimation can be fooled if multiple servers mount the same network storage +bool XrdClientAdmin::GetSpaceInfo(const char *logicalname, + long long &totspace, + long long &totfree, + long long &totused, + long long &largestchunk) { + + bool ret = true; + XrdClientVector<XrdClientLocate_Info> hosts; + + totspace = 0; + totfree = 0; + totused = 0; + largestchunk = 0; + + if (fConnModule->GetServerProtocol() >= 0x291) { + if (!Locate((kXR_char *)"*", hosts)) return false; + } + else { + XrdClientLocate_Info nfo; + memset(&nfo, 0, sizeof(nfo)); + strcpy((char *)nfo.Location, GetCurrentUrl().HostWPort.c_str()); + hosts.Push_back(nfo); + } + + + // Then we cycle among them asking everyone + for (int i = 0; i < hosts.GetSize(); i++) { + + fConnModule->Disconnect(false); + XrdClientUrlInfo url((const char *)hosts[i].Location); + url.Proto = "root"; + + if (fConnModule->GoToAnotherServer(url) != kOK) { + ret = false; + break; + } + + + + // Fire the query request and update the results + ClientRequest qspacereq; + + // Set the max transaction duration + fConnModule->SetOpTimeLimit(EnvGetLong(NAME_TRANSACTIONTIMEOUT)); + + + memset( &qspacereq, 0, sizeof(qspacereq) ); + + fConnModule->SetSID(qspacereq.header.streamid); + + qspacereq.query.requestid = kXR_query; + qspacereq.query.infotype = kXR_Qspace; + qspacereq.query.dlen = ( logicalname ? strlen(logicalname) : 0); + + char *resp = 0; + if (fConnModule->SendGenCommand(&qspacereq, logicalname, + (void **)&resp, 0, TRUE, + (char *)"GetSpaceInfo")) { + + XrdOucString rs(resp), s; + free(resp); + + // Here we have the response relative to a server + // Now we are going to have fun in parsing it + + int from = 0; + while ((from = rs.tokenize(s,from,'&')) != -1) { + if (s.length() < 4) continue; + + int pos = s.find("="); + XrdOucString tk, val; + if (pos != STR_NPOS) { + tk.assign(s, 0, pos-1); + val.assign(s, pos+1); +#ifndef WIN32 + if ( (tk == "oss.space") && (val.length() > 1) ) { + totspace += atoll(val.c_str()); + } else + if ( (tk == "oss.free") && (val.length() > 1) ) { + totfree += atoll(val.c_str()); + } else + if ( (tk == "oss.maxf") && (val.length() > 1) ) { + largestchunk = xrdmax(largestchunk, atoll(val.c_str())); + } else + if ( (tk == "oss.used") && (val.length() > 1) ) { + totused += atoll(val.c_str()); + } +#else + if ( (tk == "oss.space") && (val.length() > 1) ) { + totspace += _atoi64(val.c_str()); + } else + if ( (tk == "oss.free") && (val.length() > 1) ) { + totfree += _atoi64(val.c_str()); + } else + if ( (tk == "oss.maxf") && (val.length() > 1) ) { + largestchunk = xrdmax(largestchunk, _atoi64(val.c_str())); + } else + if ( (tk == "oss.used") && (val.length() > 1) ) { + totused += _atoi64(val.c_str()); + } +#endif + } + } + + + } + + } + + // At the end we want to rewind to the main redirector in any case + GoBackToRedirector(); + return ret; } diff --git a/net/xrootd/src/xrootd/src/XrdClient/XrdClientAdmin.hh b/net/xrootd/src/xrootd/src/XrdClient/XrdClientAdmin.hh index 1ac0813bd02db08c9b2d9213b274a85f0fb3bd90..3292a6fb939526bc3c1aea6afca366eae1b0a065 100644 --- a/net/xrootd/src/xrootd/src/XrdClient/XrdClientAdmin.hh +++ b/net/xrootd/src/xrootd/src/XrdClient/XrdClientAdmin.hh @@ -45,7 +45,7 @@ struct XrdClientLocate_Info { class XrdClientAdmin : public XrdClientAbs { XrdOucString fInitialUrl; - + bool DirList_low(const char *dir, vecString &entries); int LocalLocate(kXR_char *path, XrdClientVector<XrdClientLocate_Info> &res, bool writable, bool nowait, bool all = false); @@ -85,7 +85,19 @@ class XrdClientAdmin : public XrdClientAbs { int &stagingutil); bool DirList(const char *dir, - vecString &); + vecString &entries, bool askallservers=false); + + struct DirListInfo { + XrdOucString fullpath; + XrdOucString host; + long long size; + long id; + long flags; + long modtime; + }; + bool DirList(const char *dir, + XrdClientVector<DirListInfo> &dirlistinfo, + bool askallservers=false); bool ExistFiles(vecString&, vecBool&); @@ -93,6 +105,14 @@ class XrdClientAdmin : public XrdClientAbs { bool ExistDirs(vecString&, vecBool&); + // Compute an estimation of the available free space in the given cachefs partition + // The estimation can be fooled if multiple servers mount the same network storage + bool GetSpaceInfo(const char *logicalname, + long long &totspace, + long long &totfree, + long long &totused, + long long &largestchunk); + long GetChecksum(kXR_char *path, kXR_char **chksum); diff --git a/net/xrootd/src/xrootd/src/XrdClient/XrdClientCallback.hh b/net/xrootd/src/xrootd/src/XrdClient/XrdClientCallback.hh new file mode 100644 index 0000000000000000000000000000000000000000..01ac8fdc1b6c74171aa3bf5082f86ed1586033e4 --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdClient/XrdClientCallback.hh @@ -0,0 +1,32 @@ +////////////////////////////////////////////////////////////////////////// +// // +// XrdClientCallback // +// // +// Author: Fabrizio Furano (CERN IT-DSS, 2009) // +// // +// Base class for objects receiving events from XrdClient // +// // +////////////////////////////////////////////////////////////////////////// + +// $Id$ + +#ifndef XRD_CLIENTCALLBACK_H +#define XRD_CLIENTCALLBACK_H + +class XrdClientAbs; + +class XrdClientCallback +{ + +public: + + // Invoked when an Open request completes with some result. + virtual void OpenComplete(XrdClientAbs *clientP, void *cbArg, bool res) = 0; + + XrdClientCallback() {} + virtual ~XrdClientCallback() {} +}; + + + +#endif diff --git a/net/xrootd/src/xrootd/src/XrdClient/XrdClientConn.cc b/net/xrootd/src/xrootd/src/XrdClient/XrdClientConn.cc index 6c0baaff81fb687d9b3f87b94333bc527527f496..26273cea3a25d3347a4f83598725c4fbfd193975 100644 --- a/net/xrootd/src/xrootd/src/XrdClient/XrdClientConn.cc +++ b/net/xrootd/src/xrootd/src/XrdClient/XrdClientConn.cc @@ -904,7 +904,8 @@ bool XrdClientConn::CheckErrorStatus(XrdClientMessage *mex, short &Retry, char * fOpenError = (XErrorCode)ntohl(body_err->errnum); - Info(XrdClientDebug::kNODEBUG, "CheckErrorStatus", "Server declared: " << + Info(XrdClientDebug::kNODEBUG, "CheckErrorStatus", "Server [" << GetCurrentUrl().HostWPort << + "] declared: " << (const char*)body_err->errmsg << "(error code: " << fOpenError << ")"); // Save the last error received @@ -2050,6 +2051,10 @@ XReqErrorType XrdClientConn::GoToAnotherServer(XrdClientUrlInfo &newdest) // Re-directs to another server fGettingAccessToSrv = false; + + if (!newdest.Port) newdest.Port = 1094; + if (newdest.HostAddr == "") newdest.HostAddr = newdest.Host; + if ( (fLogConnID = Connect( newdest, fUnsolMsgHandler)) == -1) { // Note: if Connect is unable to work then we are in trouble. diff --git a/net/xrootd/src/xrootd/src/XrdClient/XrdClientReadV.cc b/net/xrootd/src/xrootd/src/XrdClient/XrdClientReadV.cc index adf5e43da8ac277012c6082285c1528d8da24434..d643dba3abfa701371576882f43690ad64a428b6 100644 --- a/net/xrootd/src/xrootd/src/XrdClient/XrdClientReadV.cc +++ b/net/xrootd/src/xrootd/src/XrdClient/XrdClientReadV.cc @@ -38,6 +38,7 @@ kXR_int64 XrdClientReadV::ReqReadV(XrdClientConn *xrdc, char *handle, char *dest memcpy( &(buflis[i].fhandle), handle, 4 ); + if (!destbuf) xrdc->SubmitPlaceholderToCache(reqvect[firstreq+i].offset, reqvect[firstreq+i].offset + diff --git a/net/xrootd/src/xrootd/src/XrdClient/XrdClientVector.hh b/net/xrootd/src/xrootd/src/XrdClient/XrdClientVector.hh index 17db0151d50b8bc51220ea084f318bdff17c9812..fada782b0b6c4053d8cc291bcd66aadfcfb22389 100644 --- a/net/xrootd/src/xrootd/src/XrdClient/XrdClientVector.hh +++ b/net/xrootd/src/xrootd/src/XrdClient/XrdClientVector.hh @@ -160,7 +160,20 @@ public: } void Resize(int newsize) { - BufRealloc(newsize); + long oldsize = size; + + if (newsize > oldsize) { + BufRealloc(newsize); + T *item = new T; + // Add new elements if needed + for (long i = oldsize; i < newsize; i++) { + put(*item, size++); + } + } + else { + for (long i = oldsize; i > newsize; i--) + Erase(i-1, false); + } } void Push_back(T& item) { diff --git a/net/xrootd/src/xrootd/src/XrdClient/XrdCommandLine.cc b/net/xrootd/src/xrootd/src/XrdClient/XrdCommandLine.cc index 75a022375c9b9568640ac3c0bc60d1769cb166ea..8b16761fee4f347dd598e6f77889420ef699477f 100644 --- a/net/xrootd/src/xrootd/src/XrdClient/XrdCommandLine.cc +++ b/net/xrootd/src/xrootd/src/XrdClient/XrdCommandLine.cc @@ -25,6 +25,7 @@ const char *XrdCommandLineCVSID = "$Id$"; #include <unistd.h> #include <stdarg.h> #include <sstream> +#include <signal.h> #ifdef HAVE_READLINE #include <readline/readline.h> @@ -71,7 +72,7 @@ extern "C" { -#define XRDCLI_VERSION "(C) 2004-2010 by the Xrootd group. $Revision: 1.24 $ - Xrootd version: "XrdVSTRING +#define XRDCLI_VERSION "(C) 2004-2010 by the Xrootd group. $Revision: 1.33 $ - Xrootd version: "XrdVSTRING /////////////////////////////////////////////////////////////////////// @@ -91,12 +92,21 @@ char *initialhost; XrdClient *genclient = 0; XrdClientAdmin *genadmin = 0; -XrdOucString currentpath; +XrdOucString currentpath = "/"; XrdOucString cmdline_cmd; /////////////////////// + +void +CtrlCHandler(int sig) { + cerr << endl << "Please use 'exit' to terminate this program." << endl; + return; +} + + + void PrintUsage() { cerr << "usage: xrd [host]" @@ -144,6 +154,8 @@ void PrintHelp() { cout << endl << XRDCLI_VERSION << endl << endl << + "Usage: xrd [-O<opaque_info>] [-DS<var_name> stringvalue] [-DI<var_name> integervalue] [host[:port]] [batchcommand]" << endl << endl << + "List of available commands:" << endl << " cat <filename> [xrdcp parameters]" << endl << " outputs a file on standard output using xrdcp. <filename> can be a root:// URL." << endl << @@ -159,6 +171,8 @@ void PrintHelp() { " current remote path. Also, they can be root:// URLs specifying any other host." << endl << " dirlist [dirname]" << endl << " gets the requested directory listing." << endl << + " dirlistrec [dirname]" << endl << + " gets the requested recursive directory listing." << endl << " envputint <varname> <intval>" << endl << " puts an integer in the internal environment." << endl << " envputstring <varname> <stringval>" << endl << @@ -198,6 +212,8 @@ void PrintHelp() { " stages a file in." << endl << " query <reqcode> <parms>" << endl << " obtain server information" << endl << + " queryspace <logicalname>" << endl << + " obtain space information" << endl << endl << "For further information, please read the xrootd protocol documentation." << endl << endl; @@ -263,6 +279,7 @@ void PrintLocateInfo(XrdClientLocate_Info &loc) { int main(int argc, char**argv) { int retval = 0; + //signal(SIGINT, CtrlCHandler); DebugSetLevel(0); @@ -285,7 +302,8 @@ int main(int argc, char**argv) { continue; } - if ( (strstr(argv[i], "-h") == argv[i])) { + if ( (strstr(argv[i], "-h") == argv[i]) || + (strstr(argv[i], "--help") == argv[i]) ) { PrintUsage(); exit(0); continue; @@ -381,19 +399,33 @@ int main(int argc, char**argv) { // Quite trivial directory processing if (!strcmp(parmname, "..")) { + if (currentpath == "/") continue; + int pos = currentpath.rfind('/'); if (pos != STR_NPOS) currentpath.erase(pos); - retval = 1; - } + if (!currentpath.length()) { + currentpath = "/"; + } - if (!strcmp(parmname, ".")) retval = 1; + continue; + } + else + if (!strcmp(parmname, ".")) { + retval = 1; + continue; + } - currentpath += "/"; - currentpath += parmname; + if (!currentpath.length() || (currentpath[currentpath.length()-1] != '/')) { + currentpath += "/"; + } + + if (parmname[0] == '/') currentpath = parmname; + else + currentpath += parmname; } else @@ -471,6 +503,122 @@ int main(int argc, char**argv) { } else + // -------------------------- dirlistrec --------------------------- + if (!strcmp(cmd, "dirlistrec")) { + XrdClientVector<XrdOucString> pathq; + + if (!genadmin) { + cout << "Not connected to any server." << endl; + retval = 1; + } + + char *dirname = tkzer.GetToken(0, 0); + XrdOucString path; + + if (dirname) { + if (dirname[0] == '/') + path = dirname; + else { + if ((currentpath.length() > 0) && (currentpath[currentpath.length()-1] != '/')) + path = currentpath + "/" + dirname; + else + path = currentpath + dirname; + + } + } + else path = currentpath; + + if (!path.length()) { + cout << "The current path is an empty string. Assuming '/'." << endl; + path = '/'; + } + + + // Initialize the queue with this path + pathq.Push_back(path); + + + while (pathq.GetSize() > 0) { + XrdOucString pathtodo = pathq.Pop_back(); + + // Now try to issue the request + XrdClientVector<XrdClientAdmin::DirListInfo> nfo; + if (!genadmin->DirList(pathtodo.c_str(), nfo, true)) { + retval = 1; + cout << "Error listing path '" << pathtodo << "' in server " << + genadmin->GetCurrentUrl().HostWPort << + " The path does not exist in some servers or there are malformed filenames." << endl; + } + + // Now check the answer + if (!CheckAnswer(genadmin)) { + retval = 1; + cout << "Error '" << genadmin->LastServerError()->errmsg << + "' listing path '" << pathtodo << + "' in server" << genadmin->GetCurrentUrl().HostWPort << + " or in some of its child nodes." << endl; + } + + for (int i = 0; i < nfo.GetSize(); i++) { + + if ((nfo[i].flags & kXR_isDir) && + (nfo[i].flags & kXR_readable) && + (nfo[i].flags & kXR_xset)) { + + + // The path has not to be pushed if it's already present + // This may happen if several servers have the same path + bool foundpath = false; + for (int ii = 0; ii < pathq.GetSize(); ii++) { + if (nfo[i].fullpath == pathq[ii]) { + foundpath = true; + break; + } + } + + if (!foundpath) + pathq.Push_back(nfo[i].fullpath); + else + // If the path is already present in the queue then it was already printed as well. + continue; + + } + + char ts[256]; + strcpy(ts, "n/a"); + + struct tm *t = gmtime(&nfo[i].modtime); + strftime(ts, 255, "%F %T", t); + + char cflgs[16]; + memset(cflgs, 0, 16); + + if (nfo[i].flags & kXR_isDir) + strcat(cflgs, "d"); + else strcat(cflgs, "-"); + + if (nfo[i].flags & kXR_readable) + strcat(cflgs, "r"); + else strcat(cflgs, "-"); + + if (nfo[i].flags & kXR_writable) + strcat(cflgs, "w"); + else strcat(cflgs, "-"); + + if (nfo[i].flags & kXR_xset) + strcat(cflgs, "x"); + else strcat(cflgs, "-"); + + printf("%s(%03ld) %12lld %s %s\n", cflgs, nfo[i].flags, nfo[i].size, ts, nfo[i].fullpath.c_str()); + + } + + if (nfo.GetSize()) cout << endl; + } + + if (retval) cout << "Errors during processing. Please check them." << endl; + } + else // -------------------------- dirlist --------------------------- if (!strcmp(cmd, "dirlist")) { @@ -485,28 +633,68 @@ int main(int argc, char**argv) { if (dirname) { if (dirname[0] == '/') path = dirname; - else - path = currentpath + "/" + dirname; + else { + if ((currentpath.length() > 0) && (currentpath[currentpath.length()-1] != '/')) + path = currentpath + "/" + dirname; + else + path = currentpath + dirname; + + } } else path = currentpath; if (!path.length()) { - cout << "The current path is empty." << endl; - retval = 1; + cout << "The current path is an empty string. Assuming '/'." << endl; + path = '/'; } // Now try to issue the request - vecString vs; - genadmin->DirList(path.c_str(), vs); + XrdClientVector<XrdClientAdmin::DirListInfo> nfo; + if (!genadmin->DirList(path.c_str(), nfo, true)) { + //nfo.Clear(); + retval = 1; + + } // Now check the answer - if (!CheckAnswer(genadmin)) + if (!CheckAnswer(genadmin)) { retval = 1; + //nfo.Clear(); + } - for (int i = 0; i < vs.GetSize(); i++) - cout << vs[i] << endl; - cout << endl; + for (int i = 0; i < nfo.GetSize(); i++) { + char ts[256]; + strcpy(ts, "n/a"); + + struct tm *t = gmtime(&nfo[i].modtime); + strftime(ts, 255, "%F %T", t); + + char cflgs[16]; + memset(cflgs, 0, 16); + + if (nfo[i].flags & kXR_isDir) + strcat(cflgs, "d"); + else strcat(cflgs, "-"); + + if (nfo[i].flags & kXR_readable) + strcat(cflgs, "r"); + else strcat(cflgs, "-"); + + if (nfo[i].flags & kXR_writable) + strcat(cflgs, "w"); + else strcat(cflgs, "-"); + + if (nfo[i].flags & kXR_xset) + strcat(cflgs, "x"); + else strcat(cflgs, "-"); + + printf("%s(%03ld) %12lld %s %s\n", cflgs, nfo[i].flags, nfo[i].size, ts, nfo[i].fullpath.c_str()); + + } + + if (retval) cout << "Errors during processing. Please check." << endl; + cout << endl; } else @@ -1276,6 +1464,36 @@ int main(int argc, char**argv) { cout << endl; + } + else + // -------------------------- queryspace --------------------------- + if (!strcmp(cmd, "queryspace")) { + + if (!genadmin) { + cout << "Not connected to any server." << endl; + retval = 1; + } + + char *ns = tkzer.GetToken(0, 0); + long long totspace; + long long totfree; + long long totused; + long long largestchunk; + + genadmin->GetSpaceInfo(ns, totspace, totfree, totused, largestchunk); + + // Now check the answer + if (!CheckAnswer(genadmin)) + retval = 1; + + cout << "Disk space approximations (MB):" << endl << + "Total : " << totspace/(1024*1024) << endl << + "Free : " << totfree/(1024*1024) << endl << + "Used : " << totused/(1024*1024) << endl << + "Largest chunk : " << largestchunk/(1024*1024) << endl; + + cout << endl; + } else { @@ -1286,7 +1504,7 @@ int main(int argc, char**argv) { } - free(linebuf); + delete[] linebuf; // if it was a cmd from the commandline... if (cmdline_cmd.length() > 0) break; diff --git a/net/xrootd/src/xrootd/src/XrdClient/Xrdcp.cc b/net/xrootd/src/xrootd/src/XrdClient/Xrdcp.cc index bd8a2cccd105186bf87cf08cf061ed2a986771d5..0883937b41404894ac33a0f0c0df26ace9a88b67 100644 --- a/net/xrootd/src/xrootd/src/XrdClient/Xrdcp.cc +++ b/net/xrootd/src/xrootd/src/XrdClient/Xrdcp.cc @@ -88,7 +88,7 @@ struct XrdCpInfo { #define XRDCP_BLOCKSIZE (4*1024*1024) #define XRDCP_XRDRASIZE (20*XRDCP_BLOCKSIZE) -#define XRDCP_VERSION "(C) 2004-2010 by the Xrootd group. $Revision: 1.99 $ - Xrootd version: "XrdVSTRING +#define XRDCP_VERSION "(C) 2004-2010 by the Xrootd group. $Revision: 1.100 $ - Xrootd version: "XrdVSTRING /////////////////////////////////////////////////////////////////////// // Coming from parameters on the cmd line @@ -662,12 +662,13 @@ int doCp_xrd2xrd(XrdClient **xrddest, const char *src, const char *dst) { cpnfo.mon->PutProgressInfo(bytesread, cpnfo.len, (float)bytesread / cpnfo.len * 100.0); free(buf); - } - if (!xrdxtrdfile && ((buf == 0) || (len == 0)) && (bytesread >= size)) { - if (buf) free(buf); - break; } + else + if (!xrdxtrdfile && ( ((buf == 0) && (len == 0)) || (bytesread >= size))) { + if (buf) free(buf); + break; + } } else { @@ -933,12 +934,13 @@ int doCp_xrd2loc(const char *src, const char *dst) { cpnfo.mon->PutProgressInfo(bytesread, cpnfo.len, (float)bytesread / cpnfo.len * 100.0); free(buf); - } - if (!xrdxtrdfile && ((buf == 0) || (len == 0)) && (bytesread >= size)) { - if (buf) free(buf); - break; - } + } + else + if (!xrdxtrdfile && ( ((buf == 0) && (len == 0)) || (bytesread >= size)) ) { + if (buf) free(buf); + break; + } } diff --git a/net/xrootd/src/xrootd/src/XrdCms/GNUmakefile b/net/xrootd/src/xrootd/src/XrdCms/GNUmakefile index cc02e27878622d331fbdfd02c2deec8ae695070e..918fe62f48cb9567c49837ab59b42bb6f466efd0 100644 --- a/net/xrootd/src/xrootd/src/XrdCms/GNUmakefile +++ b/net/xrootd/src/xrootd/src/XrdCms/GNUmakefile @@ -1,4 +1,4 @@ -# $Id: GNUmakefile,v 1.19 2009/11/03 02:52:19 abh Exp $ +# $Id: GNUmakefile,v 1.20 2010/01/05 01:16:38 abh Exp $ #------------------------------------------------------------------------------# # C o m m o n V a r i a b l e s # @@ -446,7 +446,7 @@ $(OBJDIR)/XrdCmsRRQ.o: XrdCmsRRQ.cc XrdCmsRRQ.hh \ $(ECHO)$(CC) -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdCmsRRQ.o XrdCmsRRQ.cc $(OBJDIR)/XrdCmsRTable.o: XrdCmsRTable.cc XrdCmsRTable.hh \ - XrdCmsNode.hh XrdCmsTrace.hh \ + XrdCmsNode.hh XrdCmsTrace.hh XrdCmsTypes.hh\ XrdSysPthread.hh @echo Compiling XrdCmsRTable.cc $(ECHO)$(CC) -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdCmsRTable.o XrdCmsRTable.cc diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsCache.cc b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsCache.cc index f977e4c90db4a23cef613bfadd299ab415c4770c..3d297557977533730f8ba7200b150a05e5a74338 100644 --- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsCache.cc +++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsCache.cc @@ -335,7 +335,7 @@ void XrdCmsCache::Bounce(SMask_t smask, int SNum) void XrdCmsCache::Drop(SMask_t smask, int SNum, int xHi) { - SMask_t nmask = ~smask; + SMask_t nmask(~smask); // Remove the node from the path list // @@ -451,7 +451,7 @@ void XrdCmsCache::Dispatch(XrdCmsKeyItem *iP, short roQ, short rwQ) SMask_t XrdCmsCache::getBVec(unsigned int TODa, unsigned int &TODb) { EPNAME("getBVec"); - SMask_t BVec = 0; + SMask_t BVec(0); long long i; // See if we can use a previously calculated bVec diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsClientMan.cc b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsClientMan.cc index cc97dd47bc88e88a6153b8517185dbff80859b35..11b8d5d689da10671e6cbb1bfd86d4d7e91a0056 100644 --- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsClientMan.cc +++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsClientMan.cc @@ -222,11 +222,10 @@ void *XrdCmsClientMan::Start() // must receive the respwait before the subsequent response. // while(Receive()) - if (Response.modifier & CmsResponse::kYR_async) relayResp(); - else if (Response.rrCode == kYR_status) setStatus(); - else {XrdCmsClientMsg::Reply(HPfx, Response, NetBuff); - if (Response.rrCode == kYR_waitresp) syncResp.Wait(); - } + if (Response.modifier & CmsResponse::kYR_async) relayResp(); + else if (Response.rrCode == kYR_status) setStatus(); + else if (XrdCmsClientMsg::Reply(HPfx, Response, NetBuff)) + {if (Response.rrCode == kYR_waitresp) syncResp.Wait();} // Tear down the connection // diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsCluster.cc b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsCluster.cc index 40454e5d7565018f4caae92b3427115cef90993a..423292184b0f0fb011843ea9606a82b578c75e62 100644 --- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsCluster.cc +++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsCluster.cc @@ -111,7 +111,7 @@ XrdCmsNode *XrdCmsCluster::Add(XrdLink *lp, int port, int Status, const char *act = ""; unsigned int ipaddr; XrdCmsNode *nP = 0; - int Slot, Free = -1, Bump1 = -1, Bump2 = -1, Bump3 = -1; + int Slot, Free = -1, Bump1 = -1, Bump2 = -1, Bump3 = -1, aSet = 0; int tmp, Special = (Status & (CMS_isMan|CMS_isPeer)); XrdSysMutexHelper STMHelper(STMutex); @@ -124,15 +124,16 @@ XrdCmsNode *XrdCmsCluster::Add(XrdLink *lp, int port, int Status, // Slot = Reconnecting node // Free = Available slot ( 1st in table) // Bump1 = Disconnected server (last in table) -// Bump2 = Connected server (last in table) +// Bump2 = Connected server (last in table) if new one is managr/peer // Bump3 = Disconnected managr/peer ( 1st in table) if new one is managr/peer // for (Slot = 0; Slot < STMax; Slot++) if (NodeTab[Slot]) {if (NodeTab[Slot]->isNode(ipaddr, theNID)) break; -//Conn// if (NodeTab[Slot]->isConn) - {if (!NodeTab[Slot]->isPerm) Bump2 = Slot; // Last conn Server -//Disc// } else { +/*Conn*/ if (NodeTab[Slot]->isConn) + {if (!NodeTab[Slot]->isPerm && Special) + Bump2 = Slot; // Last conn Server +/*Disc*/ } else { if ( NodeTab[Slot]->isPerm) {if (Bump3 < 0 && Special) Bump3 = Slot;}// 1st disc Man/Pr else Bump1 = Slot; // Last disc Server @@ -171,7 +172,9 @@ XrdCmsNode *XrdCmsCluster::Add(XrdLink *lp, int port, int Status, return 0; } - if (NodeTab[Slot] && !(Status & CMS_isPeer)) sendAList(lp); + if (Status & CMS_isMan) {setAltMan(Slot,ipaddr,sport); aSet=1;} + if (NodeTab[Slot] && !(Status & CMS_isPeer)) + sendAList(NodeTab[Slot]->Link); DEBUG(lp->ID << " bumps " << NodeTab[Slot]->Ident <<" #" <<Slot); NodeTab[Slot]->Lock(); @@ -187,7 +190,7 @@ XrdCmsNode *XrdCmsCluster::Add(XrdLink *lp, int port, int Status, // Assign new server // - if (Status & CMS_isMan) setAltMan(Slot, ipaddr, sport); + if (!aSet && (Status & CMS_isMan)) setAltMan(Slot, ipaddr, sport); if (Slot > STHi) STHi = Slot; nP->isBound = 1; nP->isConn = 1; @@ -237,7 +240,7 @@ SMask_t XrdCmsCluster::Broadcast(SMask_t smask, const struct iovec *iod, EPNAME("Broadcast") int i; XrdCmsNode *nP; - SMask_t bmask, unQueried = 0; + SMask_t bmask, unQueried(0); // Obtain a lock on the table and screen out peer nodes // @@ -304,7 +307,7 @@ SMask_t XrdCmsCluster::getMask(unsigned int IPv4adr) { int i; XrdCmsNode *nP; - SMask_t smask = 0; + SMask_t smask(0); // Obtain a lock on the table // @@ -327,7 +330,7 @@ SMask_t XrdCmsCluster::getMask(unsigned int IPv4adr) SMask_t XrdCmsCluster::getMask(const char *Cid) { XrdCmsNode *nP; - SMask_t smask = 0; + SMask_t smask(0); XrdOucTList *cP; int i = 1, Cnum = -1; @@ -424,16 +427,35 @@ int XrdCmsCluster::Locate(XrdCmsSelect &Sel) { EPNAME("Locate"); XrdCmsPInfo pinfo; - SMask_t qfVec = 0; + SMask_t qfVec(0); + char *Path; int retc = 0; +// Check if this is a locate for all current servers +// + if (*Sel.Path.Val != '*') Path = Sel.Path.Val; + else {if (*(Sel.Path.Val+1) == '\0') + {Sel.Vec.hf = ~0LL; Sel.Vec.pf = Sel.Vec.wf = 0; + return 0; + } + Path = Sel.Path.Val+1; + } + // Find out who serves this path // - if (!Cache.Paths.Find(Sel.Path.Val, pinfo) || !pinfo.rovec) + if (!Cache.Paths.Find(Path, pinfo) || !pinfo.rovec) {Sel.Vec.hf = Sel.Vec.pf = Sel.Vec.wf = 0; return -1; } else Sel.Vec.wf = pinfo.rwvec; +// Check if this was a non-lookup request +// + if (*Sel.Path.Val == '*') + {Sel.Vec.hf = pinfo.rovec; Sel.Vec.pf = 0; + Sel.Vec.wf = pinfo.rwvec; + return 0; + } + // Complete the request info object if we have one // if (Sel.InfoP) @@ -481,7 +503,7 @@ void *XrdCmsCluster::MonPerf() struct iovec ioV[] = {{(char *)&Usage, sizeof(Usage)}}; int ioVnum = sizeof(ioV)/sizeof(struct iovec); int ioVtot = sizeof(Usage); - SMask_t allNodes = ~static_cast<SMask_t>(0); + SMask_t allNodes(~0); int uInterval = Config.AskPing*Config.AskPerf; // Sleep for the indicated amount of time, then ask for load on each server @@ -596,10 +618,6 @@ void XrdCmsCluster::Remove(const char *reason, XrdCmsNode *theNode, int immed) // theNode->isOffline = 1; -// If the node is part of the cluster, do not count it anymore -// - if (theNode->isBound) {theNode->isBound = 0; NodeCnt--;} - // If the node is connected the simply close the connection. This will cause // the connection handler to re-initiate the node removal. The LockHandler // destructor will release the node table and node object locks as needed. @@ -611,11 +629,16 @@ void XrdCmsCluster::Remove(const char *reason, XrdCmsNode *theNode, int immed) return; } -// Indicate new state of this nodes if we are a reporting manager + +// If the node is part of the cluster, do not count it anymore and +// indicate new state of this nodes if we are a reporting manager // - if (Config.asManager()) - CmsState.Update(XrdCmsState::Counts, theNode->isSuspend ? 0 : -1, - theNode->isNoStage ? 0 : -1); + if (theNode->isBound) + {theNode->isBound = 0; NodeCnt--; + if (Config.asManager()) + CmsState.Update(XrdCmsState::Counts, theNode->isSuspend ? 0 : -1, + theNode->isNoStage ? 0 : -1); + } // If this is an immediate drop request, do so now. Drop() will delete // the node object and remove the node lock. So, tell LockHandler that. @@ -697,7 +720,8 @@ int XrdCmsCluster::Select(XrdCmsSelect &Sel) // which will force the client to wait. Otherwise, compute the primary and // secondary selections. If there are none, the client may have to wait if we // have servers that we can query regarding the file. Note that for files being -// opened in write mode, only one writable copy may exist. +// opened in write mode, only one writable copy may exist unless this is a +// meta-operation (e.g., remove) in which case the file itself remain unmodified // if (!(Sel.Opts & XrdCmsSelect::Refresh) && (retc = Cache.GetFile(Sel, pinfo.rovec))) @@ -705,7 +729,8 @@ int XrdCmsCluster::Select(XrdCmsSelect &Sel) { if (Sel.Vec.bf) pmask = smask = 0; else if (Sel.Vec.hf) {if (Sel.Opts & XrdCmsSelect::NewFile) return SelFail(Sel,eExists); - if (Multiple(Sel.Vec.hf)) return SelFail(Sel,eDups); + if (!(Sel.Opts & XrdCmsSelect::isMeta) + && Multiple(Sel.Vec.hf)) return SelFail(Sel,eDups); if (!(pmask = Sel.Vec.hf & amask)) return SelFail(Sel,eROfs); smask = 0; } @@ -775,7 +800,7 @@ int XrdCmsCluster::Select(XrdCmsSelect &Sel) int XrdCmsCluster::Select(int isrw, SMask_t pmask, int &port, char *hbuff, int &hlen) { - static const SMask_t smLow = 255; + static const SMask_t smLow(255); XrdCmsNode *nP = 0; SMask_t tmask; int Snum = 0; @@ -1356,8 +1381,9 @@ XrdCmsNode *XrdCmsCluster::SelbyRef(SMask_t mask, int &nump, int &delay, void XrdCmsCluster::sendAList(XrdLink *lp) { static CmsTryRequest Req = {{0, kYR_try, 0, 0}, 0}; + static int HdrSize = sizeof(Req.Hdr) + sizeof(Req.sLen); static char *AltNext = AltMans; - static struct iovec iov[4] = {{(caddr_t)&Req, sizeof(Req)}, + static struct iovec iov[4] = {{(caddr_t)&Req, HdrSize}, {0, 0}, {AltMans, 0}, {(caddr_t)"\0", 1}}; @@ -1377,14 +1403,15 @@ void XrdCmsCluster::sendAList(XrdLink *lp) dlen = iov[1].iov_len + iov[2].iov_len; } -// Complete the request +// Complete the request (account for trailing null character) // + dlen++; Req.Hdr.datalen = htons(static_cast<unsigned short>(dlen+sizeof(Req.sLen))); Req.sLen = htons(static_cast<unsigned short>(dlen)); // Send the list of alternates (rotated once) // - lp->Send(iov, 4, dlen+sizeof(Req)); + lp->Send(iov, 4, dlen+HdrSize); } /******************************************************************************/ diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsConfig.cc b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsConfig.cc index 738f6a61f1b93d87cc561cae7e44133e269967b7..2d3e8f7e7db0659c4b4037be24fd71c48fb31c52 100644 --- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsConfig.cc +++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsConfig.cc @@ -314,7 +314,7 @@ int XrdCmsConfig::Configure2() Output: 0 upon success or !0 otherwise. */ EPNAME("Configure2"); - int NoGo = 0; + int Who, NoGo = 0; char *p, buff[512]; // Print herald @@ -377,7 +377,9 @@ int XrdCmsConfig::Configure2() if (isMeta) {SUPCount = 1; SUPLevel = 0;} if (!ManList) CmsState.Update(XrdCmsState::FrontEnd, 1); } - CmsState.Set(SUPCount, isManager && !isServer, AdminPath); + if (isManager) Who = (isServer ? -1 : 1); + else Who = 0; + CmsState.Set(SUPCount, Who, AdminPath); // Create the pid file // diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsMeter.cc b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsMeter.cc index a6194f7b63c0ddf84936516419affe5f103a5433..9d96ce617e1f220bbb208f081fa21cc15c25793c 100644 --- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsMeter.cc +++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsMeter.cc @@ -483,7 +483,7 @@ void XrdCmsMeter::SpaceMsg(int why) void XrdCmsMeter::UpdtSpace() { - static const SMask_t allNodes = ~0ULL; + static const SMask_t allNodes(~0); SpaceData mySpace; // Get new space values for the cluser diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsNode.cc b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsNode.cc index 0793583b9962da743125be0953e8a1a502acf5ab..caf875530e6d0bead530a19d66c587baf87ad6cf 100644 --- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsNode.cc +++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsNode.cc @@ -73,7 +73,7 @@ XrdCmsNode::XrdCmsNode(XrdLink *lnkp, int port, const char *nid, int lvl, int id) { static XrdSysMutex iMutex; - static const SMask_t smask_1 = 1; + static const SMask_t smask_1(1); static int iNum = 1; Link = lnkp; @@ -654,7 +654,7 @@ const char *XrdCmsNode::do_Mkpath(XrdCmsRRData &Arg) const char *XrdCmsNode::do_Mv(XrdCmsRRData &Arg) { EPNAME("do_Mv") - static const SMask_t allNodes = ~static_cast<SMask_t>(0); + static const SMask_t allNodes(~0); int rc; // Do some debugging @@ -803,7 +803,7 @@ const char *XrdCmsNode::do_PrepDel(XrdCmsRRData &Arg) const char *XrdCmsNode::do_Rm(XrdCmsRRData &Arg) { EPNAME("do_Rm") - static const SMask_t allNodes = ~static_cast<SMask_t>(0); + static const SMask_t allNodes(~0); int rc; // Do some debugging @@ -845,7 +845,7 @@ const char *XrdCmsNode::do_Rm(XrdCmsRRData &Arg) const char *XrdCmsNode::do_Rmdir(XrdCmsRRData &Arg) { EPNAME("do_Rmdir") - static const SMask_t allNodes = ~static_cast<SMask_t>(0); + static const SMask_t allNodes(~0); int rc; // Do some debugging @@ -1405,13 +1405,14 @@ const char *XrdCmsNode::do_Try(XrdCmsRRData &Arg) // Add all the alternates to our alternate list // + tp = theList.GetLine(); while((tp = theList.GetToken())) myMans.Add(IPAddr, tp, Config.PortTCP, myLevel); // Close the link and return an error // - Disc("redirected."); - return 0; +// Disc("redirected."); + return ".redirected"; } /******************************************************************************/ diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsPList.cc b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsPList.cc index 47289a06c46972e20477af0076a2b68b58c9c168..337254ab4693a7912da1dbebee482c43ad525e70 100644 --- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsPList.cc +++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsPList.cc @@ -93,7 +93,7 @@ SMask_t XrdCmsPList_Anchor::Insert(const char *pname, XrdCmsPInfo *pinfo) void XrdCmsPList_Anchor::Remove(SMask_t mask) { - SMask_t zmask = ~mask; + SMask_t zmask(~mask); XrdCmsPList *pp = next, *prevp = 0; // Lock the list diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsPList.hh b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsPList.hh index 3540d79952c41b4e4cdb7918f7206365bbf3553c..f8c1452e135e2658d1e299f0378b220a65b91367 100644 --- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsPList.hh +++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsPList.hh @@ -54,25 +54,24 @@ class XrdCmsPList public: friend class XrdCmsPList_Anchor; -inline XrdCmsPList *Next() {return next;} +inline XrdCmsPList *Next() {return next;} inline char *Path() {return pathname;} const char *PType(); XrdCmsPList(const char *pname="", XrdCmsPInfo *pi=0) - {next = 0; - pathlen = strlen(pname); - pathname = strdup(pname); - if (pi) pathmask.Set(pi); - } + : next(0), pathname(strdup(pname)), pathlen(strlen(pname)), + pathtype(0) {if (pi) pathmask.Set(pi);} ~XrdCmsPList() {if (pathname) free(pathname);} private: +XrdCmsPInfo pathmask; XrdCmsPList *next; -int pathlen; char *pathname; -XrdCmsPInfo pathmask; +int pathlen; +char pathtype; +char reserved[3]; }; class XrdCmsPList_Anchor diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsProtocol.cc b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsProtocol.cc index 560b639a623c1f7e3e84e970861a1a983831cb9d..0f69f5eda84c0bc5786da788fb7953188da0fead 100644 --- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsProtocol.cc +++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsProtocol.cc @@ -203,7 +203,8 @@ int XrdCmsProtocol::Execute(XrdCmsRRData &Arg) if (*etxt == '!') {DEBUGR(etxt+1 <<" delayed " <<Arg.waitVal <<" seconds"); return -EINPROGRESS; - } else Reply_Error(Arg, kYR_EINVAL, etxt); + } else if (*etxt == '.') return -ECONNABORTED; + else Reply_Error(Arg, kYR_EINVAL, etxt); else if (Arg.Routing & XrdCmsRouting::Forward && Cluster.NodeCnt && !(Arg.Request.modifier & kYR_dnf)) Reissue(Arg); return 0; @@ -354,7 +355,7 @@ void XrdCmsProtocol::Pander(const char *manager, int mport) // Remove manager from the config // Manager.Remove(myNode, (rc == kYR_redirect ? "redirected" - : (rc ? "lost connection" : Reason))); + : (Reason ? Reason : "lost connection"))); ManTree.Disc(myNID); Link->Close(); delete myNode; myNode = 0; @@ -469,7 +470,7 @@ XrdCmsRouting *XrdCmsProtocol::Admit() XrdCmsLogin Source(myBuff, sizeof(myBuff)); CmsLoginData Data; const char *Reason; - SMask_t newmask, servset = 0; + SMask_t newmask, servset(0); int addedp = 0, Status = 0, isMan = 0, isPeer = 0, isProxy = 0, isServ = 0; int wasSuspended = 0; @@ -814,9 +815,11 @@ do{if ((rc = Link->RecvAll((char *)&Data->Request, ReqSize, maxWait)) < 0) // if (!(Data->Ident) || !(*Data->Ident)) Data->Ident = myNode->Ident; -// Schedule this request +// Schedule this request if async. Otherwise, do this inline. Note that +// synchrnous requests are allowed to return status changes (e.g., redirect) // - if (Data->Routing & XrdCmsRouting::isSync) Execute(*Data); + if (Data->Routing & XrdCmsRouting::isSync) + {if ((rc = Execute(*Data)) && rc == -ECONNABORTED) return "redirected";} else if ((jp = XrdCmsJob::Alloc(this, Data))) {Sched->Schedule((XrdJob *)jp); Data = XrdCmsRRData::Objectify(); diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsRRData.cc b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsRRData.cc index 925577f09e9df9b377a68bcecbbbd7d9fa48c047..43bcbe016b2a36fb1e1b69bdf240224aa7b289b2 100644 --- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsRRData.cc +++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsRRData.cc @@ -31,8 +31,10 @@ int XrdCmsRRData::getBuff(size_t bsz) size_t Alignment = PageSize; if (bsz < Alignment) - {do {Alignment = Alignment >> 1;} while(bsz < Alignment); - Alignment = Alignment << 1; bsz = Alignment; + {if (bsz <= 8) Alignment = bsz = 8; + else {do {Alignment = Alignment >> 1;} while(bsz < Alignment); + Alignment = Alignment << 1; bsz = Alignment; + } } if (Buff) free(Buff); diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsRTable.hh b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsRTable.hh index 387be5fab20d6e967722d28e957b40f7c9abae1a..e98a4bc7ec2ecfd21f31c234b6b7dafbb157b2aa 100644 --- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsRTable.hh +++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsRTable.hh @@ -15,6 +15,7 @@ #include <string.h> #include "XrdCms/XrdCmsNode.hh" +#include "XrdCms/XrdCmsTypes.hh" #include "XrdSys/XrdSysPthread.hh" class XrdCmsRTable @@ -39,8 +40,6 @@ void UnLock() {myMutex.UnLock();} private: -static const int maxRD = 65; // slot 0 is never used. - XrdSysMutex myMutex; XrdCmsNode *Rtable[maxRD]; int Hwm; diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsResp.cc b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsResp.cc index 5fc2647343456b606b872baddefbd5b55f704982..e086c65b45ce1947c282ff380b571dcc2ead6d7a 100644 --- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsResp.cc +++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsResp.cc @@ -89,18 +89,17 @@ XrdCmsResp *XrdCmsResp::Alloc(XrdOucErrInfo *erp, int msgid) /* R e c y c l e */ /******************************************************************************/ -void XrdCmsResp::Recycle(void *p) +void XrdCmsResp::Recycle() { // Recycle appendages // if (myBuff) {myBuff->Recycle(); myBuff = 0;} -// If a free area pointer has been passed then this is from delete() and we -// need to check if we have too many message objects at this point. Otherwise, -// it's from a synchrnous recycle and it's always OK to requeue these. +// We keep a stash of allocated response objects. If there are too many we +// simply delete this object. // - if (p && XrdCmsResp::numFree >= XrdCmsResp::maxFree) free(p); + if (XrdCmsResp::numFree >= XrdCmsResp::maxFree) delete this; else {myMutex.Lock(); next = nextFree; nextFree = this; @@ -165,6 +164,7 @@ void XrdCmsResp::Reply() void XrdCmsResp::ReplyXeq() { EPNAME("Reply") + XrdOucEICB *theCB; int Result; // If there is no callback object, ignore this call. Eventually, we may wish @@ -202,9 +202,15 @@ void XrdCmsResp::ReplyXeq() // SyncCB.Wait(); -// Invoke the callback; it must explicitly invoke delete on our upcast object. +// We now must request a callback to recycle this object once the callback +// response is actually sent by setting the callback object pointer to us. // - ErrCB->Done(Result, (XrdOucErrInfo *)this); + theCB = ErrCB; + ErrCB = (XrdOucEICB *)this; + +// Invoke the callback +// + theCB->Done(Result, (XrdOucErrInfo *)this); } /******************************************************************************/ diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsResp.hh b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsResp.hh index e7b845024f9060d815c748632e6af206906c62d0..80e4f5cb073a20bcbdd7715f2a76564a551b0c28 100644 --- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsResp.hh +++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsResp.hh @@ -49,13 +49,15 @@ XrdSysSemaphore respSync; class XrdNetBuffer; -class XrdCmsResp : public XrdOucErrInfo +class XrdCmsResp : public XrdOucEICB, public XrdOucErrInfo { public: friend class XrdCmsRespQ; static XrdCmsResp *Alloc(XrdOucErrInfo *erp, int msgid); + void Done(int &Result, XrdOucErrInfo *eInfo) {Recycle();} + inline int ID() {return myID;} void Reply(const char *Man, XrdCms::CmsRRHdr &rrhdr, @@ -63,15 +65,16 @@ inline int ID() {return myID;} static void Reply(); + int Same(unsigned long long arg1, unsigned long long arg2) + {return 0;} + static void setDelay(int repdly) {RepDelay = repdly;} XrdCmsResp() : XrdOucErrInfo(UserID) {next = 0; myBuff = 0;} ~XrdCmsResp() {} -void operator delete(void *p) {((XrdCmsResp *)p)->Recycle();} - private: - void Recycle(void *p=0); + void Recycle(); void ReplyXeq(); static XrdSysSemaphore isReady; diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsState.cc b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsState.cc index d4fbf2f5bff108354ad1170fa5432514a9ac9564..3000ad89c3440384d7b7191b218017291c701323 100644 --- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsState.cc +++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsState.cc @@ -113,9 +113,9 @@ void *XrdCmsState::Monitor() {if (myStatus.Hdr.modifier & CmsStatusRequest::kYR_Resume) {myStatus.Hdr.streamid = htonl(myPort); RTsend = 1;} else {myStatus.Hdr.streamid = 0; - RTsend = (theState & SRV_Suspend); + RTsend = (isMan > 0 ? (theState & SRV_Suspend) : 0); } - if (isMan && RTsend) + if (isMan && RTsend) RTable.Send("status", (char *)&myStatus, sizeof(myStatus)); Manager.Inform(myStatus.Hdr); } diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsTrace.hh b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsTrace.hh index ea430094c55099d044bee8f13147eb77b4528c87..e001d8fe0c255302b2e46ed558e620db6e5619a9 100644 --- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsTrace.hh +++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsTrace.hh @@ -41,7 +41,7 @@ #define TRACEX(y) {Trace.Beg(0,epname); cerr <<y; Trace.End();} -#define EPNAME(x) const char *epname = x; +#define EPNAME(x) static const char *epname = x; #else diff --git a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsTypes.hh b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsTypes.hh index 75c381b003255a967a5c3f994e86fabf16ee816f..b2f5030eb3e3d9b2566896dd4c8992f1be75b64f 100644 --- a/net/xrootd/src/xrootd/src/XrdCms/XrdCmsTypes.hh +++ b/net/xrootd/src/xrootd/src/XrdCms/XrdCmsTypes.hh @@ -20,6 +20,11 @@ typedef unsigned long long SMask_t; // #define STMax 64 +// The following defines the maximum number of redirectors. It is one greater +// than the actual maximum as the zeroth is never used. +// +#define maxRD 65 + #define XrdCmsMAX_PATH_LEN 1024 #define XrdCmsVERSION "1.0.0" diff --git a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsConfig.cc b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsConfig.cc index fc3683e1aa06b8b5a831b2a356b35062cff0290d..86858ca3555070fda279c3f80e22b9245ed70f5e 100644 --- a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsConfig.cc +++ b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsConfig.cc @@ -110,7 +110,7 @@ int XrdCnsConfig::Configure(int argc, char **argv, char *argt) const char *TraceID = "Config"; XrdOucArgs Spec(&MLog,(argt ? "Cns_Config: ":"XrdCnsd: "), - "a:b:dD:e:E:i:I:l:L:N:p:q:R:"); + "a:b:B:c:dD:e:E:i:I:l:L:N:p:q:R:"); char buff[2048], *dP, *tP, *dnsEtxt = 0, *n2n = 0, *lroot = 0, *xpl = 0; char theOpt, *theArg; long long llval; @@ -132,6 +132,8 @@ int XrdCnsConfig::Configure(int argc, char **argv, char *argt) case 'B': Opts |= optNoCns; case 'b': bPath = Spec.argval; break; + case 'c': cPath = Spec.argval; + break; case 'D': NoGo |= XrdOuca2x::a2i(MLog,"-D value",Spec.argval,&n,0,4); if (!NoGo) EnvPutInt("DebugLevel", n); break; diff --git a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsDaemon.cc b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsDaemon.cc index 0163e3d2f17531fc0528026d51038315ee6de44d..7de754bbd890b5e7ba75f260cabb52344d87f7eb 100644 --- a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsDaemon.cc +++ b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsDaemon.cc @@ -71,7 +71,7 @@ void XrdCnsDaemon::getEvents(XrdOucStream &Events, const char *Who) || !evP->setType(eP)) Miss = "eventid"; else {switch(evP->Type()) {case XrdCnsLogRec::lrClosew: - if (!(tp=Events.GetToken())) {Miss = "lfn"; break;} + if (!(tp=getLFN(Events))) {Miss = "lfn"; break;} evP->setLfn1(tp); if (!(tp=Events.GetToken())) {Miss = "size"; break;} Size = strtoll(tp, &etp, 10); @@ -85,17 +85,17 @@ void XrdCnsDaemon::getEvents(XrdOucStream &Events, const char *Who) Mode = strtol(tp, &etp, 8); if (*etp) {Miss = "mode"; break;} evP->setMode(Mode); - if (!(tp=Events.GetToken())) {Miss = "lfn"; break;} + if (!(tp=getLFN(Events))) {Miss = "lfn"; break;} evP->setLfn1(tp); break; case XrdCnsLogRec::lrMv: - if (!(tp=Events.GetToken())) {Miss = "lfn1"; break;} + if (!(tp=getLFN(Events))) {Miss = "lfn1"; break;} evP->setLfn1(tp); - if (!(tp=Events.GetToken())) {Miss = "lfn2"; break;} + if (!(tp=getLFN(Events))) {Miss = "lfn2"; break;} evP->setLfn2(tp); break; default: // rm | rmdir - if (!(tp=Events.GetToken())) {Miss = "lfn"; break;} + if (!(tp=getLFN(Events))) {Miss = "lfn"; break;} evP->setLfn1(tp); break; } @@ -114,3 +114,18 @@ void XrdCnsDaemon::getEvents(XrdOucStream &Events, const char *Who) // MLog.Emsg("doEvents", "Lost event connection to", Who, "!"); } + +/******************************************************************************/ +/* g e t L F N */ +/******************************************************************************/ + +char *XrdCnsDaemon::getLFN(XrdOucStream &Events) +{ + char *tP, *cgiP; + +// Obtain the lfn but discard any CGI information that has been mistakenly +// passed. Some people recall the old documentation, sigh. +// + if ((tP=Events.GetToken()) && (cgiP = index(tP, '?'))) *cgiP = '\0'; + return tP; +} diff --git a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsDaemon.hh b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsDaemon.hh index b2eb341242475490336a2a7d5da7de12720bca1a..2ebe5029518c23a49bb7f06b0ceb5fbd10cdce02 100644 --- a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsDaemon.hh +++ b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsDaemon.hh @@ -26,5 +26,7 @@ void getEvents(XrdOucStream &, const char *Who); ~XrdCnsDaemon() {} private: + +char *getLFN(XrdOucStream &Events); }; #endif diff --git a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogClient.cc b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogClient.cc index 75bd73082bb6888f338a70f87f48459de74801a3..83e916150e581c713b8ad79d4e43c92f56b3a9a9 100644 --- a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogClient.cc +++ b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogClient.cc @@ -227,8 +227,9 @@ int XrdCnsLogClient::Run(int Always) do{if (arkFN && time(0) >= mCheck) {if (!Manifest()) - if (!Always) return 0; - MLog.Emsg("LogClient","Unable to create inventory at",arkURL); + {if (!Always) return 0; + MLog.Emsg("LogClient","Unable to create inventory at",arkURL); + } mCheck = time(0) + Config.mInt; } diff --git a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogFile.cc b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogFile.cc index ecfc2dba12e1e7982c3a36de4a03dc29a8e7f17d..b6d9c1d590e6221e814c763f5e15524bd62f1dd0 100644 --- a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogFile.cc +++ b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogFile.cc @@ -122,7 +122,7 @@ int XrdCnsLogFile::Commit() int XrdCnsLogFile::Eol() { - XrdCnsLogFile *lfP = subNext; + XrdCnsLogFile *lfX, *lfP = subNext; XrdCnsLogRec lRec(XrdCnsLogRec::lrEOL); int rc, bL = XrdCnsLogRec::MinSize + lRec.DLen(); char *bP = (char *)&lRec; @@ -141,8 +141,9 @@ int XrdCnsLogFile::Eol() while(lfP) {lfP->logSem.Post(); lfP->logWait = 0; - lfP->synSem.Post(); + lfX = lfP; lfP = lfP->subNext; + lfX->synSem.Post(); } // All done @@ -166,6 +167,10 @@ XrdCnsLogRec *XrdCnsLogFile::getRec() bL = XrdCnsLogRec::MinSize; recOffset = logOffset; if (!Read(bP, bL) || !((bL = Rec.DLen()))) return 0; + if (bL > XrdCnsLogRec::MinSize) + {MLog.Emsg("getRec", "Invalid record length detected in", logFN); + return 0; + } bP = bP + XrdCnsLogRec::MinSize; if (!Read(bP, bL)) return 0; @@ -188,7 +193,7 @@ XrdCnsLogRec *XrdCnsLogFile::getRec() /* O p e n */ /******************************************************************************/ -int XrdCnsLogFile::Open(int allocbuff) +int XrdCnsLogFile::Open(int allocbuff, off_t thePos) { static const int AMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; @@ -197,6 +202,13 @@ int XrdCnsLogFile::Open(int allocbuff) if ((logFD = open(logFN, O_CREAT|O_RDWR, AMode)) < 0) {MLog.Emsg("Open", errno, "open", logFN); return 0;} +// Set starting position if need be +// + if (thePos && (lseek(logFD, thePos, SEEK_SET) == (off_t)-1)) + {MLog.Emsg("Open", errno, "seek into", logFN); close(logFD), logFD = -1; + return 0; + } + // Allocate a memory buffer if so wanted // if (allocbuff) diff --git a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogFile.hh b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogFile.hh index 1a6bdf9487559a3b9ada5d1ba13e94254afe486b..362d3143668890edaf5dbf154aa744d4f9880ef8 100644 --- a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogFile.hh +++ b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogFile.hh @@ -37,14 +37,14 @@ static void maxRecs(int nRecs) {logRMax = nRecs; logBMax = nRecs * sizeof(XrdCnsLogRec); } -int Open(int aBuff=1); +int Open(int aBuff=1, off_t thePos=0); XrdCnsLogFile *Subscribe(const char *Path, int cNum); int Unlink(); XrdCnsLogFile(const char *Path, int cnum=0, int Wait=1) - : logSem(0), subNext(0), + : Next(0), logSem(0), subNext(0), logBuff(0),logNext(0), logFN(strdup(Path)), logFD(-1), logRdr(cnum), logWait(Wait), logOffset(0), recOffset(0) {} diff --git a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogRec.cc b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogRec.cc index ae589c585e1e9d3022c276bf6cb13861b44d8f4b..69bd89d415db77f089eb30905ccc7fcbe5439ac9 100644 --- a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogRec.cc +++ b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogRec.cc @@ -83,7 +83,7 @@ XrdCnsLogRec *XrdCnsLogRec::Get(char &lrType) qSem.Wait(); qMutex.Lock(); } - frstRec = lrP->Next; + if (!(frstRec = lrP->Next)) lastRec = 0; qMutex.UnLock(); // Get the type and if its an eol marker recycle now @@ -137,14 +137,14 @@ int XrdCnsLogRec::setData(const char *dP1, const char *dP2) int n1 = strlen(dP1), n2 = strlen(dP2); char *dP; -// Make sure we have room +// Make sure we have room "'lfn' + 'data1' + ' ' + data2" // if (n1+n2+2 > MAXPATHLEN) return 0; // Add the data in the fields // setSize(static_cast<long long>(Rec.Hdr.lfn1Len)); - dP = Rec.Data.lfn + Rec.Hdr.lfn1Len + 1; + dP = Rec.Data.lfn + Rec.Hdr.lfn1Len+1; strcpy(dP, dP1); dP += n1; *dP++ = ' '; strcpy(dP, dP2); diff --git a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogRec.hh b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogRec.hh index 02f69a0e589031c36abf68e8cf27b618a0444a06..c3870a24248d6e0efe75d654ceaccc3d1a41685c 100644 --- a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogRec.hh +++ b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogRec.hh @@ -55,7 +55,8 @@ struct LogRec static const int OffDone = offsetof(LogRec, Hdr.Done); static const int FixDLen = offsetof(Arg, lfn); -static const int MinSize = sizeof(Ctl); +static const int MinSize = sizeof(Ctl); // Header +static const int MaxSize = sizeof(Arg); // Data static const long tBase = 1248126834L; static const char lrClosew = 't'; diff --git a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogServer.cc b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogServer.cc index 9c0a8474f898c06fa3449b4ab6df0e4988146416..ce4aed551a54d89e74056e8b99007691111d3d76 100644 --- a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogServer.cc +++ b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsLogServer.cc @@ -112,7 +112,7 @@ int XrdCnsLogServer::Init(XrdOucTList *rList) // if (Stat.st_size != 0) {XrdCnsLogFile myLogFile(logDir); - if (!myLogFile.Open(0) || !myLogFile.Eol()) return 0; + if (!myLogFile.Open(0, Stat.st_size) || !myLogFile.Eol()) return 0; } unlink(logDir); @@ -206,7 +206,7 @@ do{nRecs = Config.qLim; lfP = logFile; while(nRecs && (lrP = XrdCnsLogRec::Get(lrT))) {if (lrP->Type() == XrdCnsLogRec::lrCreate) Massage(lrP); - lfP->Add(lrP); + lfP->Add(lrP); lrP->Recycle(); nRecs--; } @@ -219,4 +219,8 @@ do{nRecs = Config.qLim; lfP = logFile; delete lfP; } } while(1); + +// At the moment we don't really have a recovery strategy +// + MLog.Emsg("Run", "Fatal error occurred; terminating. . ."); } diff --git a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsMain.cc b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsMain.cc index a08f5e874d166e996b83e3165bd95edcb96ebea4..e47c3346357443a01009421e59578abaa68edbd6 100644 --- a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsMain.cc +++ b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsMain.cc @@ -146,6 +146,7 @@ int main(int argc, char *argv[]) XrdSysLogger MLogger; XrdOucStream stdinEvents; // STDIN fed events sigset_t myset; + char *xrdLogD = 0; // Establish message routing // @@ -168,14 +169,15 @@ int main(int argc, char *argv[]) // if (!Config.Configure(argc, argv)) exit(1); -// Construct the logfile path and bind it (command line only) +// Construct the logfile path and bind it // - if (!Config.logfn && (Config.logfn = getenv("XRDLOGDIR"))) + if (Config.logfn || (xrdLogD = getenv("XRDLOGDIR"))) {pthread_t tid; char buff[2048]; int retc; + if (Config.logfn) strcpy(buff, Config.logfn); + else {strcpy(buff, xrdLogD); strcat(buff, "cnsdlog");} if (Config.logKeep) MLogger.setKeep(Config.logKeep); - strcpy(buff, Config.logfn); strcat(buff, "cnsdlog"); MLogger.Bind(buff, 24*60*60); MLog.logger(&MLogger); if ((retc = XrdSysThread::Run(&tid, MLogWorker, (void *)0, diff --git a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsSsi.cc b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsSsi.cc index 37f669a3f8ceb481157fd382e62f580a29c8763e..c06cfa142e8fa58d92f8746d630ad6c248a38e26 100644 --- a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsSsi.cc +++ b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsSsi.cc @@ -137,16 +137,17 @@ int XrdCnsSsiApplyM(const char *Mount, char *xP, void *Arg) static int doInit = 1; int n, iFD = *(int *)Arg; -// Initialize the header (needs to be done once +// Initialize the header (needs to be done once) // if (doInit) {memset(Hdr, ' ', sizeof(Hdr)); aP->Type = XrdCnsLogRec::lrMount; doInit = 0; - } else aP->Mount = *xP; + } // Write out a directory record. Terminate processing upon error // + aP->Mount = *xP; iov[1].iov_base = (char *)Mount; n = strlen(Mount); iov[1].iov_len = n; @@ -167,10 +168,11 @@ int XrdCnsSsiApplyS(const char *Space, char *xP, void *Arg) {memset(Hdr, ' ', sizeof(Hdr)); aP->Type = XrdCnsLogRec::lrSpace; doInit = 0; - } else aP->Mount = *xP; + } // Write out a directory record. Terminate processing upon error // + aP->Space = *xP; iov[1].iov_base = (char *)Space; n = strlen(Space); iov[1].iov_len = n; @@ -610,9 +612,9 @@ XrdCnsSsiFRec *XrdCnsSsi::AddFile(char *lfn, char *lP) // Extract out the directory, file name, space name and mount point, if any // - if ((sP = index(lfn, '?'))) *sP = '\0'; + if ((sP = index(lfn, ' '))) *sP++ = '\0'; if (!(fP = rindex(lfn+1, '/')) || !(*(fP+1))) - {if (sP) *sP = '?'; + {if (sP) *(sP-1) = ' '; Say.V("Invalid log record ", lP); nErrs++; return 0; } @@ -620,7 +622,7 @@ XrdCnsSsiFRec *XrdCnsSsi::AddFile(char *lfn, char *lP) if (sP) {if (!(mP = index(sP, ' '))) aP->Mount = mountP->Default(); else {*mP++ = '\0'; aP->Mount = mountP->Add(mP);} - if (*(sP+1)) aP->Space = spaceP->Add(sP+1); + if (*sP) aP->Space = spaceP->Add(sP); else aP->Space = spaceP->Default(); } else { aP->Mount = mountP->Default(); aP->Space = spaceP->Default(); diff --git a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsSsiCfg.cc b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsSsiCfg.cc index a642372d7a363bc68a534486221465b33c6821c3..694326e3e4d5d62a10e5fc00f99d3918e2e96bf7 100644 --- a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsSsiCfg.cc +++ b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsSsiCfg.cc @@ -59,7 +59,7 @@ int XrdCnsSsiCfg::Configure(int argc, char **argv) Say.M("The diff function is not yet implemented."); return 0; } - else if (!strcmp("list", argv[1])) {Xeq = 'l'; Opts = "fhmnpsS";} + else if (!strcmp("list", argv[1])) {Xeq = 'l'; Opts = "fhlmnpsS";} else if (!strcmp("updt", argv[1])) {Xeq = 'u'; Opts = "v";} else Usage("Invalid function - ", argv[1]); @@ -138,7 +138,7 @@ int XrdCnsSsiCfg::Configure(int argc, char **argv, const char *Opts) // Make sure we have some host entries // if (!(dirList = XrdCnsLog::Dirs(bPath, n))) - {Say.M("No server inventories found in'",bPath,"'."); return 0;} + {Say.M("No server inventories found in '",bPath,"'."); return 0;} // Issue warning if errors occurred // diff --git a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsXref.cc b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsXref.cc index b5f20174d26997133d2b725962ff5f65d46145fb..be043b5a4df39edd978bc2aa5a09c4493d06ccef 100644 --- a/net/xrootd/src/xrootd/src/XrdCns/XrdCnsXref.cc +++ b/net/xrootd/src/xrootd/src/XrdCns/XrdCnsXref.cc @@ -57,7 +57,9 @@ char XrdCnsXref::Add(const char *kval, char idx) // If a character was specified, try to use it. // - if ((i = (idx ? c2i(idx) : availI())) < 0) return 0; + if (idx) i = c2i(idx); + else if ((oldx = xTable.Find(xKey))) return *oldx; + else if ((i = availI()) < 0) return 0; // Try to add the new entry // @@ -67,7 +69,7 @@ char XrdCnsXref::Add(const char *kval, char idx) {if (yTable[j]) {xTable.Del(yTable[j]); free(yTable[j]); yTable[j] = 0;} xTable.Rep(xKey, xIndex+i, 0, Hash_keep); - yTable[static_cast<int>(idx)] = xKey; + yTable[j] = xKey; } } else free(xKey); diff --git a/net/xrootd/src/xrootd/src/XrdCrypto/XrdCryptoTrace.hh b/net/xrootd/src/xrootd/src/XrdCrypto/XrdCryptoTrace.hh index 616c3be42c2e1afa0951cd48a81725951706a492..58b1866ca38e6f9d1c444eca70e0a7264852f9be 100644 --- a/net/xrootd/src/xrootd/src/XrdCrypto/XrdCryptoTrace.hh +++ b/net/xrootd/src/xrootd/src/XrdCrypto/XrdCryptoTrace.hh @@ -23,7 +23,7 @@ cerr <<y; cryptoTrace->End();}} #define TRACE(act,x) if (QTRACE(act)) PRINT(x) #define DEBUG(y) TRACE(Debug,y) -#define EPNAME(x) const char *epname = x; +#define EPNAME(x) static const char *epname = x; #else diff --git a/net/xrootd/src/xrootd/src/XrdCrypto/XrdCryptosslAux.cc b/net/xrootd/src/xrootd/src/XrdCrypto/XrdCryptosslAux.cc index d864562dc5822052d4d4bfabfc247238be465eb2..019142c69259ee278d0c7f98ff8c32dc9f188c84 100644 --- a/net/xrootd/src/xrootd/src/XrdCrypto/XrdCryptosslAux.cc +++ b/net/xrootd/src/xrootd/src/XrdCrypto/XrdCryptosslAux.cc @@ -617,7 +617,7 @@ int XrdCryptosslASN1toUTC(ASN1_TIME *tsn1) if (!gmtime_r(&now, >n)) return etime; // // Calculate correction - int tzcor = (int)difftime(mktime(<n), mktime(>n)); + int tzcor = (int) difftime(mktime(<n), mktime(>n)); // // Apply correction etime += tzcor; diff --git a/net/xrootd/src/xrootd/src/XrdCrypto/XrdCryptosslTrace.hh b/net/xrootd/src/xrootd/src/XrdCrypto/XrdCryptosslTrace.hh index 0a5147a526abf668b2a6a881b8b04c9fefad5503..d2f3936b8ad0f3b416166c986dcd583b4acfbccc 100644 --- a/net/xrootd/src/xrootd/src/XrdCrypto/XrdCryptosslTrace.hh +++ b/net/xrootd/src/xrootd/src/XrdCrypto/XrdCryptosslTrace.hh @@ -23,7 +23,7 @@ cerr <<y; sslTrace->End();}} #define TRACE(act,x) if (QTRACE(act)) PRINT(x) #define DEBUG(y) TRACE(Debug,y) -#define EPNAME(x) const char *epname = x; +#define EPNAME(x) static const char *epname = x; #else diff --git a/net/xrootd/src/xrootd/src/XrdFrm/GNUmakefile b/net/xrootd/src/xrootd/src/XrdFrm/GNUmakefile index ccc009153628f07553f66b79dee6b40148a089bb..85f0cb3a487b3bf6e30aab1d2c772094b05e1b11 100644 --- a/net/xrootd/src/xrootd/src/XrdFrm/GNUmakefile +++ b/net/xrootd/src/xrootd/src/XrdFrm/GNUmakefile @@ -1,4 +1,4 @@ -# $Id: GNUmakefile,v 1.4 2009/06/11 15:31:49 ganis Exp $ +# $Id: GNUmakefile,v 1.8 2010/01/05 22:55:24 abh Exp $ #------------------------------------------------------------------------------# # C o m m o n V a r i a b l e s # @@ -26,11 +26,15 @@ SOURCES = \ XrdFrmPstgMain.cc \ XrdFrmPstgReq.cc \ XrdFrmPstgXfr.cc \ + XrdFrmPurge.cc \ + XrdFrmPurgMain.cc \ + XrdFrmTSort.cc \ XrdFrmUtils.cc OBJLIB = \ $(OBJDIR)/XrdFrmConfig.o \ $(OBJDIR)/XrdFrmFiles.o \ + $(OBJDIR)/XrdFrmTSort.o \ $(OBJDIR)/XrdFrmUtils.o OBJADMIN= \ @@ -48,7 +52,11 @@ OBJPSTG = \ $(OBJDIR)/XrdFrmPstgReq.o \ $(OBJDIR)/XrdFrmPstgXfr.o -OBJECTS = $(OBJLIB) $(OBJADMIN) $(OBJPSTG) +OBJPURG = \ + $(OBJDIR)/XrdFrmPurgMain.o \ + $(OBJDIR)/XrdFrmPurge.o + +OBJECTS = $(OBJLIB) $(OBJADMIN) $(OBJPSTG) $(OBJPURG) OBJADDS = \ $(OBJDIR)/XrdXrootdMonitor.o \ @@ -65,8 +73,9 @@ LIBRARY = $(LIBDIR)/libXrdFrm.a BINADMIN= $(BINDIR)/frm_admin BINPSTGA= $(BINDIR)/frm_pstga BINPSTGD= $(BINDIR)/frm_pstgd +BINPURGD= $(BINDIR)/frm_purged -TARGETS = $(LIBRARY) $(BINADMIN) $(BINPSTGA) $(BINPSTGD) +TARGETS = $(LIBRARY) $(BINADMIN) $(BINPSTGA) $(BINPSTGD) $(BINPURGD) #------------------------------------------------------------------------------# # S e a r c h P a t h s # @@ -120,11 +129,16 @@ $(BINPSTGD): $(OBJLIB) $(OBJPSTG) $(OBJADDS) $(OBJDEPS) @echo Creating executable $(BINPSTGD) $(ECHO)$(LD) $(LDOP) $(OBJPSTG) $(OBJADDS) $(LIBS) $(BINLIBS) $(TYPELIBMT) -o $(BINPSTGD) +$(BINPURGD): $(OBJLIB) $(OBJPURG) $(OBJADDS) $(OBJDEPS) + @echo Creating executable $(BINPURGD) + $(ECHO)$(LD) $(LDOP) $(OBJPURG) $(OBJADDS) $(LIBS) $(BINLIBS) $(TYPELIBMT) -o $(BINPURGD) + $(OBJDIR)/XrdFrmAdmin.o: XrdFrmAdmin.hh XrdFrmAdmin.cc XrdFrmTrace.hh \ XrdFrmConfig.hh XrdFrmUtils.hh \ XrdOss.hh XrdOucExport.hh \ XrdOuca2x.hh XrdOucArgs.hh \ - XrdOucTList.hh XrdOucTokenizer.hh + XrdOucTList.hh XrdOucTokenizer.hh \ + XrdSysError.hh @echo Compiling XrdFrmAdmin.cc $(ECHO)$(CC) -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdFrmAdmin.o XrdFrmAdmin.cc @@ -133,19 +147,19 @@ $(OBJDIR)/XrdFrmAdminAudit.o: XrdFrmAdmin.hh XrdFrmAdminAudit.cc \ XrdFrmTrace.hh XrdFrmUtils.hh \ XrdOssPath.hh XrdOssSpace.hh \ XrdOucNSWalk.hh XrdOucTList.hh \ - XrdSysTimer.hh + XrdSysError.hh XrdSysTimer.hh @echo Compiling XrdFrmAdminAudit.cc $(ECHO)$(CC) -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdFrmAdminAudit.o XrdFrmAdminAudit.cc $(OBJDIR)/XrdFrmAdminFiles.o: XrdFrmAdmin.hh XrdFrmAdminFiles.cc \ XrdFrmConfig.hh XrdFrmFiles.hh \ - XrdFrmTrace.hh XrdOssPath.hh + XrdFrmTrace.hh XrdSysError.hh @echo Compiling XrdFrmAdminFiles.cc $(ECHO)$(CC) -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdFrmAdminFiles.o XrdFrmAdminFiles.cc $(OBJDIR)/XrdFrmAdminFind.o: XrdFrmAdmin.hh XrdFrmAdminFind.cc \ XrdFrmConfig.hh XrdFrmFiles.hh \ - XrdFrmTrace.hh XrdOssPath.hh \ + XrdFrmTrace.hh XrdSysError.hh \ XrdOucArgs.hh XrdOucNSWalk.hh @echo Compiling XrdFrmAdminFind.cc $(ECHO)$(CC) -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdFrmAdminFind.o XrdFrmAdminFind.cc @@ -161,7 +175,7 @@ $(OBJDIR)/XrdFrmAdminMain.o: XrdFrmAdmin.hh XrdFrmAdminMain.cc \ $(OBJDIR)/XrdFrmAdminQuery.o: XrdFrmAdmin.hh XrdFrmAdminQuery.cc \ XrdFrmConfig.hh XrdFrmFiles.hh \ - XrdFrmTrace.hh \ + XrdFrmTrace.hh XrdSysError.hh \ XrdOssPath.hh XrdOssSpace.hh \ XrdOucArgs.hh XrdOucNSWalk.hh \ XrdOucTList.hh @@ -170,7 +184,7 @@ $(OBJDIR)/XrdFrmAdminQuery.o: XrdFrmAdmin.hh XrdFrmAdminQuery.cc \ $(OBJDIR)/XrdFrmAdminUnlink.o: XrdFrmAdmin.hh XrdFrmAdminUnlink.cc \ XrdFrmConfig.hh XrdFrmTrace.hh \ - XrdFrmUtils.hh \ + XrdFrmUtils.hh XrdSysError.hh \ XrdOss.hh XrdOssPath.hh \ XrdOucNSWalk.hh XrdCmsNotify.hh @echo Compiling XrdFrmAdminUnlink.cc @@ -179,25 +193,29 @@ $(OBJDIR)/XrdFrmAdminUnlink.o: XrdFrmAdmin.hh XrdFrmAdminUnlink.cc \ $(OBJDIR)/XrdFrmConfig.o: XrdFrmConfig.hh XrdFrmConfig.cc XrdFrmTrace.hh \ XrdCmsNotify.hh \ XrdInfo.hh XrdNetDNS.hh XrdOss.hh \ - XrdOuca2x.hh XrdOucEnv.hh XrdOucStream.hh \ + XrdOuca2x.hh XrdOucEnv.hh XrdOucExport.hh \ XrdOucUtils.hh XrdOucName2Name.hh XrdOucMsubs.hh \ - XrdOucProg.hh XrdOucTList.hh XrdOssSpace.hh \ + XrdOucPList.hh XrdOucProg.hh \ + XrdOucStream.hh XrdOucTList.hh XrdOssSpace.hh \ XrdSysError.hh XrdSysHeaders.hh XrdSysLogger.hh \ XrdSysTimer.hh XrdSysPlugin.hh XrdSysPthread.hh \ - XrdSysPlatform.hh XrdXrootdMonitor.hh + XrdSysPlatform.hh \ + XrdXrootdMonitor.hh @echo Compiling XrdFrmConfig.cc $(ECHO)$(CC) -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdFrmConfig.o XrdFrmConfig.cc $(OBJDIR)/XrdFrmFiles.o: XrdFrmFiles.hh XrdFrmFiles.cc XrdFrmTrace.hh \ XrdFrmConfig.hh XrdFrmUtils.hh XrdOssPath.hh \ - XrdOucHash.hh XrdOucNSWalk.hh + XrdOucHash.hh XrdOucNSWalk.hh \ + XrdSysError.hh XrdSysPlatform.hh @echo Compiling XrdFrmFiles.cc $(ECHO)$(CC) -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdFrmFiles.o XrdFrmFiles.cc $(OBJDIR)/XrdFrmPstg.o: XrdFrmPstg.hh XrdFrmPstg.cc XrdFrmTrace.hh \ XrdFrmConfig.hh XrdFrmPstgReq.hh XrdFrmPstgXfr.hh \ XrdNetMsg.hh XrdOucStream.hh \ - XrdSysHeaders.hh XrdSysPlatform.hh XrdSysPthread.hh + XrdSysHeaders.hh XrdSysPlatform.hh XrdSysPthread.hh \ + XrdSysError.hh @echo Compiling XrdFrmPstg.cc $(ECHO)$(CC) -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdFrmPstg.o XrdFrmPstg.cc @@ -232,6 +250,31 @@ $(OBJDIR)/XrdFrmPstgXfr.o: XrdFrmPstgXfr.cc XrdFrmPstgXfr.hh \ @echo Compiling XrdFrmPstgXfr.cc $(ECHO)$(CC) -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdFrmPstgXfr.o XrdFrmPstgXfr.cc +$(OBJDIR)/XrdFrmPurge.o: XrdFrmPurge.hh XrdFrmPurge.cc XrdFrmTrace.hh \ + XrdFrmConfig.hh XrdFrmFiles.hh XrdFrmTSort.hh \ + XrdCmsNotify.hh \ + XrdOss.hh XrdOssPath.hh XrdOssSpace.hh \ + XrdOucHash.hh XrdOucNSWalk.hh XrdOucUtils.hh \ + XrdOucProg.hh XrdOucStream.hh XrdSysError.hh \ + XrdSysPlatform.hh + @echo Compiling XrdFrmPurge.cc + $(ECHO)$(CC) -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdFrmPurge.o XrdFrmPurge.cc + +$(OBJDIR)/XrdFrmPurgMain.o: XrdFrmPurgMain.cc \ + XrdFrmConfig.hh XrdFrmPurge.hh \ + XrdFrmTrace.hh \ + XrdNetOpts.hh XrdNetSocket.hh \ + XrdOucUtils.cc \ + XrdSysError.hh XrdSysHeaders.hh \ + XrdSysLogger.hh XrdSysPthread.hh \ + XrdSysTimer.hh + @echo Compiling XrdFrmPurgMain.cc + $(ECHO)$(CC) -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdFrmPurgMain.o XrdFrmPurgMain.cc + +$(OBJDIR)/XrdFrmTSort.o: XrdFrmTSort.hh XrdFrmTSort.cc XrdFrmFiles.hh + @echo Compiling XrdFrmTSort.cc + $(ECHO)$(CC) -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdFrmTSort.o XrdFrmTSort.cc + $(OBJDIR)/XrdFrmUtils.o: XrdFrmUtils.hh XrdFrmUtils.cc XrdFrmTrace.hh \ XrdSysError.hh @echo Compiling XrdFrmUtils.cc diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminAudit.cc b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminAudit.cc index 44fdb5f53eec22d7ff1b5d642d586dd342f6696d..6375ac4979b15fd45c4290ee9726af0c559f040c 100644 --- a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminAudit.cc +++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminAudit.cc @@ -39,16 +39,16 @@ int XrdFrmAdmin::AuditNameNB(XrdFrmFileset *sP) // Report what is orphaned // - if (sP->File[XrdOssPath::isFail]) - {num++; Msg("Orphaned fail file: ", sP->File[XrdOssPath::isFail]->Path);} - if (sP->File[XrdOssPath::isLock]) - {num++; Msg("Orphaned lock file: ", sP->File[XrdOssPath::isLock]->Path);} - if (sP->File[XrdOssPath::isPfn ] ) - {num++; Msg("Orphaned pfn file: ", sP->File[XrdOssPath::isPfn ] ->Path); - Msg("PFN file refers to: ", sP->File[XrdOssPath::isPfn ] ->Link); + if (sP->failFile()) + {num++; Msg("Orphaned fail file: ", sP->failPath());} + if (sP->lockFile()) + {num++; Msg("Orphaned lock file: ", sP->lockPath());} + if (sP->pfnFile() ) + {num++; Msg("Orphaned pfn file: ", sP->pfnPath()); + Msg("PFN file refers to: ", sP->pfnFile()->Link); } - if (sP->File[XrdOssPath::isPin ] ) - {num++; Msg("Orphaned pin file: ", sP->File[XrdOssPath::isPin ] ->Path);} + if (sP->pinFile() ) + {num++; Msg("Orphaned pin file: ", sP->pinPath());} // Return if no fix is needed, otherwise check if we should ask before removal // @@ -81,8 +81,8 @@ int XrdFrmAdmin::AuditNameNF(XrdFrmFileset *sP) // Indicate what is wrong // - Msg("Dangling link: ", sP->File[XrdOssPath::isBase]->Path); - Msg("Missing target: ", sP->File[XrdOssPath::isBase]->Link); + Msg("Dangling link: ", sP->basePath()); + Msg("Missing target: ", sP->baseFile()->Link); numProb++; // Return if no fix is needed, otherwise check if we should ask before removal @@ -95,8 +95,8 @@ int XrdFrmAdmin::AuditNameNF(XrdFrmFileset *sP) // Remove the symlink and associated files // - if (unlink(sP->File[XrdOssPath::isBase]->Path)) - Emsg(errno,"remove symlink", sP->File[XrdOssPath::isBase]->Path); + if (unlink(sP->basePath())) + Emsg(errno,"remove symlink", sP->basePath()); else if (AuditRemove(sP)) {Msg("Symlink removed."); numFix++; @@ -115,7 +115,7 @@ int XrdFrmAdmin::AuditNameNL(XrdFrmFileset *sP) // Indicate what is wrong // - Msg("Missing lock file: ", sP->File[XrdOssPath::isBase]->Path); + Msg("Missing lock file: ", sP->basePath()); numProb++; // Return if no fix is needed, otherwise check if we should ask before removal @@ -128,7 +128,7 @@ int XrdFrmAdmin::AuditNameNL(XrdFrmFileset *sP) // Create the lock file // - if (!mkFile(mkLF|isPFN, sP->File[XrdOssPath::isBase]->Path)) return 1; + if (!mkFile(mkLF|isPFN, sP->basePath())) return 1; Msg("Lock file created."); numFix++; return 1; @@ -156,12 +156,12 @@ int XrdFrmAdmin::AuditNames() if (!Config.LocalPath(lDir, pDir, sizeof(pDir))) {finalRC = 4; return 1;} fP = new XrdFrmFiles(pDir, opts); while(Act && (sP = fP->Get(ec,1))) - {if (!(sP->File[XrdOssPath::isBase])) Act = AuditNameNB(sP); - else {if (sP->File[XrdOssPath::isBase]->Type == XrdOucNSWalk::NSEnt::isLink) + {if (!(sP->baseFile())) Act = AuditNameNB(sP); + else {if (sP->baseFile()->Type == XrdOucNSWalk::NSEnt::isLink) Act = AuditNameNF(sP); - if (Act && Opt.MPType && !(sP->File[XrdOssPath::isLock])) + if (Act && Opt.MPType && !(sP->lockFile())) Act = AuditNameNL(sP); - if (Act && sP->File[XrdOssPath::isBase]->Link && isXA(sP->File[XrdOssPath::isBase])) + if (Act && sP->baseFile()->Link && isXA(sP->baseFile())) Act = AuditNameXA(sP); } } @@ -189,18 +189,18 @@ int XrdFrmAdmin::AuditNameXA(XrdFrmFileset *sP) // Make sure there is a PFN file here // - strcpy(Path, sP->File[XrdOssPath::isBase]->Link); strcat(Path, ".pfn"); + strcpy(Path, sP->baseFile()->Link); strcat(Path, ".pfn"); if (lstat(Path,&buf)) {if (errno != ENOENT) {Emsg(errno, "stat ", Path); return AuditNameXL(sP,-1);} - Msg("Missing pfn link to ", sP->File[XrdOssPath::isBase]->Path); + Msg("Missing pfn link to ", sP->basePath()); return AuditNameXL(sP,0); } // Make sure the PFN file is a link // if ((buf.st_mode & S_IFMT) != S_IFLNK) - {Msg("Invalid pfn file for ", sP->File[XrdOssPath::isBase]->Path); + {Msg("Invalid pfn file for ", sP->basePath()); return AuditNameXL(sP,1); } @@ -209,8 +209,8 @@ int XrdFrmAdmin::AuditNameXA(XrdFrmFileset *sP) if ((n = readlink(Path, lkbuff, sizeof(lkbuff)-1)) < 0) {Emsg(errno, "read link from ", Path); return AuditNameXL(sP,-1);} lkbuff[n] = '\0'; - if (strcmp(sP->File[XrdOssPath::isBase]->Path, lkbuff)) - {Msg("Incorrect pfn link to ", sP->File[XrdOssPath::isBase]->Path); + if (strcmp(sP->basePath(), lkbuff)) + {Msg("Incorrect pfn link to ", sP->basePath()); return AuditNameXL(sP,1); } @@ -241,9 +241,9 @@ int XrdFrmAdmin::AuditNameXL(XrdFrmFileset *sP, int dorm) // Create the pfn symlink // - strcpy(Path, sP->File[XrdOssPath::isBase]->Link); strcat(Path, ".pfn"); + strcpy(Path, sP->baseFile()->Link); strcat(Path, ".pfn"); if (dorm) unlink(Path); - if (symlink(sP->File[XrdOssPath::isBase]->Path, Path)) + if (symlink(sP->basePath(), Path)) {Emsg(errno, "create symlink ", Path); return 1;} Msg("pfn symlink created."); numFix++; @@ -260,20 +260,20 @@ int XrdFrmAdmin::AuditRemove(XrdFrmFileset *sP) // Remove the orphaned files // - if (sP->File[XrdOssPath::isFail]) - {if (unlink(sP->File[XrdOssPath::isFail]->Path)) Emsg(errno,"remove fail file."); + if (sP->failFile()) + {if (unlink(sP->failPath())) Emsg(errno,"remove fail file."); else rem++; } - if (sP->File[XrdOssPath::isLock]) - {if (unlink(sP->File[XrdOssPath::isLock]->Path)) Emsg(errno,"remove lock file."); + if (sP->lockFile()) + {if (unlink(sP->lockPath())) Emsg(errno,"remove lock file."); else rem++; } - if (sP->File[XrdOssPath::isPin ]) - {if (unlink(sP->File[XrdOssPath::isPin ] ->Path)) Emsg(errno,"remove pin file."); + if (sP-> pinFile()) + {if (unlink(sP-> pinPath())) Emsg(errno,"remove pin file."); else rem++; } - if (sP->File[XrdOssPath::isPfn ]) - {if (unlink(sP->File[XrdOssPath::isPfn ] ->Path)) Emsg(errno,"remove pfn file."); + if (sP-> pfnFile()) + {if (unlink(sP-> pinPath())) Emsg(errno,"remove pfn file."); else rem++; } @@ -471,9 +471,9 @@ int XrdFrmAdmin::AuditSpaceXA(const char *Space, const char *Path) // Go and check out the files // while(Act && (sP = fP->Get(ec,1))) - { if (!(sP->File[XrdOssPath::isBase])) Act = AuditNameNB(sP); - else if (!(sP->File[XrdOssPath::isPfn ] )) Act = AuditSpaceXANB(sP); - else {numFiles++; numBytes += sP->File[XrdOssPath::isBase]->Stat.st_size; continue;} + { if (!(sP->baseFile())) Act = AuditNameNB(sP); + else if (!(sP-> pfnFile())) Act = AuditSpaceXANB(sP); + else {numFiles++; numBytes += sP->baseFile()->Stat.st_size; continue;} } // All done @@ -494,11 +494,11 @@ int XrdFrmAdmin::AuditSpaceXANB(XrdFrmFileset *sP) // Update statistics which we may have to correct later // - numProb++; numFiles++; numBytes += sP->File[XrdOssPath::isBase]->Stat.st_size; + numProb++; numFiles++; numBytes += sP->baseFile()->Stat.st_size; // Report what is orphaned // - Msg("Missing pfn file for data file ", sP->File[XrdOssPath::isBase]->Path); + Msg("Missing pfn file for data file ", sP->basePath()); // Return if no fix is needed, otherwise check if we should ask before removal // @@ -510,8 +510,8 @@ int XrdFrmAdmin::AuditSpaceXANB(XrdFrmFileset *sP) // Remove the file // - if (unlink(sP->File[XrdOssPath::isBase]->Path)) Emsg(errno,"remove data file ", sP->File[XrdOssPath::isBase]->Path); - else {numFix++; numFiles--; numBytes -= sP->File[XrdOssPath::isBase]->Stat.st_size;} + if (unlink(sP->basePath())) Emsg(errno,"remove data file ", sP->basePath()); + else {numFix++; numFiles--; numBytes -= sP->baseFile()->Stat.st_size;} return 1; } @@ -674,8 +674,8 @@ int XrdFrmAdmin::AuditUsageXA(const char *Path, const char *Space) // Go and check out the files // while((sP = fP->Get(ec))) - {if ((sP->File[XrdOssPath::isBase])) - {numFiles++; numBytes += sP->File[XrdOssPath::isBase]->Stat.st_size;} + {if ((sP->baseFile())) + {numFiles++; numBytes += sP->baseFile()->Stat.st_size;} } // All done diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminFiles.cc b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminFiles.cc index 0e06379054b2b0ff859ea828e9d0a2b35b369dfa..f734e800737898eb1e04b48a33f48c835808fa07 100644 --- a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminFiles.cc +++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminFiles.cc @@ -26,7 +26,6 @@ const char *XrdFrmAdminFilesCVSID = "$Id$"; #include "XrdFrm/XrdFrmConfig.hh" #include "XrdFrm/XrdFrmFiles.hh" #include "XrdFrm/XrdFrmUtils.hh" -#include "XrdOss/XrdOssPath.hh" using namespace XrdFrm; @@ -58,8 +57,7 @@ int XrdFrmAdmin::mkLock(const char *Lfn) // fP = new XrdFrmFiles(Pfn, opts); while((sP = fP->Get(ec,1))) - {if (sP->File[XrdOssPath::isBase] - && mkFile(mkLF|isPFN, sP->File[XrdOssPath::isBase]->Path)) numFiles++;} + {if (sP->baseFile() && mkFile(mkLF|isPFN, sP->basePath())) numFiles++;} // All done // @@ -96,8 +94,7 @@ int XrdFrmAdmin::mkPin(const char *Lfn, const char *Pdata, int Pdlen) // fP = new XrdFrmFiles(Pfn, opts); while((sP = fP->Get(ec,1))) - {if (sP->File[XrdOssPath::isBase] - && mkFile(mkPF|isPFN,sP->File[XrdOssPath::isBase]->Path,Pdata,Pdlen)) + {if (sP->baseFile() && mkFile(mkPF|isPFN,sP->basePath(),Pdata,Pdlen)) numFiles++; } diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminFind.cc b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminFind.cc index 2490332f914014e569b51314278bdb242dfa6618..0a63a84b0fbbbb99459e9334663964e543dc781b 100644 --- a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminFind.cc +++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminFind.cc @@ -19,7 +19,6 @@ #include "XrdFrm/XrdFrmConfig.hh" #include "XrdFrm/XrdFrmFiles.hh" #include "XrdFrm/XrdFrmTrace.hh" -#include "XrdOss/XrdOssPath.hh" #include "XrdOuc/XrdOucArgs.hh" #include "XrdOuc/XrdOucNSWalk.hh" @@ -78,8 +77,7 @@ int XrdFrmAdmin::FindNolk(XrdOucArgs &Spec) do {if (!Config.LocalPath(lDir, pDir, sizeof(pDir))) continue; fP = new XrdFrmFiles(pDir, opts); while((sP = fP->Get(ec))) - {if (!(sP->File[XrdOssPath::isLock])) - {Msg(sP->File[XrdOssPath::isBase]->Path); num++;} + {if (!(sP->lockFile())) {Msg(sP->basePath()); num++;} } if (ec) rc = 4; delete fP; @@ -110,13 +108,13 @@ int XrdFrmAdmin::FindUnmi(XrdOucArgs &Spec) do {if (!Config.LocalPath(lDir, pDir, sizeof(pDir))) continue; fP = new XrdFrmFiles(pDir, opts); while((sP = fP->Get(ec))) - { if (!(sP->File[XrdOssPath::isLock])) + { if (!(sP->lockFile())) Why = "Unmigrated; no lock file: "; - else if (sP->File[XrdOssPath::isBase]->Stat.st_mtime < - sP->File[XrdOssPath::isLock]->Stat.st_mtime) + else if (sP->baseFile()->Stat.st_mtime < + sP->lockFile()->Stat.st_mtime) Why="Unmigrated; modified: "; else continue; - Msg(Why, sP->File[XrdOssPath::isBase]->Path); num++; + Msg(Why, sP->basePath()); num++; } if (ec) rc = 4; delete fP; diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminQuery.cc b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminQuery.cc index 50ab2455b449b4ce9914ea45ce2b38d059dfcfac..6d3ad80e6a7348b1f4313677e239a5d9978c4d9a 100644 --- a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminQuery.cc +++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmAdminQuery.cc @@ -128,10 +128,10 @@ int XrdFrmAdmin::QuerySpace(XrdOucArgs &Spec) } else{fP = new XrdFrmFiles(pfn, opts); while((sP = fP->Get(ec,1))) - {if (sP->File[XrdOssPath::isBase]) - QuerySpace(sP->File[XrdOssPath::isBase]->Path, - sP->File[XrdOssPath::isBase]->Link, - sP->File[XrdOssPath::isBase]->Lksz); + {if (sP->baseFile()) + QuerySpace(sP->basePath(), + sP->baseFile()->Link, + sP->baseFile()->Lksz); } if (ec) finalRC = 4; delete fP; diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmConfig.cc b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmConfig.cc index f6ec363ab874f4da92f770a7e0f5382adbed64b1..c0dc46f0a14eb8fc15b560097cfc31eba7337f62 100644 --- a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmConfig.cc +++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmConfig.cc @@ -25,17 +25,21 @@ const char *XrdFrmConfigCVSID = "$Id$"; #include "Xrd/XrdInfo.hh" #include "XrdCms/XrdCmsNotify.hh" #include "XrdFrm/XrdFrmConfig.hh" -#include "XrdFrm/XrdFrmTrace.hh" +#include "XrdFrm/XrdFrmTrace.hh" // Add to GNUmake +#include "XrdFrm/XrdFrmUtils.hh" #include "XrdNet/XrdNetDNS.hh" #include "XrdOss/XrdOss.hh" #include "XrdOss/XrdOssSpace.hh" #include "XrdOuc/XrdOuca2x.hh" #include "XrdOuc/XrdOucEnv.hh" +#include "XrdOuc/XrdOucExport.hh" #include "XrdOuc/XrdOucMsubs.hh" #include "XrdOuc/XrdOucName2Name.hh" #include "XrdOuc/XrdOucProg.hh" #include "XrdOuc/XrdOucStream.hh" +#include "XrdOuc/XrdOucPList.hh" #include "XrdOuc/XrdOucTList.hh" +#include "XrdOuc/XrdOucTokenizer.hh" // Add to GNUmake #include "XrdOuc/XrdOucUtils.hh" #include "XrdSys/XrdSysError.hh" #include "XrdSys/XrdSysHeaders.hh" @@ -100,7 +104,7 @@ void *XrdFrmConfigMum(void *parg) // All done // theSE->mySem.Post(); - pthread_exit((void *)0); +// pthread_exit((void *)0); return (void *)0; } @@ -123,6 +127,7 @@ void *XrdLogWorker(void *parg) /******************************************************************************/ XrdFrmConfig::XrdFrmConfig(SubSys ss, const char *vopts, const char *uinfo) + : dfltPolicy("*", -2, -3, 72000, 0) { char *sP, buff[128]; @@ -144,8 +149,18 @@ XrdFrmConfig::XrdFrmConfig(SubSys ss, const char *vopts, const char *uinfo) cmsPath = 0; monStage = 0; sSpec = 0; - Solitary = 0; + isOTO = 0; + Test = 0; + Verbose = 0; + pathList = 0; + spacList = 0; lockFN = "DIR_LOCK"; // May be ".DIR_LOCK" if hidden + cmdHold = -1; + cmdFree = 0; + pVecNum = 0; + pProg = 0; + Fix = 0; + dirHold = 40*60*60; myUid = geteuid(); myGid = getegid(); @@ -170,7 +185,7 @@ XrdFrmConfig::XrdFrmConfig(SubSys ss, const char *vopts, const char *uinfo) if (ss == ssAdmin) {myFrmid = "admin"; myFrmID = "ADMIN";} else if (ss == ssMigr) {myFrmid = "migr"; myFrmID = "MIGR";} else if (ss == ssPstg) {myFrmid = "pstg"; myFrmID = "PSTG";} - else if (ss == ssPurg) {myFrmid = "purg"; myFrmID = "PURG";} + else if (ss == ssPurg) {myFrmid = "purge"; myFrmID = "PURG";} else {myFrmid = "frm"; myFrmID = "FRM";} // Set correct error prefix @@ -181,8 +196,7 @@ XrdFrmConfig::XrdFrmConfig(SubSys ss, const char *vopts, const char *uinfo) // Set correct oss type // - sprintf(buff, "XRDOSSTYPE=%s", myFrmid); - putenv(strdup(buff)); + XrdOucEnv::Export("XRDOSSTYPE", myFrmid); // Set correct option prefix // @@ -200,7 +214,7 @@ int XrdFrmConfig::Configure(int argc, char **argv, int (*ppf)()) { extern XrdOss *XrdOssGetSS(XrdSysLogger *, const char *, const char *); XrdFrmConfigSE theSE; - int n, retc, isMum = 0, myXfrMax = -1, NoGo = 0, Verbose = 0; + int n, retc, isMum = 0, myXfrMax = -1, NoGo = 0; const char *temp; char c, buff[1024], *logfn = 0; long long logkeep = 0; @@ -212,6 +226,7 @@ int XrdFrmConfig::Configure(int argc, char **argv, int (*ppf)()) retc = strlen(argv[0]); while(retc--) if (argv[0][retc] == '/') break; myProg = &argv[0][retc+1]; + vectArg = argv; numcArg = argc; // Process the options // @@ -224,7 +239,9 @@ int XrdFrmConfig::Configure(int argc, char **argv, int (*ppf)()) ConfigFN = strdup(optarg); break; case 'd': Trace.What |= TRACE_ALL; - putenv((char *)"XRDDEBUG=1"); + XrdOucEnv::Export("XRDDEBUG","1"); + break; + case 'f': Fix = 1; break; case 'h': Usage(0); case 'k': n = strlen(optarg)-1; @@ -242,8 +259,13 @@ int XrdFrmConfig::Configure(int argc, char **argv, int (*ppf)()) break; case 'n': myInsName = optarg; break; + case 'O': isOTO = 1; + if (!ConfigOTO(optarg)) Usage(1); + break; case 's': sSpec = 1; break; + case 'T': Test = 1; + break; case 'v': Verbose = 1; break; case 'w': if (XrdOuca2x::a2tm(Say,"wait time",optarg,&WaitTime)) @@ -289,14 +311,14 @@ int XrdFrmConfig::Configure(int argc, char **argv, int (*ppf)()) sprintf(buff,"XRDINSTANCE=%s %s@%s",myProg,(myInst ? myInst:"anon"),myName); putenv(strdup(buff)); // XRDINSTANCE myInstance = strdup(index(buff,'=')+1); - sprintf(buff,"XRDHOST=%s", myName); putenv(strdup(buff)); - sprintf(buff,"XRDPROG=%s", myProg); putenv(strdup(buff)); - if (myInsName) - {sprintf(buff, "XRDNAME=%s", myInsName); putenv(strdup(buff));} + XrdOucEnv::Export("XRDHOST", myName); + XrdOucEnv::Export("XRDPROG", myProg); + if (myInsName) XrdOucEnv::Export("XRDNAME", myInsName); // We need to divert the output if we are in admin mode with no logfile // - if (!logfn && ssID == ssAdmin && !Verbose) isMum = ConfigMum(theSE); + if (!logfn && (ssID == ssAdmin || isOTO) && !Trace.What) + isMum = ConfigMum(theSE); // Put out the herald // @@ -318,21 +340,27 @@ int XrdFrmConfig::Configure(int argc, char **argv, int (*ppf)()) // Obtain and configure the oss (lightweight option only) // if (!isAgent) - {putenv(strdup("XRDREDIRECT=Q")); - Solitary = 1; + {XrdOucEnv::Export("XRDREDIRECT", "Q"); + XrdOucEnv::Export("XRDOSSTYPE", myFrmID); + if (ssID == ssMigr || ssID == ssPurg) + XrdOucEnv::Export("XRDOSSCSCAN", "off"); if (!NoGo && !(ossFS=XrdOssGetSS(Say.logger(),ConfigFN,ossLib))) NoGo=1; } -// Configure the admin component -// - if (!NoGo && ssID == ssAdmin - && (ConfigN2N() || ConfigMss())) NoGo = 1; - -// Configure the pstg component -// - if (!NoGo && ssID == ssPstg && !isAgent - && (ConfigN2N() || !XrdXrootdMonitor::Init(0,&Say) - || !(xfrVec = ConfigCmd("xfrcmd", xfrCmd)))) NoGo = 1; +// Configure each specific component +// + if (!NoGo) switch(ssID) + {case ssAdmin: NoGo = (ConfigN2N() || ConfigMss()); + break; + case ssMigr: NoGo = (ConfigN2N() || ConfigMP("migratable")); + break; + case ssPstg: if (!isAgent && (ConfigN2N() || ConfigMss() + || !(xfrVec = ConfigCmd("xfrcmd", xfrCmd)))) NoGo = 1; + break; + case ssPurg: NoGo = (ConfigN2N() || ConfigMP("purgeable")); + break; + default: break; + } // If we have a post-processing routine, invoke it // @@ -364,7 +392,7 @@ int XrdFrmConfig::Configure(int argc, char **argv, int (*ppf)()) // return !NoGo; } - + /******************************************************************************/ /* Public: L o c a l P a t h */ /******************************************************************************/ @@ -465,58 +493,106 @@ XrdOucMsubs *XrdFrmConfig::ConfigCmd(const char *cname, char *cdata) } /******************************************************************************/ -/* Private: C o n f i g M s s */ +/* Private: C o n f i g M P */ /******************************************************************************/ - -int XrdFrmConfig::ConfigMss() -{ - if (MSSCmd) - {MSSProg = new XrdOucProg(&Say); - if (MSSProg->Setup(MSSCmd)) return 1; - } - return 0; -} -/******************************************************************************/ -/* Private: C o n f i g N 2 N */ -/******************************************************************************/ - -int XrdFrmConfig::ConfigN2N() +int XrdFrmConfig::ConfigMP(const char *pType) { - XrdSysPlugin *myLib; - XrdOucName2Name *(*ep)(XrdOucgetName2NameArgs); + EPNAME("ConfigMP"); + extern XrdOucPListAnchor *XrdOssRPList; + XrdOucTList *nP, *tP, *mypList = 0, *expList = 0; + char pDir[MAXPATHLEN+1]; + long long pOpts, xOpt = (*pType == 'm' ? XRDEXP_MIG : XRDEXP_PURGE); + int i, NoGo = 0; + +// Verify that we have an RPList +// + if (!XrdOssRPList) + {Say.Emsg("Config", "Cannot determine", pType, "paths."); return 1;} + +// Parse the arguments which consist of space names and paths +// + for (i = nextArg; i < numcArg; i++) + {char *psVal = vectArg[i]; + int psLen = strlen(psVal); + if (*psVal == '/') + {pOpts = XrdOssRPList->Find(psVal); + if (pOpts & xOpt) mypList = InsertPL(mypList, psVal, psLen, + (pOpts & XRDEXP_NOTRW ? 0 : 1)); + else {Say.Say("Config", psVal, "not marked", pType); NoGo = 1;} + } else { + VPInfo *vP = VPList; + while(vP && strcmp(psVal, vP->Name)) vP = vP->Next; + if (vP) spacList = new XrdOucTList(psVal, psLen, spacList); + else {Say.Emsg("Config", "Space", psVal, "not defined."); + NoGo = 1; + } + } + } -// If we have no library path then use the default method (this will always -// succeed). +// Check if we should continue // - if (!N2N_Lib) - {the_N2N = XrdOucgetName2Name(&Say, ConfigFN, "", LocalRoot, RemoteRoot); - if (LocalRoot) lcl_N2N = the_N2N; - if (RemoteRoot) rmt_N2N = the_N2N; - return 0; - } + if (NoGo) return 1; -// Create a pluin object (we will throw this away without deletion because -// the library must stay open but we never want to reference it again). +// Get correct path list // - if (!(myLib = new XrdSysPlugin(&Say, N2N_Lib))) return 1; + if (!mypList) + {XrdOucPList *fP = XrdOssRPList->First(); + short sval[2]; + while(fP) + {sval[0] = (fP->Flag() & XRDEXP_NOTRW ? 0 : 1); + sval[1] = fP->Plen(); + if (fP->Flag() & xOpt) + mypList = new XrdOucTList(fP->Path(), sval, mypList); + else + expList = new XrdOucTList(fP->Path(), sval, expList); + fP = fP->Next(); + } + if (!mypList) + {Say.Emsg("Config", "No", pType, "paths found."); return 1;} + } -// Now get the entry point of the object creator -// - ep = (XrdOucName2Name *(*)(XrdOucgetName2NameArgs))(myLib->getPlugin("XrdOucgetName2Name")); - if (!ep) return 1; +// Now we need to construct a search list which may include excludes which +// hapen when we get nested subtrees with different options +// + while((tP = mypList)) + {if (!LocalPath(tP->text, pDir, sizeof(pDir))) NoGo = 1; + else {pathList = new VPInfo(pDir, int(tP->sval[0]), pathList); + DEBUG("Will scan " <<(tP->sval[0]?"r/w: ":"r/o: ") <<pDir); + nP = expList; + while(nP) + {if (!strncmp(tP->text, nP->text, tP->sval[1])) + InsertXD(nP->text); + nP = nP->next; + } + mypList = tP->next; delete tP; + } + } +// Delete the explist +// + while((tP = expList)) {expList = tP->next; delete tP;} -// Get the Object now +// All done now // - lcl_N2N = rmt_N2N = the_N2N = ep(&Say, ConfigFN, - (N2N_Parms ? N2N_Parms : ""), - LocalRoot, RemoteRoot); - return lcl_N2N == 0; + return NoGo; +} + +/******************************************************************************/ +/* Private: C o n f i g M s s */ +/******************************************************************************/ + +int XrdFrmConfig::ConfigMss() +{ + if (MSSCmd) + {MSSProg = new XrdOucProg(&Say); + if (MSSProg->Setup(MSSCmd)) return 1; + } + return 0; } /******************************************************************************/ -/* C o n f i g M u m */ +/* Private: C o n f i g M u m */ /******************************************************************************/ int XrdFrmConfig::ConfigMum(XrdFrmConfigSE &theSE) @@ -568,25 +644,101 @@ int XrdFrmConfig::ConfigMum(XrdFrmConfigSE &theSE) FD.stdErr = -1; return 1; } + +/******************************************************************************/ +/* Private: C o n f i g N 2 N */ +/******************************************************************************/ + +int XrdFrmConfig::ConfigN2N() +{ + XrdSysPlugin *myLib; + XrdOucName2Name *(*ep)(XrdOucgetName2NameArgs); + +// If we have no library path then use the default method (this will always +// succeed). +// + if (!N2N_Lib) + {the_N2N = XrdOucgetName2Name(&Say, ConfigFN, "", LocalRoot, RemoteRoot); + if (LocalRoot) lcl_N2N = the_N2N; + if (RemoteRoot) rmt_N2N = the_N2N; + return 0; + } + +// Create a pluin object (we will throw this away without deletion because +// the library must stay open but we never want to reference it again). +// + if (!(myLib = new XrdSysPlugin(&Say, N2N_Lib))) return 1; + +// Now get the entry point of the object creator +// + ep = (XrdOucName2Name *(*)(XrdOucgetName2NameArgs))(myLib->getPlugin("XrdOucgetName2Name")); + if (!ep) return 1; + + +// Get the Object now +// + lcl_N2N = rmt_N2N = the_N2N = ep(&Say, ConfigFN, + (N2N_Parms ? N2N_Parms : ""), + LocalRoot, RemoteRoot); + return lcl_N2N == 0; +} +/******************************************************************************/ +/* C o n f i g O T O */ +/******************************************************************************/ + +int XrdFrmConfig::ConfigOTO(char *Parms) +{ + char *Comma; + +// Pick up free argument +// + if ((Comma = index(Parms, ','))) *Comma = '\0'; + if (XrdOuca2x::a2sp(Say, "free value", Parms, &cmdFree, 1)) return 0; + +// Pick up hold argument +// + if (!Comma || !(*(Comma+1))) return 1; + if (*(Comma+1) == ',') Comma++; + else {Parms = Comma+1; + if ((Comma = index(Parms, ','))) *Comma = '\0'; + if (XrdOuca2x::a2i(Say,"hold value",Parms,&cmdHold,0)) return 0; + } + +// All done +// + return 1; +} + /******************************************************************************/ /* C o n f i g P a t h s */ /******************************************************************************/ int XrdFrmConfig::ConfigPaths() { - char *xPath, *yPath, buff[MAXPATHLEN]; + char *xPath, *yPath, buff[MAXPATHLEN]; + const char *insName; int retc; // Establish the cmsd notification path // - if (!(xPath = AdminPath) && !(xPath = getenv("XRDADMINPATH"))) - xPath = (char *)"/tmp/"; - cmsPath = new XrdCmsNotify(&Say, xPath, myInsName, XrdCmsNotify::isServ); + // Set the directory where the meta information is to go -// - yPath = XrdOucUtils::genPath(xPath, myInsName, "frm"); +// XRDADMINPATH already contains the instance name + + if ( (!AdminPath) && (xPath = getenv("XRDADMINPATH"))) { + insName = 0; + } + else { + if (!(xPath = AdminPath)) + xPath = (char *)"/tmp/"; + insName = myInsName; + } + + cmsPath = new XrdCmsNotify(&Say, xPath, insName, XrdCmsNotify::isServ); + + yPath = XrdOucUtils::genPath(xPath, insName, "frm"); if (AdminPath) free(AdminPath); AdminPath = yPath; // Create the admin directory if it does not exists @@ -668,29 +820,50 @@ int XrdFrmConfig::ConfigXeq(char *var, int mbok) // Process directives specific to each subsystem // -// if (ssID == ssAdmin) -// {if (!strcmp(var, "oss.mssgwcmd" )) return Grab(var, &MSSCmd, 0); -// if (!strcmp(var, "oss.msscmd" )) return Grab(var, &MSSCmd, 0); -// } - - if (ssID == ssPstg || ssID == ssAdmin) - {if (!strcmp(var, "ofs.osslib" )) return Grab(var, &ossLib, 0); + if (ssID == ssAdmin) + { + if (!strcmp(var, "ofs.osslib" )) return Grab(var, &ossLib, 0); if (!strcmp(var, "oss.cache" )) return xcache(); if (!strcmp(var, "oss.localroot" )) return Grab(var, &LocalRoot, 0); if (!strcmp(var, "oss.namelib" )) return xnml(); if (!strcmp(var, "oss.remoteroot")) return Grab(var, &LocalRoot, 0); +// if (!strcmp(var, "oss.mssgwcmd" )) return Grab(var, &MSSCmd, 0); +// if (!strcmp(var, "oss.msscmd" )) return Grab(var, &MSSCmd, 0); + } + + if (ssID == ssMigr) + { + if (!strcmp(var, "stopfile" )) return Grab(var, &StopFile, 0); + if (!strcmp(var, "waittime" )) return xwtm(); } if (ssID == ssPstg) { - if (!strcmp(var, "xrootd.monitor")) return xmon(); + if (!strcmp(var, "ofs.osslib" )) return Grab(var, &ossLib, 0); + if (!strcmp(var, "oss.cache" )) return xcache(); + if (!strcmp(var, "oss.localroot" )) return Grab(var, &LocalRoot, 0); + if (!strcmp(var, "oss.namelib" )) return xnml(); + if (!strcmp(var, "oss.remoteroot")) return Grab(var, &LocalRoot, 0); + if (!strcmp(var, "stopfile" )) return Grab(var, &StopFile, 0); if (!strcmp(var, "waittime" )) return xwtm(); + if (!strcmp(var, "xrootd.monitor")) return xmon(); if (!strcmp(var, "xfrmax" )) return xmaxx(); if (!strcmp(var, "xfrcmd" )) return Grab(var, &xfrCmd, 1); - if (!strcmp(var, "stopfile" )) return Grab(var, &StopFile, 0); if (!strcmp(var, "queuepath" )) return Grab(var, &qPath, 0); } + if (ssID == ssPurg) + { + if (!strcmp(var, "dirhold" )) return xdpol(); + if (!strcmp(var, "oss.cache" )) return xcache(1); + if (!strcmp(var, "ofs.osslib" )) return Grab(var, &ossLib, 0); + if (!strcmp(var, "policy" )) return xpol(); + if (!strcmp(var, "polprog" )) return xpolprog(); + if (!strcmp(var, "queuepath" )) return Grab(var, &qPath, 0); + if (!strcmp(var, "stopfile" )) return Grab(var, &StopFile, 0); + if (!strcmp(var, "waittime" )) return xwtm(); + } + // No match found, complain. // if (!mbok) cFile->noEcho(); @@ -700,6 +873,19 @@ int XrdFrmConfig::ConfigXeq(char *var, int mbok) return 0; } +/******************************************************************************/ +/* Private: g e t T i m e */ +/******************************************************************************/ + +int XrdFrmConfig::getTime(const char *emsg, const char *item, int *val, + int minv, int maxv) +{ + if (strcmp(item, "forever")) + return XrdOuca2x::a2tm(Say, emsg, item, val, minv, maxv); + *val = -1; + return 0; +} + /******************************************************************************/ /* Private: G r a b */ /******************************************************************************/ @@ -743,6 +929,52 @@ int XrdFrmConfig::Grab(const char *var, char **Dest, int nosubs) return 0; } +/******************************************************************************/ +/* Private: I n s e r t P L */ +/******************************************************************************/ + +XrdOucTList *XrdFrmConfig::InsertPL(XrdOucTList *pL, const char *Path, + int Plen, int isRW) +{ + short sval[2] = {isRW, Plen}; + XrdOucTList *pP = 0, *tP = pL; + +// Find insertion point +// + while(tP && tP->sval[1] < Plen) {pP = tP; tP = tP->next;} + +// Insert new element +// + if (pP) pP->next = new XrdOucTList(Path, sval, tP); + else pL = new XrdOucTList(Path, sval, tP); + +// Return the new list +// + return pL; +} + +/******************************************************************************/ +/* Private: I n s e r t X D */ +/******************************************************************************/ + +void XrdFrmConfig::InsertXD(const char *Path) +{ + EPNAME("InsertXD"); + char pBuff[MAXPATHLEN], *pP; + int n = strlen(Path); + +// Make sure this does not end with a slash +// + strcpy(pBuff, Path); + pP = pBuff + n - 1; + while(*pP == '/' && pP != pBuff) {*pP-- = '\0'; n--;} + +// Insert this directory into the exclude list for the current path +// + pathList->Dir = new XrdOucTList(pBuff, n, pathList->Dir); + DEBUG("Excluding '" <<pBuff <<"'"); +} + /******************************************************************************/ /* Private: U s a g e */ /******************************************************************************/ @@ -753,6 +985,53 @@ void XrdFrmConfig::Usage(int rc) _exit(rc); } +/******************************************************************************/ +/* Private: x a p a t h */ +/******************************************************************************/ + +/* Function: xapath + + Purpose: To parse the directive: adminpath <path> [group] + + <path> the path of the FIFO to use for admin requests. + + group allows group access to the admin path + + Output: 0 upon success or !0 upon failure. +*/ + +int XrdFrmConfig::xapath() +{ + char *pval, *val; + mode_t mode = S_IRWXU; + +// Get the path +// + pval = cFile->GetWord(); + if (!pval || !pval[0]) + {Say.Emsg("Config", "adminpath not specified"); return 1;} + +// Make sure it's an absolute path +// + if (*pval != '/') + {Say.Emsg("Config", "adminpath not absolute"); return 1;} + +// Record the path +// + if (AdminPath) free(AdminPath); + AdminPath = strdup(pval); + +// Get the optional access rights +// + if ((val = cFile->GetWord()) && val[0]) + {if (!strcmp("group", val)) mode |= S_IRWXG; + else {Say.Emsg("Config", "invalid admin path modifier -", val); + return 1; + } + } + AdminMode = mode; + return 0; +} /******************************************************************************/ /* x c a c h e */ @@ -769,7 +1048,7 @@ void XrdFrmConfig::Usage(int rc) Output: 0 upon success or !0 upon failure. */ -int XrdFrmConfig::xcache() +int XrdFrmConfig::xcache(int isPrg) { char *val, *pfxdir, *sfxdir; char grp[XrdOssSpace::minSNbsz], fn[MAXPATHLEN], dn[MAXNAMLEN]; @@ -842,61 +1121,38 @@ void XrdFrmConfig::xcacheBuild(char *grp, char *fn, int isxa) while(nP && strcmp(nP->Name, grp)) nP = nP->Next; - if (!nP) VPList = nP = new VPInfo(grp, VPList); + if (!nP) VPList = nP = new VPInfo(grp, 0, VPList); tP = nP->Dir; while(tP && strcmp(tP->text, fn)) tP = tP->next; if (!tP) nP->Dir = new XrdOucTList(fn, isxa, nP->Dir); } - + /******************************************************************************/ -/* Private: x a p a t h */ +/* Private: x d p o l */ /******************************************************************************/ + -/* Function: xapath - - Purpose: To parse the directive: adminpath <path> [group] +/* Function: xdpol - <path> the path of the FIFO to use for admin requests. + Purpose: To parse the directive: dirpolicy <sec> - group allows group access to the admin path + <sec> number of seconds to hold an empty directory or the + word 'forever'. Output: 0 upon success or !0 upon failure. */ +int XrdFrmConfig::xdpol() +{ int htm; + char *val; -int XrdFrmConfig::xapath() -{ - char *pval, *val; - mode_t mode = S_IRWXU; - -// Get the path -// - pval = cFile->GetWord(); - if (!pval || !pval[0]) - {Say.Emsg("Config", "adminpath not specified"); return 1;} - -// Make sure it's an absolute path -// - if (*pval != '/') - {Say.Emsg("Config", "adminpath not absolute"); return 1;} - -// Record the path -// - if (AdminPath) free(AdminPath); - AdminPath = strdup(pval); - -// Get the optional access rights -// - if ((val = cFile->GetWord()) && val[0]) - {if (!strcmp("group", val)) mode |= S_IRWXG; - else {Say.Emsg("Config", "invalid admin path modifier -", val); - return 1; - } - } - AdminMode = mode; - return 0; + if (!(val = cFile->GetWord())) + {Say.Emsg("Config", "dirpolicy hold time not specified"); return 1;} + if (XrdOuca2x::a2tm(Say,"dirpolicy hold time", val, &htm, 0)) return 1; + dirHold = htm; + return 0; } - + /******************************************************************************/ /* Private: x m a x x */ /******************************************************************************/ @@ -915,7 +1171,7 @@ int XrdFrmConfig::xmaxx() if (!(val = cFile->GetWord())) {Say.Emsg("Config", "xfrmax value not specified"); return 1;} - if (XrdOuca2x::a2tm(Say, "xfrmax", val, &xmax, 1)) return 1; + if (XrdOuca2x::a2i(Say, "xfrmax", val, &xmax, 1)) return 1; xfrMax = xmax; return 0; } @@ -1071,6 +1327,226 @@ int XrdFrmConfig::xnml() return 0; } +/******************************************************************************/ +/* Private: x p o l */ +/******************************************************************************/ + +/* Function: xpol + + Purpose: To parse the directive: policy {*|sname} {nopurge|min [max]] [opts] + + * The default policy for all spaces. + + sname The policy to apply for this space. Defaults apply for + unspecified values. To make sure the specified default + is used, the '*' entry must appear first. + + nopurge Turns off purging. + + min% Minimum free space; purge starts when less available. + Can be specified as a percentage (i.e., n%) or an + absolute size value (with k, m, g, t suffix). + Default: 5% + + max% Maximum free space; purge stops when more available. + Must be specified in the same units as min and must be + greater than min. + Default: min% + 2 or min * 1.2 + + opts: hold <tm> Time to hold a file before it can be purged. The <tm> + can be a suffixed number or 'forever'. + Default: 20h (20*3600)s + + polprog Invoke the policy program to do final determination. + + + Output: 0 upon success or !0 upon failure. +*/ +int XrdFrmConfig::xpol() +{ + Policy *pP = &dfltPolicy; + char *val, sname[XrdOssSpace::minSNbsz]; + long long minP = dfltPolicy.minFree, maxP = dfltPolicy.maxFree; + int Hold = dfltPolicy.Hold, Ext = 0; + struct purgeopts {const char *opname; int isTime; int *oploc;} pgopts[] = + { + {"polprog", -1, &Ext}, + {"hold", 1, &Hold} + }; + int i, rc, numopts = sizeof(pgopts)/sizeof(struct purgeopts); + +// Get the space name +// + if (!(val = cFile->GetWord())) + {Say.Emsg("Config", "space name not specified"); return 1;} + if (strlen(val) >= sizeof(sname)) + {Say.Emsg("Config", "space name '", val, "' too long"); return 1;} + +// If we have an equal sign then an external policy is being defined +// + if (!strcmp("=", val)) return xpolprog(); + strcpy(sname, val); + +// The next item may be minimum percentage followed by a maximum percentage +// Otherwise, it may be 'nopurge'. +// + if ( (val = cFile->GetWord()) && isdigit(*val)) + {if ( XrdOuca2x::a2sp(Say, "min free", val, &minP, 1)) return 1; + if ((val = cFile->GetWord()) && isdigit(*val)) + {if (XrdOuca2x::a2sp(Say, "max free", val, &maxP, 1)) return 1; + if ((minP < 0 && maxP >= 0) || (minP >= 0 && maxP < 0)) + {Say.Emsg("Config", "purge min/max may not differ in type."); + return 1; + } + if (XRDABS(minP) >= XRDABS(maxP)) + {Say.Emsg("Config", "purge min must be < max value."); return 1;} + val = cFile->GetWord(); + } else { + if (minP < 0) maxP = (minP < -99 ? -100 : minP - 1); + else maxP = (minP * 120LL)/100LL; + } + } else if (val && !strcmp(val, "nopurge")) + {minP = maxP = 0; + if ((val = cFile->GetWord())) + {Say.Say("Config warning: ignoring extraneous policy option '",val,"'."); + val = 0; + } + } + +// Pick up the remining options +// + while(val) + {for (i = 0; i < numopts; i++) if (!strcmp(val,pgopts[i].opname)) break; + if (i >= numopts) + {Say.Say("Config warning: ignoring invalid policy option '",val,"'."); + val = cFile->GetWord(); + continue; + } + if (pgopts[i].isTime < 0) *(pgopts[i].oploc) = 1; + else {if (!(val = cFile->GetWord())) + {Say.Emsg("Config", "policy", pgopts[i].opname, + "argument not specified."); + return 1; + } + rc = (pgopts[i].isTime + ? getTime( "purge value",val,pgopts[i].oploc,0) + : XrdOuca2x::a2i (Say,"purge value",val,pgopts[i].oploc,0)); + if (rc) return 1; + } + val = cFile->GetWord(); + } + +// If an external policy applies, it must be present +// + if (Ext && !pProg) + {Say.Emsg("Config", "External policy has not been pre-defined."); + return 1; + } + +// Add this policy definition +// + while(pP && strcmp(pP->Sname, sname)) pP = pP->Next; + if (pP) {pP->minFree=minP; pP->maxFree=maxP; pP->Hold=Hold; pP->Ext=Ext;} + else {pP = new Policy(sname, minP, maxP, Hold, Ext); + pP->Next = dfltPolicy.Next; dfltPolicy.Next = pP; + } + return 0; +} + +/******************************************************************************/ +/* Private: x p o l p r o g */ +/******************************************************************************/ + +/* Function: xpolprog + + Purpose: To parse the directive: policy = [vars] |<prog> [args] + + Where: + = Defines an external policy via a program, as follows: + + vars The information to ship to the program via stdin: + atime - access time + ctime - create time + fname - the filename itself + fsize - file size + fspace - free space + mtime - modify time + pfn - physical file name + sname - space name + tspace - total space + + |<prog> The name of the policy program to receive the info. + + args Optional program arguments (substituted), up to 8. + + Output: 0 upon success or !0 upon failure. +*/ +int XrdFrmConfig::xpolprog() +{ + char *val, pBuff[4096], *pbP = pBuff; + struct polopts {const char *opname; int opval;} plopts[] = + { + {"atime", PP_atime }, + {"ctime", PP_ctime }, + {"fname", PP_fname }, + {"fsize", PP_fsize }, + {"fspace", PP_fspace}, + {"mtime", PP_mtime }, + {"pfn", PP_pfn }, + {"sname", PP_sname }, + {"tspace", PP_tspace}, + {"usage", PP_usage} + }; + int i, n, numopts = sizeof(plopts)/sizeof(struct polopts); + +// Get the first token +// + if (!(val = cFile->GetWord())) + {Say.Emsg("Config", "policy program not specified"); return 1;} + pVecNum = 0; + +// Pick up the remining options +// + while(val && *val != '|') + {for (i = 0; i < numopts; i++) if (!strcmp(val,plopts[i].opname)) break; + if (i >= numopts) + {Say.Say("Config warning: ignoring invalid policy option '",val,"'."); + val = cFile->GetWord(); + continue; + } + if (pVecNum >= pVecMax) + {Say.Emsg("Config", "To many policy program variables specified."); + return 1; + } + pVec[pVecNum++] = static_cast<char>(plopts[i].opval); + val = cFile->GetWord(); + } + +// Pick up the program +// + if (val) val++; + if (val && !(*val)) val = cFile->GetWord(); + if (!val) + {Say.Emsg("Config", "policy program not specified."); return 1;} + i = strlen(val); + if (i >= (int)sizeof(pBuff)-8) + {Say.Emsg("Config", "policy program name is too long."); return 1;} + strcpy(pBuff, val); pbP = pBuff+i; *(pbP+1) = '\0'; + +// Now get any optional arguments +// + n = sizeof(pBuff) - i - 1; + if (!cFile->GetRest(pbP+1, n)) + {Say.Emsg("Config", "policy program args are too long."); return 1;} + if (*(pbP+1)) *pbP = ' '; + +// Record the program +// + if (pProg) free(pProg); + pProg = strdup(pBuff); + return 0; +} + /******************************************************************************/ /* Private: x w t m */ /******************************************************************************/ @@ -1084,12 +1560,12 @@ int XrdFrmConfig::xnml() Output: 0 upon success or !0 upon failure. */ int XrdFrmConfig::xwtm() -{ int wscan = 0; +{ int wscan = 0, wtime = (Test ? 1 : 30); char *val; if (!(val = cFile->GetWord())) {Say.Emsg("Config", "wait time not specified"); return 1;} - if (XrdOuca2x::a2tm(Say, "wait time", val, &wscan, 30)) return 1; + if (XrdOuca2x::a2tm(Say, "wait time", val, &wscan, wtime)) return 1; WaitTime = wscan; return 0; } diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmConfig.hh b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmConfig.hh index 28c059c3ad8df7c50659937618f8517106d25fd5..681a6c48b8509277f47ac0ea392ef3dde84d9196 100644 --- a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmConfig.hh +++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmConfig.hh @@ -15,6 +15,8 @@ #include <string.h> #include <unistd.h> +#include "XrdOss/XrdOssSpace.hh" + class XrdCmsNotify; class XrdOss; class XrdOucMsubs; @@ -50,23 +52,56 @@ XrdOss *ossFS; XrdCmsNotify *cmsPath; uid_t myUid; gid_t myGid; +long long cmdFree; +int cmdHold; int AdminMode; int isAgent; int xfrMax; int WaitTime; int monStage; int sSpec; +int isOTO; +int Fix; +int Test; +int Verbose; +char **vectArg; int nextArg; -int Solitary; +int numcArg; struct VPInfo {VPInfo *Next; char *Name; XrdOucTList *Dir; - VPInfo(char *n, struct VPInfo *p=0) - : Next(p), Name(strdup(n)), Dir(0) {} + int Val; + VPInfo(char *n, int m=0, struct VPInfo *p=0) + : Next(p), Name(strdup(n)), Dir(0), Val(m) {} ~VPInfo() {} // Deletes are not important } *VPList; +VPInfo *pathList; // Migr/Purg list of paths +XrdOucTList *spacList; // Migr/Purg list of spaces + +struct Policy + {long long minFree; + long long maxFree; + int Hold; + int Ext; + Policy *Next; + char Sname[XrdOssSpace::minSNbsz]; + Policy(const char *snv, long long minV, long long maxV, + int hV, int xV) : minFree(minV), maxFree(maxV), + Hold(hV), Ext(xV), Next(0) {strcpy(Sname, snv);} + ~Policy() {} + }; +Policy dfltPolicy; + +int dirHold; +int pVecNum; // Number of policy variables +static const int pVecMax=8; +char pVec[pVecMax]; +char *pProg; + +enum PPVar {PP_atime=0, PP_ctime, PP_fname, PP_fsize, PP_fspace, + PP_mtime, PP_pfn, PP_sname, PP_tspace, PP_usage}; int Configure(int argc, char **argv, int (*ppf)()); @@ -85,18 +120,26 @@ private: XrdOucMsubs *ConfigCmd(const char *cname, char *cdata); int ConfigMum(XrdFrmConfigSE &theSE); int ConfigN2N(); +int ConfigMP(const char *); int ConfigMss(); +int ConfigOTO(char *Parms); int ConfigPaths(); int ConfigProc(); int ConfigXeq(char *var, int mbok); +int getTime(const char *, const char *, int *, int mnv=-1, int mxv=-1); int Grab(const char *var, char **Dest, int nosubs); +XrdOucTList *InsertPL(XrdOucTList *pP, const char *Path, int Plen, int isRW); +void InsertXD(const char *Path); void Usage(int rc); int xapath(); -int xcache(); +int xcache(int isPrg=0); void xcacheBuild(char *grp, char *fn, int isxa); +int xdpol(); int xmaxx(); int xnml(); int xmon(); +int xpol(); +int xpolprog(); int xwtm(); char *ConfigFN; diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmFiles.cc b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmFiles.cc index b79d120dff96802c60cf8e7668f6c32f7d6ed997..a987e85cea676456d6e1ced6b0745a959663cfbc 100644 --- a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmFiles.cc +++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmFiles.cc @@ -10,28 +10,265 @@ // $Id$ +#include <errno.h> #include <string.h> +#include <strings.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/param.h> +#include <sys/stat.h> #include <sys/types.h> #include "XrdFrm/XrdFrmConfig.hh" #include "XrdFrm/XrdFrmFiles.hh" #include "XrdFrm/XrdFrmTrace.hh" +#include "XrdOuc/XrdOucTList.hh" +#include "XrdSys/XrdSysPlatform.hh" const char *XrdFrmFilesCVSID = "$Id$"; using namespace XrdFrm; +/******************************************************************************/ +/* C l a s s X r d F r m F i l e s e t */ +/******************************************************************************/ +/******************************************************************************/ +/* D e s t r u c t o r */ +/******************************************************************************/ + +XrdFrmFileset::~XrdFrmFileset() +{ + int i; + +// Unlock any locked files +// + UnLock(); + +// Delete all the table entries +// + for (i = 0; i < XrdOssPath::sfxNum; i++) if(File[i]) delete File[i]; + +// If there is a shared directory buffer, decrease reference count, delete if 0 +// + if (dInfo) + {dInfo->ival[dRef]--; + if (!dInfo) delete dInfo; + } +} + +/******************************************************************************/ +/* d i r P a t h */ +/******************************************************************************/ + +int XrdFrmFileset::dirPath(char *dBuff, int dBlen) +{ + char *dP = 0; + int dN = 0, i; + +// If we have a shared directory pointer, use that as directory information +// Otherwise, get it from one of the files in the fileset. +// + if (dInfo) {dP = dInfo->text; dN = dInfo->ival[dLen];} + else {for (i = 0; i < XrdOssPath::sfxNum; i++) + if (File[i]) + {dP = File[i]->Path; + dN = File[i]->File - File[i]->Path; + break; + } + } + +// Copy out the directory path +// + if (dBlen > dN && dP) strncpy(dBuff, dP, dN); + else dN = 0; + *(dBuff+dN) = '\0'; + return dN; +} + +/******************************************************************************/ +/* R e f r e s h */ +/******************************************************************************/ + +int XrdFrmFileset::Refresh(int isMig, int doLock) +{ + XrdOucNSWalk::NSEnt *bP = baseFile(), *lP = lockFile(); + char pBuff[MAXPATHLEN+1], *fnP, *pnP = pBuff; + int n; + +// Get the directory path for this entry +// + if (!(n = dirPath(pBuff, sizeof(pBuff)-1))) return 0; + fnP = pBuff+n; + +// If we need to lock the entry, do so. We also check if file is in use +// + if (doLock && bP) + {strcpy(fnP, baseFile()->File); + if (chkLock(pBuff)) return 0; + if (lP && dlkFD < 0) + {if ((dlkFD = getLock(pBuff)) < 0) return 0; + strcpy(fnP, lockFile()->File); + if ((flkFD = getLock(pBuff,0,1)) < 0) + {close(dlkFD); dlkFD = -1; return 0;} + } + } + +// Do a new stat call on each relevant file (pin file excluded for isMig) +// + if (bP) + {if (bP->Link) pnP = bP->Link; + else strcpy(fnP, bP->File); + if (stat(pnP, &(bP->Stat))) + {Say.Emsg("Refresh", errno, "stat", pnP); UnLock(); return 0;} + } + + if (lP) + {strcpy(fnP, lP->File); + if (stat(pBuff, &(lP->Stat))) + {Say.Emsg("Refresh", errno, "stat", pBuff); UnLock(); return 0;} + } + + if (!isMig && (bP = pinFile())) + {strcpy(fnP, bP->File); + if (stat(pBuff, &(bP->Stat)) && errno == ENOENT) + {delete bP; File[XrdOssPath::isPin] = 0;} + } + +// All done +// + return 1; +} + +/******************************************************************************/ +/* U n L o c k */ +/******************************************************************************/ + +void XrdFrmFileset::UnLock() +{ + if (flkFD >= 0) {close(flkFD); flkFD = -1;} + if (dlkFD >= 0) {close(dlkFD); dlkFD = -1;} +} + +/******************************************************************************/ +/* P r i v a t e M e t h o d s */ +/******************************************************************************/ +/******************************************************************************/ +/* c h k L o c k */ +/******************************************************************************/ + +// Returns 0 if no lock exists o/w returns 1 or -1; + +int XrdFrmFileset::chkLock(const char *Path) +{ + FLOCK_t lock_args; + int rc, lokFD; + +// Open the file appropriately +// + if ((lokFD = open(Path, O_RDONLY)) < 0) + {Say.Emsg("chkLock", errno, "open", Path); return -1;} + +// Initialize the lock arguments +// + bzero(&lock_args, sizeof(lock_args)); + lock_args.l_type = F_WRLCK; + +// Now check if the lock can be obtained +// + do {rc = fcntl(lokFD, F_GETLK, &lock_args);} while(rc < 0 && errno == EINTR); + +// Determine the result +// + if (rc) Say.Emsg("chkLock", errno, "lock", Path); + else rc = (lock_args.l_type == F_UNLCK ? 0 : 1); + +// All done +// + close(lokFD); + return rc; +} + +/******************************************************************************/ +/* g e t L o c k */ +/******************************************************************************/ + +int XrdFrmFileset::getLock(char *Path, int Shared, int noWait) +{ + static const int AMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + FLOCK_t lock_args; + int oFlags = (Shared ? O_RDONLY : O_RDWR|O_CREAT); + int rc, lokFD, Act; + +// Open the file appropriately +// + if ((lokFD = open(Path, oFlags, AMode)) < 0) + {Say.Emsg("getLock", errno, "open", Path); return -1;} + +// Initialize the lock arguments +// + bzero(&lock_args, sizeof(lock_args)); + lock_args.l_type = (Shared ? F_RDLCK : F_WRLCK); + Act = (noWait ? F_SETLK : F_SETLKW); + +// Now obtain the lock +// + do {rc = fcntl(lokFD, Act, &lock_args);} while(rc < 0 && errno == EINTR); + +// Determine the result +// + if (rc) + {if (errno != EAGAIN) Say.Emsg("getLock", errno, "lock", Path); + close(lokFD); return -1; + } + +// All done +// + return lokFD; +} + +/******************************************************************************/ +/* M k f n */ +/******************************************************************************/ + +const char *XrdFrmFileset::Mkfn(XrdOucNSWalk::NSEnt *fP) +{ + +// If we have no file for this, return the null string +// + if (!fP) return ""; + +// If we have no shared directory pointer, return the full path +// + if (!dInfo) return fP->Path; + +// Construct the name in a non-renterant way (this is documented) +// + strcpy(dInfo->text+dInfo->ival[dLen], fP->File); + return dInfo->text; +} + +/******************************************************************************/ +/* C l a s s X r d F r m F i l e s */ +/******************************************************************************/ /******************************************************************************/ /* C o n s t r u c t o r */ /******************************************************************************/ -XrdFrmFiles::XrdFrmFiles(const char *dname, int opts) +XrdFrmFiles::XrdFrmFiles(const char *dname, int opts, + XrdOucTList *XList, XrdOucNSWalk::CallBack *cbP) : nsObj(&Say, dname, Config.lockFN, XrdOucNSWalk::retFile | XrdOucNSWalk::retLink |XrdOucNSWalk::retStat | XrdOucNSWalk::skpErrs - | (opts & Recursive ? XrdOucNSWalk::Recurse : 0)), - fsList(0) -{} + | (opts & CompressD ? XrdOucNSWalk::noPath : 0) + | (opts & Recursive ? XrdOucNSWalk::Recurse : 0), XList), + fsList(0), manMem(opts & NoAutoDel ? Hash_keep : Hash_default), + shareD(opts & CompressD) +{ + +// Set Call Back method +// + nsObj.setCallBack(cbP); +} /******************************************************************************/ /* G e t */ @@ -41,19 +278,21 @@ XrdFrmFileset *XrdFrmFiles::Get(int &rc, int noBase) { XrdOucNSWalk::NSEnt *nP; XrdFrmFileset *fsetP; + const char *dPath; // Check if we have something to return // do{while ((fsetP = fsList)) {fsList = fsetP->Next; fsetP->Next = 0; if (fsetP->File[XrdOssPath::isBase] || noBase) {rc = 0; return fsetP;} + else if (manMem) delete fsetP; } -// Start with next directory (we return when no directories left) +// Start with next directory (we return when no directories left). // - do {if (!(nP = nsObj.Index(rc))) return 0; + do {if (!(nP = nsObj.Index(rc, &dPath))) return 0; fsTab.Purge(); fsList = 0; - } while(!Process(nP)); + } while(!Process(nP, dPath)); } while(1); @@ -69,13 +308,28 @@ do{while ((fsetP = fsList)) /* P r o c e s s */ /******************************************************************************/ -int XrdFrmFiles::Process(XrdOucNSWalk::NSEnt *nP) +int XrdFrmFiles::Process(XrdOucNSWalk::NSEnt *nP, const char *dPath) { XrdOucNSWalk::NSEnt *fP; XrdFrmFileset *sP; + XrdOucTList *dP = 0; char *dotP; int fType; +// If compressed directories wanted, then setup a shared directory buffer +// Warning! We use a hard-coded value for maximum filename length instead of +// constantly calling pathconf(). +// + if (shareD) + {int n = strlen(dPath); + char *dBuff = (char *)malloc(n+264); + strcpy(dBuff, dPath); + dP = new XrdOucTList; + dP->text = dBuff; + dP->ival[XrdFrmFileset::dLen] = n; + dP->ival[XrdFrmFileset::dRef] = 0; + } + // Process the file list // while((fP = nP)) @@ -83,7 +337,9 @@ int XrdFrmFiles::Process(XrdOucNSWalk::NSEnt *nP) if (!strcmp(fP->File, Config.lockFN)) {delete fP; continue;} if ((dotP = rindex(fP->File, '.'))) *dotP = '\0'; if (!(sP = fsTab.Find(fP->File))) - {sP = fsList = new XrdFrmFileset(fsList); fsTab.Add(fP->File, sP);} + {sP = fsList = new XrdFrmFileset(fsList, dP); + fsTab.Add(fP->File, sP, 0, manMem); + } if (dotP) *dotP = '.'; fType = (int)XrdOssPath::pathType(fP->File); sP->File[fType] = fP; @@ -91,5 +347,7 @@ int XrdFrmFiles::Process(XrdOucNSWalk::NSEnt *nP) // Indicate whether we have anything here // - return fsList != 0; + if (fsList) return 1; + if (dP) delete dP; + return 0; } diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmFiles.hh b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmFiles.hh index d13d24683f84645d40d4dbd59eb923a944e906e5..95ae68f35c7889ca1a6ef1c1f7d2f30ad61f6847 100644 --- a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmFiles.hh +++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmFiles.hh @@ -19,6 +19,8 @@ #include "XrdOuc/XrdOucHash.hh" #include "XrdOuc/XrdOucNSWalk.hh" +class XrdOucTList; + /******************************************************************************/ /* C l a s s X r d F r m F i l e s e t */ /******************************************************************************/ @@ -28,20 +30,52 @@ class XrdFrmFileset public: friend class XrdFrmFiles; -// These are the basic set of files related to the base file. Two other file -// suffixes are ignore for fileset purposes (".anew" and ".stage"). To take -// ownership of the entry, simply set the pointer to zero after saving it. +// These are inline function to return most common file information // -XrdOucNSWalk::NSEnt *File[XrdOssPath::sfxNum]; +inline XrdOucNSWalk::NSEnt *baseFile() {return File[XrdOssPath::isBase];} +const char *basePath() {return Mkfn(baseFile());} +inline XrdOucNSWalk::NSEnt *failFile() {return File[XrdOssPath::isFail];} +const char *failPath() {return Mkfn(failFile());} +inline XrdOucNSWalk::NSEnt *lockFile() {return File[XrdOssPath::isLock];} +const char *lockPath() {return Mkfn(lockFile());} +inline XrdOucNSWalk::NSEnt * pfnFile() {return File[XrdOssPath::isPfn ];} +const char * pfnPath() {return Mkfn(pfnFile());} +inline XrdOucNSWalk::NSEnt * pinFile() {return File[XrdOssPath::isPin ];} +const char * pinPath() {return Mkfn(pinFile());} + +int dirPath(char *dBuff, int dBlen); + +int Refresh(int isMig=0, int doLock=1); - XrdFrmFileset(XrdFrmFileset *sP=0) : Next(sP) +void UnLock(); + + XrdFrmFileset(XrdFrmFileset *sP=0, XrdOucTList *diP=0) + : Next(sP), dInfo(diP), + dlkFD(-1), flkFD(-1) {memset(File, 0, sizeof(File));} - ~XrdFrmFileset() {int i; - for (i = 0; i < XrdOssPath::sfxNum; i++) - if(File[i]) delete File[i]; - } -private: + ~XrdFrmFileset(); + +// The following are public to ease management of this object +// XrdFrmFileset *Next; +int Age; + +private: +int chkLock(const char *Path); +int getLock(char *Path, int Shared=0, int noWait=0); +const char *Mkfn(XrdOucNSWalk::NSEnt *fP); + +// These are the basic set of files related to the base file. Two other file +// suffixes are ignore for fileset purposes (".anew" and ".stage"). +// +XrdOucNSWalk::NSEnt *File[XrdOssPath::sfxNum]; + +XrdOucTList *dInfo; // Shared directory information +static const int dLen = 0; // Index to directory path length in dInfo +static const int dRef = 1; // Index to the reference counter in dInfo + +int dlkFD; // Directory lock FD +int flkFD; // Lock file lock FD }; /******************************************************************************/ @@ -54,18 +88,23 @@ public: XrdFrmFileset *Get(int &rc, int noBase=0); -static const int Recursive = 0x0001; +static const int Recursive = 0x0001; // List filesets recursively +static const int CompressD = 0x0002; // Use shared directory object (not MT) +static const int NoAutoDel = 0x0004; // Do not automatically delete objects - XrdFrmFiles(const char *dname, int opts=Recursive); + XrdFrmFiles(const char *dname, int opts=Recursive, + XrdOucTList *XList=0, XrdOucNSWalk::CallBack *cbP=0); ~XrdFrmFiles() {} private: -int Process(XrdOucNSWalk::NSEnt *nP); +int Process(XrdOucNSWalk::NSEnt *nP, const char *dPath); XrdOucHash<XrdFrmFileset>fsTab; XrdOucNSWalk nsObj; XrdFrmFileset *fsList; +XrdOucHash_Options manMem; +int shareD; }; #endif diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmPstgXfr.cc b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmPstgXfr.cc index d1a4cd8864db2218b0cf98415fcd3d493e5e1a60..4385cddd6fecc9f1c9d2741aebe6f5f9937d7664 100644 --- a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmPstgXfr.cc +++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmPstgXfr.cc @@ -316,8 +316,7 @@ const char *XrdFrmPstgXfr::Stage(XrdFrmPstgXrq *xP, int &retcode) {rc = 0; while(!stat(Config.StopFile, &buf)) {if (!rc--) - {DEBUG("Stop file " <<Config.StopFile - <<" exists; staging suspended."); + {Say.Emsg("stage",Config.StopFile,"exists; staging suspended."); rc = 12; } XrdSysTimer::Snooze(5); diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmPurgMain.cc b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmPurgMain.cc new file mode 100644 index 0000000000000000000000000000000000000000..85f91a88b23e0fbede4a48f04f38ce23f8d98a5c --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmPurgMain.cc @@ -0,0 +1,235 @@ +/******************************************************************************/ +/* */ +/* X r d F r m P u r g M a i n . c c */ +/* */ +/* (c) 2009 by the Board of Trustees of the Leland Stanford, Jr., University */ +/* All Rights Reserved */ +/* Produced by Andrew Hanushevsky for Stanford University under contract */ +/* DE-AC02-76-SFO0515 with the Department of Energy */ +/******************************************************************************/ + +// $Id$ + +const char *XrdFrmPurgMainCVSID = "$Id$"; + +/* This is the "main" part of the frm_purge command. Syntax is: +*/ +static const char *XrdFrmOpts = ":c:dfhk:l:n:O:Tv"; +static const char *XrdFrmUsage = + + " [-c <cfgfile>] [-d] [-f] [-k {num | sz{k|m|g}] [-l <lfile>] [-n name]" + " [-O free[,hold]] [-T] [-v] [<spaces>] [<paths>]\n"; +/* +Where: + + -c The configuration file. The default is '/opt/xrootd/etc/xrootd.cf' + + -d Turns on debugging mode. + + -f Fix orphaned files (i.e., lock and pin) by removing them. + + -k Keeps num log files or no more that sz log files. + + -l Specifies location of the log file. This may also come from the + XrdOucLOGFILE environmental variable. + By default, error messages go to standard error. + + -n The instance name. + + -O Run this one time only as a command. The parms are: + {free% | sz{k|m|g}[,hold] + + -T Runs in test mode (no actual purge will occur). + + -v Verbose mode, typically prints each file purged and other details. + + o-t-a The one-time-args run this as a command only once. The args direct + the purging process. These may only be specified when -O specified. + + Syntax is: [space] path | space [path] +*/ + +/******************************************************************************/ +/* i n c l u d e f i l e s */ +/******************************************************************************/ + +#include <unistd.h> +#include <ctype.h> +#include <errno.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <stdio.h> +#include <sys/param.h> + +#include "XrdFrm/XrdFrmConfig.hh" +#include "XrdFrm/XrdFrmPurge.hh" +#include "XrdFrm/XrdFrmTrace.hh" +#include "XrdNet/XrdNetOpts.hh" +#include "XrdNet/XrdNetSocket.hh" +#include "XrdOuc/XrdOucUtils.hh" +#include "XrdSys/XrdSysError.hh" +#include "XrdSys/XrdSysHeaders.hh" +#include "XrdSys/XrdSysLogger.hh" +#include "XrdSys/XrdSysPthread.hh" +#include "XrdSys/XrdSysTimer.hh" + +using namespace XrdFrm; + +/******************************************************************************/ +/* G l o b a l V a r i a b l e s */ +/******************************************************************************/ + + XrdSysLogger XrdFrm::Logger; + + XrdSysError XrdFrm::Say(&Logger, ""); + + XrdOucTrace XrdFrm::Trace(&Say); + + XrdFrmConfig XrdFrm::Config(XrdFrmConfig::ssPurg, + XrdFrmOpts, XrdFrmUsage); + +// The following is needed to resolve symbols for objects included from xrootd +// + XrdOucTrace *XrdXrootdTrace; + XrdSysError XrdLog(&Logger, ""); + XrdOucTrace XrdTrace(&Say); + +/******************************************************************************/ +/* T h r e a d I n t e r f a c e s */ +/******************************************************************************/ + +void *mainServer(void *parg) +{ +// int udpFD = *static_cast<int *>(parg); +// XrdFrmPurge::Server(udpFD); + return (void *)0; +} + +/******************************************************************************/ +/* m a i n */ +/******************************************************************************/ + +int main(int argc, char *argv[]) +{ + extern int mainConfig(); + sigset_t myset; + +// Turn off sigpipe and host a variety of others before we start any threads +// + signal(SIGPIPE, SIG_IGN); // Solaris optimization + sigemptyset(&myset); + sigaddset(&myset, SIGPIPE); + sigaddset(&myset, SIGCHLD); + pthread_sigmask(SIG_BLOCK, &myset, NULL); + +// Set the default stack size here +// + if (sizeof(long) > 4) XrdSysThread::setStackSize((size_t)1048576); + else XrdSysThread::setStackSize((size_t)786432); + +// Perform configuration +// + if (!Config.Configure(argc, argv, &mainConfig)) exit(4); + +// Fill out the dummy symbol to avoid crashes +// + XrdXrootdTrace = new XrdOucTrace(&Say); + +// Display configuration (defered because mum might have been in effect) +// + if (!Config.isOTO || Config.Verbose) XrdFrmPurge::Display(); + +// Now simply poke the server every so often +// + if (Config.isOTO) XrdFrmPurge::Purge(); + else do {if (Config.StopFile) + {int n = 0; + struct stat buf; + while(!stat(Config.StopFile, &buf)) + {if (!n--) + {Say.Emsg("PurgMain", Config.StopFile, + "exists; purging suspended."); n = 12;} + XrdSysTimer::Snooze(5); + } + } + XrdFrmPurge::Purge(); + XrdSysTimer::Snooze(Config.WaitTime); + } while(1); + +// All done +// + exit(0); +} + +/******************************************************************************/ +/* m a i n C o n f i g */ +/******************************************************************************/ + +int mainConfig() +{ + XrdFrmConfig::Policy *pP = Config.dfltPolicy.Next; + XrdFrmConfig::VPInfo *vP = Config.VPList; + XrdNetSocket *udpSock; + pthread_t tid; + int retc, udpFD; + +// If test is in effect, remove the fix flag +// + if (Config.Test) Config.Fix = 0; + +// Go through the policy list and add each policy +// + while((pP = Config.dfltPolicy.Next)) + {if (!XrdFrmPurge::Policy(pP->Sname)) + XrdFrmPurge::Policy(pP->Sname, pP->minFree, pP->maxFree, + pP->Hold, pP->Ext); + Config.dfltPolicy.Next = pP->Next; + delete pP; + } + +// Make sure we have a public policy +// + if (!XrdFrmPurge::Policy("public")) + XrdFrmPurge::Policy("public", Config.dfltPolicy.minFree, + Config.dfltPolicy.maxFree, + Config.dfltPolicy.Hold, + Config.dfltPolicy.Ext); + +// Now add any missing policies (we need one for every space) +// + while(vP) + {if (!XrdFrmPurge::Policy(vP->Name)) + XrdFrmPurge::Policy(vP->Name, Config.dfltPolicy.minFree, + Config.dfltPolicy.maxFree, + Config.dfltPolicy.Hold, + Config.dfltPolicy.Ext); + vP = vP->Next; + } + +// Enable the appropriate spaces and over-ride config value +// + if (!XrdFrmPurge::Init(Config.spacList, Config.cmdFree, Config.cmdHold)) + return 1; + +// We are done if this is a one-time-only call +// + if (Config.isOTO) return 0; + +// Get a UDP socket for the server +// + if (!(udpSock = XrdNetSocket::Create(&Say, Config.AdminPath, + "purg.udp", Config.AdminMode, XRDNET_UDPSOCKET))) return 1; + else {udpFD = udpSock->Detach(); delete udpSock;} + +// Start the Server thread +// + if ((retc = XrdSysThread::Run(&tid, mainServer, (void *)&udpFD, + XRDSYSTHREAD_BIND, "Server"))) + {Say.Emsg("main", retc, "create server thread"); return 1;} + +// All done +// + return 0; +} diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmPurge.cc b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmPurge.cc new file mode 100644 index 0000000000000000000000000000000000000000..3d4f13b090f937625987ddf2570484e369118856 --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmPurge.cc @@ -0,0 +1,927 @@ +/******************************************************************************/ +/* */ +/* X r d F r m P u r g e . c c */ +/* */ +/* (c) 2009 by the Board of Trustees of the Leland Stanford, Jr., University */ +/* All Rights Reserved */ +/* Produced by Andrew Hanushevsky for Stanford University under contract */ +/* DE-AC02-76-SFO0515 with the Department of Energy */ +/******************************************************************************/ + +// $Id$ + +const char *XrdFrmPurgeCVSID = "$Id$"; + +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <utime.h> +#include <sys/param.h> +#include <sys/types.h> + +#include "XrdCms/XrdCmsNotify.hh" +#include "XrdOss/XrdOss.hh" +#include "XrdOss/XrdOssPath.hh" +#include "XrdOuc/XrdOucNSWalk.hh" +#include "XrdOuc/XrdOucTList.hh" +#include "XrdOuc/XrdOucProg.hh" +#include "XrdOuc/XrdOucStream.hh" +#include "XrdOuc/XrdOucUtils.hh" +#include "XrdFrm/XrdFrmFiles.hh" +#include "XrdFrm/XrdFrmConfig.hh" +#include "XrdFrm/XrdFrmPurge.hh" +#include "XrdFrm/XrdFrmTrace.hh" +#include "XrdSys/XrdSysPlatform.hh" + +using namespace XrdFrm; + +/******************************************************************************/ +/* L o c a l C l a s s e s */ +/******************************************************************************/ +/******************************************************************************/ +/* C l a s s X r d F r m P u r g e D i r */ +/******************************************************************************/ + +class XrdFrmPurgeDir : XrdOucNSWalk::CallBack +{ +public: + +void isEmpty(struct stat *dStat, const char *dPath, const char *lkFN); + +void Reset(time_t dExp) + {expDirTime = dExp; lowDirTime = 0; numRMD = numEMD = 0;} + +time_t expDirTime; +time_t lowDirTime; +int numRMD; +int numEMD; + + XrdFrmPurgeDir() {} + ~XrdFrmPurgeDir() {} +}; + +/******************************************************************************/ +/* i s E m p t y */ +/******************************************************************************/ + +void XrdFrmPurgeDir::isEmpty(struct stat *dStat, const char *dPath, + const char *lkFN) +{ + static const int ossOpts = XRDOSS_isPFN | XRDOSS_resonly; + static const char *What = (Config.Test ? "Zorch " : "Purged "); + struct stat pStat; + struct utimbuf times; + char Parent[MAXPATHLEN+1], *Slash; + int n, rc; + +// Check if this directory is still considered active +// + numEMD++; + if (dStat->st_mtime > expDirTime) + {if (!lowDirTime || lowDirTime > dStat->st_mtime) + lowDirTime = dStat->st_mtime; + return; + } + +// We can expire the directory. However, we need to get the parent mtime +// because removing this directory should not change the parent's mtime. +// + strcpy(Parent, dPath); + n = strlen(Parent); + if (Parent[n-1] == '/') Parent[--n] = '\0'; + if ((Slash = rindex(Parent, '/'))) + {*Slash = '\0'; + if (stat(Parent, &pStat)) Slash = 0; + } + +// Delete the directory +// + if (Config.Test) rc = 0; + else if (!(rc = Config.ossFS->Remdir(dPath, ossOpts)) && Slash) + {times.actime = pStat.st_atime; + times.modtime = pStat.st_mtime; + utime(Parent, ×); + } + +// Report if successful +// + if (!rc) + {numRMD++; + if (Config.Verbose) + {char sbuff[32]; + struct tm tNow; + localtime_r(&(dStat->st_mtime), &tNow); + sprintf(sbuff, "%02d%02d%02d %02d:%02d:%02d ", + tNow.tm_year-100, tNow.tm_mon+1, tNow.tm_mday, + tNow.tm_hour, tNow.tm_min, tNow.tm_sec); + Say.Say(What, "empty dir ", sbuff, dPath); + } + } +} + +/******************************************************************************/ +/* C l a s s X r d F r m P u r g e */ +/******************************************************************************/ +/******************************************************************************/ +/* S t a t i c M e m b e r s */ +/******************************************************************************/ + +XrdFrmPurge *XrdFrmPurge::First = 0; +XrdFrmPurge *XrdFrmPurge::Default = 0; + +XrdOucProg *XrdFrmPurge::PolProg = 0; +XrdOucStream *XrdFrmPurge::PolStream = 0; + +int XrdFrmPurge::Left2Do = 0; + +time_t XrdFrmPurge::lastReset = 0; +time_t XrdFrmPurge::nextReset = 0; + +XrdOucHash<char> XrdFrmPurge::BadFiles; + +/******************************************************************************/ +/* C o n s t r u c t o r */ +/******************************************************************************/ + +XrdFrmPurge::XrdFrmPurge(const char *snp, XrdFrmPurge *spp) : FSTab(1) +{ + strncpy(SName, snp, sizeof(SName)-1); SName[sizeof(SName)-1] = '\0'; + Next = spp; + freeSpace = 0; + usedSpace =-1; + pmaxSpace = 0; + totlSpace = 0; + contSpace = 0; + minFSpace = 0; + maxFSpace = 0; + Enabled = 0; + Stop = 0; + SNlen = strlen(SName); + Clear(); +} + +/******************************************************************************/ +/* Private: A d d */ +/******************************************************************************/ + +void XrdFrmPurge::Add(XrdFrmFileset *sP) +{ + EPNAME("Add"); + XrdOucNSWalk::NSEnt *baseFile = sP->baseFile(); + XrdFrmPurge *psP = Default; + const char *Why; + time_t xTime; + +// First, get the space name associated with the base file +// + if ((baseFile->Link)) + {char snBuff[XrdOssSpace::minSNbsz]; + XrdOssPath::getCname(0, snBuff, baseFile->Link, baseFile->Lksz); + if (!(psP = Find(snBuff))) psP = Default; + } + +// Ignore the file is the space is not enabled for purging +// + if (!(psP->Enabled)) {delete sP; return;} + psP->numFiles++; + +// Check to see if the file is really eligible for purging +// + if ((Why = psP->Eligible(sP, xTime))) + {DEBUG(sP->basePath() <<"cannot be purged; " <<Why); + delete sP; + return; + } + +// Add the file to the purge table or the defer queue based on access time +// + if (xTime >= psP->Hold) psP->FSTab.Add(sP); + else psP->Defer(sP, xTime); +} + +/******************************************************************************/ +/* Private: A d v a n c e */ +/******************************************************************************/ + +XrdFrmFileset *XrdFrmPurge::Advance() +{ + XrdFrmFileset *fP, *xP; + int n; + +// Find a defer queue entry that meets the hold threshold +// + for (n = DeferQsz-1; n >= 0 && !DeferQ[n]; n--) {} + if (n < 0) return 0; + if (time(0) - DeferT[n] > Hold) return 0; + fP = DeferQ[n]; DeferQ[n] = 0; DeferT[n] = 0; + +// Try to re-add everything in this queue +// + while((xP = fP)) + {fP = fP->Next; + if (xP->Refresh(0,0)) Add(xP); + else delete xP; + } + +// Return based on whether something now exists in the purge table +// + return FSTab.Oldest(); +} + +/******************************************************************************/ +/* Private: C l e a r */ +/******************************************************************************/ + +void XrdFrmPurge::Clear() +{ + XrdFrmFileset *fP; + int n; + +// Zero out the defer queue +// + for (n = 0; n < DeferQsz; n++) + while ((fP = DeferQ[n])) {DeferQ[n] = fP->Next; delete fP;} + memset(DeferT, 0, sizeof(DeferT)); + +// Purge the eligible file table +// + FSTab.Purge(); + +// Clear counters +// + numFiles = 0; prgFiles = 0; purgBytes = 0; +} + +/******************************************************************************/ +/* Private: D e f e r */ +/******************************************************************************/ + +void XrdFrmPurge::Defer(XrdFrmFileset *sP, time_t xTime) +{ + time_t aTime = sP->baseFile()->Stat.st_atime; + int n = xTime/DeferQsz; + +// Slot the entry into the defer queue vector +// + if (!DeferQ[n] || aTime < DeferT[n]) DeferT[n] = aTime; + sP->Next = DeferQ[n]; + DeferQ[n] = sP; +} + +/******************************************************************************/ +/* D i s p l a y */ +/******************************************************************************/ + +void XrdFrmPurge::Display() +{ + XrdFrmConfig::VPInfo *vP = Config.pathList; + XrdFrmPurge *spP = First; + XrdOucTList *tP; + const char *isExt; + char buff[1024], minfsp[32], maxfsp[32]; + +// Type header +// + Say.Say("=====> ", "Purge configuration:"); + +// Display what we will scan +// + while(vP) + {Say.Say("=====> ", "Scanning ", (vP->Val?"r/w: ":"r/o: "), vP->Name); + tP = vP->Dir; + while(tP) {Say.Say("=====> ", "Excluded ", tP->text); tP = tP->next;} + vP = vP->Next; + } + +// Display directory hold value +// + if (Config.dirHold < 0) strcpy(buff, "forever"); + else sprintf(buff, "%d", Config.dirHold); + Say.Say("=====> ", "Directory hold: ", buff); + +// Run through all of the policies, displaying each one +// + spP = First; + while(spP) + {if (spP->Enabled) + {XrdOucUtils::fmtBytes(spP->minFSpace, minfsp, sizeof(minfsp)); + XrdOucUtils::fmtBytes(spP->maxFSpace, maxfsp, sizeof(maxfsp)); + isExt = spP->Ext ? " polprog" : ""; + sprintf(buff, "policy %s min %s max %s free; hold: %d%s", + spP->SName, minfsp, maxfsp, spP->Hold, isExt); + } else sprintf(buff, "policy %s nopurge", spP->SName); + Say.Say("=====> ", buff); + spP = spP->Next; + } +} + +/******************************************************************************/ +/* Private: E l i g i b l e */ +/******************************************************************************/ + +const char *XrdFrmPurge::Eligible(XrdFrmFileset *sP, time_t &xTime, int hTime) +{ + XrdOucNSWalk::NSEnt *baseFile = sP->baseFile(); + XrdOucNSWalk::NSEnt *lockFile = sP->lockFile(); + XrdOucNSWalk::NSEnt * pinFile; + time_t aTime, mTime, nowTime = time(0); + char pBuff[256]; + int pFD, rLen, idleMin; + +// Get the acess time and modification time +// + aTime = baseFile->Stat.st_atime; + mTime = baseFile->Stat.st_mtime; + +// File is not eligible if it's been accessed too recently +// + xTime = static_cast<int>(nowTime - aTime); + if (hTime && xTime <= hTime) return "is in hold"; + +// File is ineligible if ot has a fail file +// + if (sP->failFile()) return "has fail file"; + +// If there is a lock file and the file has not migrated, then ineligible +// Note that entries were pre-screened for lock file requirements. +// + if (lockFile && lockFile->Stat.st_mtime < mTime) return "not migrated"; + +// If there is no pin file, then it is eligible subject to external policy +// + if (!(pinFile = sP->pinFile())) return 0; + +// If the pin file sticky bit is on then the pin is permanent (ineligible) +// + if (pinFile->Stat.st_mode & S_ISUID) return "is perm pinned"; + +// If the mtime of the pin file is in the future this is when it gets unpinned +// + if (pinFile->Stat.st_mtime > nowTime) return "is time pinned"; + +// Check if there are more conditions +// + if (!(pinFile->Stat.st_size)) return 0; + if ( pinFile->Stat.st_size >= static_cast<off_t>(sizeof(pBuff))) + return "is pinned incorrectly"; + +// The contents of the file indicate how long the inactive period is, get it +// + if ((pFD = open(pinFile->Path, O_RDONLY)) < 0 + || (rLen = read(pFD, pBuff, sizeof(pBuff)-1)) < 0) + {const char *Why = "open"; + if (pFD >= 0) {close(pFD); Why = "read";} + Say.Emsg("Eligible", errno, Why, pinFile->Path); + return "is pinned"; + } + +// Get the inactivity period +// + close(pFD); + pBuff[rLen] = '\0'; + if (sscanf(pBuff,"&inact_time=%d",&idleMin) != 1) return "pinfile error"; + if (idleMin > xTime) return "is pin defered"; + return 0; +} + +/******************************************************************************/ +/* Private: F i n d */ +/******************************************************************************/ + +XrdFrmPurge *XrdFrmPurge::Find(const char *snp) +{ + XrdFrmPurge *spP = First; + +// See if we already have this space defined +// + while(spP && strcmp(snp, spP->SName)) spP = spP->Next; + return spP; +} + +/******************************************************************************/ +/* I n i t */ +/******************************************************************************/ + +int XrdFrmPurge::Init(XrdOucTList *sP, long long minV, int hVal) +{ + static char pVec[] = {char(XrdFrmConfig::PP_sname), + char(XrdFrmConfig::PP_pfn), + char(XrdFrmConfig::PP_fsize), + char(XrdFrmConfig::PP_atime), + char(XrdFrmConfig::PP_mtime) + }; + + XrdFrmConfig::VPInfo *vP; + XrdOssVSInfo vsInfo; + XrdFrmPurge *xP, *ppP = 0, *spP = First; + XrdOucTList *tP; + char xBuff[32]; + int setIt, rc, haveExt = 0; + +// The first step is to remove any defined policies for which there is no space +// + while(spP) + {vP = Config.VPList; + while(vP && strcmp(spP->SName, vP->Name)) vP = vP->Next; + if (!vP && strcmp("public", spP->SName)) + {Say.Emsg("Init", "Purge policy", spP->SName, + "deleted; space not defined."); + if (ppP) ppP->Next = spP->Next; + else First = spP->Next; + xP = spP; spP = spP->Next; delete xP; + } else {ppP = spP; spP = spP->Next;} + } + +// For each space enable it and optionally over-ride policy +// + spP = First; + while(spP) + {setIt = 1; + if ((tP = sP)) + {while(tP && strcmp(tP->text, spP->SName)) tP = tP->next; + if (!tP) setIt = 0; + } + if (setIt) + {if (minV) spP->minFSpace = spP->maxFSpace = minV; + if (hVal >= 0) {spP->Hold = hVal; spP->Hold2x = hVal*2;} + if (spP->minFSpace && spP->Hold >= 0) + {spP->Enabled = 1; haveExt |= spP->Ext;} + } + spP = spP->Next; + } + +// Go through each space policy getting the actual space and calculating +// the targets based on the policy (we need to do this only once) +// + spP = First; ppP = 0; + while(spP) + {if ((rc = Config.ossFS->StatVS(&vsInfo, spP->SName, 1))) + {Say.Emsg("Init", rc, "calculate space for", spP->SName); + if (ppP) ppP->Next = spP->Next; + else First = spP->Next; + xP = spP; spP = spP->Next; delete xP; continue; + } + spP->totlSpace = vsInfo.Total; + spP->spaceTLen = sprintf(xBuff, "%lld", vsInfo.Total); + spP->spaceTotl = strdup(xBuff); + spP->pmaxSpace = vsInfo.Large; + spP->spaceTLep = sprintf(xBuff, "%lld", vsInfo.Large); + spP->spaceTotP = strdup(xBuff); + if (spP->minFSpace < 0) + {spP->minFSpace = vsInfo.Total * XRDABS(spP->minFSpace) / 100LL; + spP->maxFSpace = vsInfo.Total * XRDABS(spP->maxFSpace) / 100LL; + } else if (vsInfo.Total < spP->minFSpace + || vsInfo.Total < spP->maxFSpace) + Say.Emsg("Init", "Warning: ", spP->SName, " min/max free " + "space policy exceeds total available!"); + ppP = spP; spP = spP->Next; + } + +// Make sure "public" still exists. While this should not happen, we check for +// this possibility anyway. +// + if (!(Default = Find("public"))) + {Say.Emsg("Init", "Unable to start purge; no public policy found."); + return 0; + } + +// If a policy program is present, then we need to verify it +// + if (Config.pProg && haveExt) + {PolProg = new XrdOucProg(&Say); + if (PolProg->Setup(Config.pProg) || PolProg->Start()) return 0; + PolStream = PolProg->getStream(); + if (!Config.pVecNum) + {memcpy(Config.pVec, pVec, sizeof(pVec)); + Config.pVecNum = sizeof(pVec); + } + } + +// All went well +// + return 1; +} + +/******************************************************************************/ +/* Private: L o w O n S p a c e */ +/******************************************************************************/ + +// This method *must* be called prior to Purge() and returns: +// =0 -> Purge not needed. +//!>0 -> Purge is needed. + +int XrdFrmPurge::LowOnSpace() +{ + XrdOssVSInfo VSInfo; + XrdFrmPurge *psP = First; + time_t eNow; + +// Recalculate free space and set initial status +// + Left2Do = 0; + while(psP) + {if (psP->Enabled) + {if (Config.ossFS->StatVS(&VSInfo, psP->SName, 1)) psP->Stop = 1; + else {psP->freeSpace = VSInfo.Free; + psP->contSpace = VSInfo.LFree; + psP->usedSpace = VSInfo.Usage; + if (psP->freeSpace >= psP->minFSpace) psP->Stop = 1; + else {Left2Do++; psP->Stop = 0;} + } + } else psP->Stop = 1; + psP = psP->Next; + } + +// If enough space then indicate no purge is needed +// + if (!Left2Do) return 0; + +// Reset all policies to prepare for purging +// + psP = First; + while(psP) + {psP->Clear(); + psP = psP->Next; + } + +// We must check whether or not a full name space scan is required. This is +// based on the last time we did one and whether or not a space needs one now. +// + eNow = time(0); + if (eNow >= nextReset) {lastReset = eNow; nextReset = 0; Scan();} + return 1; +} + +/******************************************************************************/ +/* P o l i c y */ +/******************************************************************************/ + +XrdFrmPurge *XrdFrmPurge::Policy(const char *sname, long long minV, + long long maxV, int hVal, int xVal) +{ + XrdFrmPurge *psP; + +// Find or create a new policy +// + if (!(psP = Find(sname))) First = psP = new XrdFrmPurge(sname, First); + +// Fill out the policy +// + psP->minFSpace = minV; + psP->maxFSpace = maxV; + psP->Hold = hVal; + psP->Hold2x = hVal*2; + psP->Ext = xVal; + return psP; +} + +/******************************************************************************/ +/* P u r g e */ +/******************************************************************************/ + +void XrdFrmPurge::Purge() +{ + XrdFrmPurge *psP = First; + +// Check if are low on space, if not, ignore the call +// + if (!LowOnSpace()) + {Say.Emsg("Purge", "Purge cycle skipped; all policies met."); + return; + } + +// Report data at the start of the purge cycle +// + Say.Emsg("Purge", "Purge cycle started."); + Stats(0); + +// Cycle through each space until we no longer can cycle +// +do{psP = First; + while(psP && Left2Do) + {if (!(psP->Stop) && (psP->Stop = psP->PurgeFile())) Left2Do--; + psP = psP->Next; + } + } while(Left2Do); + +// Report data at the end of the purge cycle +// + Stats(1); + Say.Emsg("Purge", "Purge cycle ended."); +} + +/******************************************************************************/ +/* Private: P u r g e F i l e */ +/******************************************************************************/ + +int XrdFrmPurge::PurgeFile() +{ + EPNAME("PurgeFile"); + XrdFrmFileset *fP; + const char *fn, *Why = "file in use"; + time_t xTime; + int rc, FilePurged = 0; + +// If we have don't have a file, see if we can grab some from the defer queue +// +do{if (!(fP = FSTab.Oldest()) && !(fP = Advance())) + {time_t nextScan = time(0)+Hold; + if (!nextReset || nextScan < nextReset) nextReset = nextScan; + return 1; + } + if (fP->Refresh() && !(Why = Eligible(fP, xTime, Hold)) + && (!Ext || !(Why = XPolOK(fP)))) + {fn = fP->basePath(); + if (Config.Test) rc = 0; + else if (!(rc = Config.ossFS->Unlink(fn, XRDOSS_isPFN))) + Config.cmsPath->Gone(fn); + if (!rc) {prgFiles++; FilePurged = 1; + freeSpace += fP->baseFile()->Stat.st_size; + purgBytes += fP->baseFile()->Stat.st_size; + if (Config.Verbose) Track(fP); + } + } else {DEBUG("Purge " <<SName <<": keeping " <<fP->basePath() <<"; " <<Why);} + delete fP; + } while(!FilePurged && !Stop); + +// All done, indicate whether we should stop now +// + return freeSpace >= maxFSpace || Stop; +} + +/******************************************************************************/ +/* Private: R e m f i x */ +/******************************************************************************/ + +void XrdFrmPurge::Remfix(const char *Ftype, const char *Fname) +{ +// Remove the offending file +// + if (!Config.ossFS->Unlink(Fname,XRDOSS_isPFN)) + Say.Emsg("Remfix", Ftype, "file orphan fixed; removed", Fname); +} + +/******************************************************************************/ +/* Private: S c a n */ +/******************************************************************************/ + + +void XrdFrmPurge::Scan() +{ + static const int Opts = XrdFrmFiles::Recursive | XrdFrmFiles::CompressD + | XrdFrmFiles::NoAutoDel; + static time_t lastHP = time(0), nextDP = 0, nowT = time(0); + static XrdFrmPurgeDir purgeDir; + static XrdOucNSWalk::CallBack *cbP; + + XrdFrmConfig::VPInfo *vP = Config.pathList; + XrdFrmFileset *sP; + XrdFrmFiles *fP; + const char *Extra; + char buff[128]; + int isRW, ec = 0, Bad = 0, aFiles = 0, bFiles = 0; + +// Purge that bad file table evey 24 hours to keep complaints down +// + if (nowT - lastHP >= 86400) {BadFiles.Purge(); lastHP = nowT;} + +// Determine if we need to do an empty directory trim +// + if (Config.dirHold < 0 || nowT < nextDP) {cbP = 0; Extra = 0;} + else {nextDP = nowT + Config.dirHold; + purgeDir.Reset(nowT - Config.dirHold); + cbP = (XrdOucNSWalk::CallBack *)&purgeDir; + Extra = "and empty directory"; + } + +// Indicate scan started +// + VMSG("Scan", "Name space", Extra, "scan started. . ."); + +// Process each directory +// + do {fP = new XrdFrmFiles(vP->Name, Opts, vP->Dir, cbP); + isRW = vP->Val; + while((sP = fP->Get(ec,1))) + {aFiles++; + if (Screen(sP, isRW)) Add(sP); + else {delete sP; bFiles++;} + } + if (ec) Bad = 1; + delete fP; + } while((vP = vP->Next)); + +// If we did a directory purge, schedule the next one and say what we did +// + if (cbP) + {if ((purgeDir.numEMD - purgeDir.numRMD) > 0 + && purgeDir.lowDirTime + Config.dirHold < nextDP) + nextDP = purgeDir.lowDirTime + Config.dirHold; + sprintf(buff, "%d of %d empty dir%s removed", purgeDir.numRMD, + purgeDir.numEMD, (purgeDir.numEMD != 1 ? "s":"")); + VMSG("Scan", "Empty directory space scan ended;", buff); + } + +// Indicate scan ended +// + sprintf(buff, "%d file%s with %d error%s", aFiles, (aFiles != 1 ? "s":""), + bFiles, (bFiles != 1 ? "s":"")); + VMSG("Scan", "Name space scan ended;", buff); + +// Issue warning if we encountered errors +// + if (Bad) Say.Emsg("Scan", "Errors encountered while scanning for " + "purgeable files."); +} + +/******************************************************************************/ +/* Private: S c r e e n */ +/******************************************************************************/ + +int XrdFrmPurge::Screen(XrdFrmFileset *sP, int isRW) +{ + const char *What = 0, *badFN = 0; + char dPath[MAXPATHLEN+1]; + +// Verify that we have all the relevant files +// + if (!(sP->baseFile())) + {if (Config.Fix) + {if (sP->lockFile()) Remfix("Lock", sP->lockPath()); + if (sP-> pinFile()) Remfix("Pin", sP-> pinPath()); + if (sP->failFile()) Remfix("Fail", sP->failPath()); + return 0; + } + What = "No base file for"; + if (sP->lockFile()) badFN = sP->lockPath(); + else if (sP-> pinFile()) badFN = sP-> pinPath(); + else if (sP->failFile()) badFN = sP->failPath(); + else {What = "Orhpaned files in"; badFN = dPath; + sP->dirPath(dPath, sizeof(dPath)); + } + } else if (isRW && !(sP->lockFile())) + {What = "No lock file for"; badFN = sP->basePath();} + else return 1; + +// Issue message if we haven't issued one before +// + if (!BadFiles.Add(badFN, 0, 0, Hash_data_is_key)) + Say.Emsg("Screen", What, badFN); + return 0; +} + +/******************************************************************************/ +/* Private: S t a t s */ +/******************************************************************************/ + +void XrdFrmPurge::Stats(int Final) +{ + XrdFrmPurge *xsP, *psP = First; + long long pVal, xBytes, zBytes; + const char *xWhat, *nWhat, *zWhat; + char fBuff[64], uBuff[80], sBuff[512], xBuff[64], zBuff[64]; + int nFiles; + +// Report data for each enabled space +// + while((xsP = psP)) + {psP = psP->Next; + if (!(xsP->Enabled)) continue; + if (xsP->usedSpace >= 0) + {if (Final) xsP->usedSpace -= xsP->purgBytes; + XrdOucUtils::fmtBytes(xsP->usedSpace, fBuff, sizeof(fBuff)); + pVal = xsP->usedSpace*100/xsP->totlSpace; + sprintf(uBuff, "used %s (%lld%%) ", fBuff, pVal); + } else *uBuff = '\0'; + XrdOucUtils::fmtBytes(xsP->freeSpace, fBuff, sizeof(fBuff)); + pVal = xsP->freeSpace*100/xsP->totlSpace; + if (Final) + {xBytes = xsP->purgBytes; xWhat = "freed"; + if ((zBytes = xsP->maxFSpace - xsP->freeSpace) > 0) + {XrdOucUtils::fmtBytes(zBytes, zBuff, sizeof(zBuff)); + zWhat = " deficit"; + } else {*zBuff = '\0'; zWhat = "goal met";} + nFiles = xsP->prgFiles; nWhat = "prgd"; + } else { + xBytes = (xsP->freeSpace < xsP->minFSpace + ? xsP->maxFSpace - xsP->freeSpace : 0); + nFiles = xsP->FSTab.Count(); + xWhat = "goal"; nWhat = "idle"; *zBuff = '\0'; zWhat = ""; + } + XrdOucUtils::fmtBytes(xBytes, xBuff, sizeof(xBuff)); + sprintf(sBuff, " %sfree %s (%lld%%) %d files %d %s; %s %s %s%s", + uBuff,fBuff,pVal,xsP->numFiles,nFiles,nWhat, + xBuff,xWhat,zBuff,zWhat); + Say.Say("++++++ ", xsP->SName, sBuff); + } +} + +/******************************************************************************/ +/* Private: T r a c k */ +/******************************************************************************/ + +void XrdFrmPurge::Track(XrdFrmFileset *sP) +{ + XrdOucNSWalk::NSEnt *fP = sP->baseFile(); + const char *What = (Config.Test ? "Zorch " : "Purged "); + char sbuff[128], fszbf[16]; + struct tm tNow; + +// Format the size +// + XrdOucUtils::fmtBytes(static_cast<long long>(fP->Stat.st_size), + fszbf, sizeof(fszbf)); + +// Format the information and display it +// + localtime_r(&(fP->Stat.st_atime), &tNow); + sprintf(sbuff, " %8s %02d%02d%02d %02d:%02d:%02d ", fszbf, + tNow.tm_year-100, tNow.tm_mon+1, tNow.tm_mday, + tNow.tm_hour, tNow.tm_min, tNow.tm_sec); + + Say.Say(What, SName, sbuff, sP->basePath()); +} + +/******************************************************************************/ +/* Private: X P o l O K */ +/******************************************************************************/ + +const char *XrdFrmPurge::XPolOK(XrdFrmFileset *fsP) +{ + static char neg1[] = {'-','1','\0'}; + XrdOucNSWalk::NSEnt *fP = fsP->baseFile(); + char *Data[sizeof(Config.pVec)*2+2]; + int Dlen[sizeof(Config.pVec)*2+2]; + char atBuff[32], ctBuff[32], mtBuff[32], fsBuff[32], spBuff[32], usBuff[32]; + char *Resp; + int i, k = 0; + +// Construct the data to be sent (not mt here) +// + for (i = 0; i < Config.pVecNum; i++) + {switch(Config.pVec[i]) + {case XrdFrmConfig::PP_atime: + Data[k] = atBuff; + Dlen[k] = sprintf(atBuff, "%lld", + static_cast<long long>(fP->Stat.st_atime)); + break; + case XrdFrmConfig::PP_ctime: + Data[k] = ctBuff; + Dlen[k] = sprintf(ctBuff, "%lld", + static_cast<long long>(fP->Stat.st_ctime)); + break; + case XrdFrmConfig::PP_fname: + Data[k] = fP->File; Dlen[k] = strlen(fP->File); + break; + case XrdFrmConfig::PP_fsize: + Data[k] = fsBuff; + Dlen[k] = sprintf(fsBuff, "%lld", + static_cast<long long>(fP->Stat.st_size)); + break; + case XrdFrmConfig::PP_fspace: + Data[k] = spBuff; + Dlen[k] = sprintf(spBuff, "%lld", freeSpace); + break; + case XrdFrmConfig::PP_mtime: + Data[k] = mtBuff; + Dlen[k] = sprintf(mtBuff, "%lld", + static_cast<long long>(fP->Stat.st_mtime)); + break; + case XrdFrmConfig::PP_pfn: + Data[k] = (char *)fsP->basePath(); + Dlen[k] = strlen(Data[k]); + break; + case XrdFrmConfig::PP_sname: + Data[k] = SName; Dlen[k] = SNlen; + break; + case XrdFrmConfig::PP_tspace: + Data[k] = spaceTotl; Dlen[k] = spaceTLen; + break; + case XrdFrmConfig::PP_usage: + if (usedSpace < 0) {Data[k] = neg1; Dlen[k]=2;} + else {Dlen[k] = sprintf(usBuff, "%lld", + usedSpace - purgBytes); + Data[k] = usBuff; + } + break; + default: break; + } + Data[++k] = (char *)" "; Dlen[k] = 1; k++; + } + +// Now finish up the vector +// + Data[k-1] = (char *)"\n"; Data[k] = 0; Dlen[k] = 0; + +// Feed the program this information get the response +// + if (PolProg->Feed((const char **)Data, Dlen) || !(Resp=PolStream->GetLine())) + {Stop = 1; return "external policy failed";} + +// Decode the response (single line with a charcater y|n|a) +// + if (*Resp == 'y') return 0; + if (*Resp == 'n') return "external policy reject"; + Stop = 1; + return "external policy stop"; +} diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmPurge.hh b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmPurge.hh new file mode 100644 index 0000000000000000000000000000000000000000..cad3bc87a94190962fa6b193af245f5d9f9418cb --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmPurge.hh @@ -0,0 +1,108 @@ +#ifndef __FRMPURGE__ +#define __FRMPURGE__ +/******************************************************************************/ +/* */ +/* X r d F r m P u r g e . h h */ +/* */ +/* (c) 2009 by the Board of Trustees of the Leland Stanford, Jr., University */ +/* All Rights Reserved */ +/* Produced by Andrew Hanushevsky for Stanford University under contract */ +/* DE-AC02-76-SFO0515 with the Department of Energy */ +/******************************************************************************/ + +// $Id$ + +#include <time.h> +#include <sys/types.h> + +#include "XrdFrm/XrdFrmTSort.hh" +#include "XrdOss/XrdOssSpace.hh" +#include "XrdOuc/XrdOucHash.hh" + +class XrdFrmFileset; +class XrdOucPolProg; +class XrdOucStream; +class XrdOucTList; + +class XrdFrmPurge +{ +public: + +static void Display(); + +static int Init(XrdOucTList *sP=0, long long minV=-1, int hVal=-1); + +static XrdFrmPurge *Policy(const char *sname) {return Find(sname);} +static XrdFrmPurge *Policy(const char *sname, long long minV, long long maxV, + int hVal, int xVal); + +static void Purge(); + + XrdFrmPurge(const char *snp, XrdFrmPurge *spp=0); + ~XrdFrmPurge() {Clear();} + +private: + +// Methods +// +static void Add(XrdFrmFileset *fsp); + XrdFrmFileset*Advance(); + void Clear(); + void Defer(XrdFrmFileset *sP, time_t xTime); +const char *Eligible(XrdFrmFileset *sP, time_t &xTime, int hTime=0); +static XrdFrmPurge *Find(const char *snp); +static int LowOnSpace(); + int PurgeFile(); +static void Remfix(const char *Ftype, const char *Fname); +static void Scan(); +static int Screen(XrdFrmFileset *sP, int isRW); +static void Stats(int Final); + void Track(XrdFrmFileset *sP); +const char *XPolOK(XrdFrmFileset *sP); +static XrdOucProg *PolProg; +static XrdOucStream *PolStream; + +// Static Variables + +static XrdOucHash<char> BadFiles; +static time_t lastReset; +static time_t nextReset; + +static XrdFrmPurge *First; +static XrdFrmPurge *Default; + +static int Left2Do; + +// Variables local to each object +// +long long freeSpace; // Current free space +long long fconMaxsp; // Current free space contiguous +long long usedSpace; // Curreny used space (if supported) +long long pmaxSpace; // PMax space (computed once) +long long totlSpace; // Total space (computed once) +long long contSpace; // Total contg (computed once) +long long purgBytes; // Purged bytes on last purge cycle +long long minFSpace; // Minimum free space +long long maxFSpace; // Maximum free space (what we purge to) +char *spaceTotl; +char *spaceTotP; +int spaceTLen; +int spaceTLep; +int Hold; // Hold value +int Hold2x; // Hold x2 (what we actually use) +int Ext; // External policy applies +int numFiles; // Total number of files +int prgFiles; // Total number of purged +int Enabled; +int Stop; +int SNlen; + +XrdFrmPurge *Next; +XrdFrmTSort FSTab; +char SName[XrdOssSpace::minSNbsz]; + +static const int DeferQsz = 16; +XrdFrmFileset *DeferQ[DeferQsz]; +time_t DeferT[DeferQsz]; +}; +#endif diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmTSort.cc b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmTSort.cc new file mode 100644 index 0000000000000000000000000000000000000000..f77e0a1282bba0f5f54c4019d05277b22b592e1a --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmTSort.cc @@ -0,0 +1,154 @@ +/******************************************************************************/ +/* */ +/* X r d F r m T S o r t . c c */ +/* */ +/* (c) 2009 by the Board of Trustees of the Leland Stanford, Jr., University */ +/* All Rights Reserved */ +/* Produced by Andrew Hanushevsky for Stanford University under contract */ +/* DE-AC02-76-SFO0515 with the Department of Energy */ +/******************************************************************************/ + +// $Id$ + +const char *XrdFrmTSortCVSID = "$Id$"; + +#include "XrdFrm/XrdFrmFiles.hh" +#include "XrdFrm/XrdFrmTSort.hh" +//#include "iostream.h" + +/******************************************************************************/ +/* A d d */ +/******************************************************************************/ + +int XrdFrmTSort::Add(XrdFrmFileset *fsp) +{ + XrdOucNSWalk::NSEnt *nsp = fsp->baseFile(); + int n; + +// Make sure we can actuall add this entry +// + if (baseT < nsp->Stat.st_atime) return 0; + +// Get the relative time +// + fsp->Age = static_cast<int>(baseT - nsp->Stat.st_atime); + +// Insert into the table +// + n = fsp->Age/dVal; + if (n > 63) n = 63; + fsp->Next = FSTab[0][n]; + FSTab[0][n] = fsp; + if (n > DYent) DYent = n; +//cerr <<"Add " <<std::hex <<fsp->Age <<' ' <<std::dec <<0 <<',' <<n <<' ' <<fsp->basePath() <<endl; + numEnt++; + return 1; +} + +/******************************************************************************/ +/* Private: B i n */ +/******************************************************************************/ + +int XrdFrmTSort::Bin(XrdFrmFileset *fsp, int j, int Shift) +{ + XrdFrmFileset *fsq; + int k, n = 0; + + while((fsq = fsp)) + {fsp = fsp->Next; + k = (fsq->Age >> Shift) & tMask; + if (k > n) n = k; + if (Shift || !sortSZ) fsq->Next = FSTab[j][k]; + else fsq = Insert(fsq, FSTab[j][k]); + FSTab[j][k] = fsq; +//cerr <<"Bin " <<std::hex <<fsq->Age <<' ' <<std::dec <<j <<',' <<k <<' ' <<fsq->basePath() <<endl; + } + return n; +} + +/******************************************************************************/ +/* Private: I n s e r t */ +/******************************************************************************/ + +XrdFrmFileset *XrdFrmTSort::Insert(XrdFrmFileset *newP, XrdFrmFileset *oldP) +{ + XrdFrmFileset *prvP = 0, *nowP = oldP; + off_t newSize = newP->baseFile()->Stat.st_size; + +// Find insertion point of new element (decreasing size order) +// + while(nowP && newSize < nowP->baseFile()->Stat.st_size) + {prvP = nowP; nowP = nowP->Next;} + +// Perform insertion +// + if (prvP) {prvP->Next = newP; newP->Next = nowP;} + else newP->Next = nowP; + +// Return correct head of list +// + return (prvP ? oldP : newP); +} + +/******************************************************************************/ +/* O l d e s t */ +/******************************************************************************/ + +XrdFrmFileset *XrdFrmTSort::Oldest() +{ + XrdFrmFileset *fsp = 0; + +// Work backwards on the list, resorting as needed +// + do {while(SCent >= 0) + {if ((fsp = FSTab[3][SCent])) + {if (!( FSTab[3][SCent] = fsp->Next)) SCent--; + numEnt--; +//cerr <<"Oldest " <<fsp->Age <<' ' <<fsp->basePath() <<endl; + return fsp; + } else SCent--; + } +//cerr <<"SC=" <<SCent <<" MN=" <<MNent <<" HR=" <<HRent <<" DY=" <<DYent <<endl; + fsp = 0; + while(MNent >= 0 && !fsp) fsp = FSTab[2][MNent--]; + if (fsp) {FSTab[2][MNent+1]=0; SCent = Bin(fsp, 3, SCshift); continue;} + while(HRent >= 0 && !fsp) fsp = FSTab[1][HRent--]; + if (fsp) {FSTab[1][HRent+1]=0; MNent = Bin(fsp, 2, MNshift); continue;} + while(DYent >= 0 && !fsp) fsp = FSTab[0][DYent--]; + if (fsp) {FSTab[0][DYent+1]=0; HRent = Bin(fsp, 1, HRshift); continue;} + } while(numEnt); + return 0; +} + +/******************************************************************************/ +/* P u r g e */ +/******************************************************************************/ + +void XrdFrmTSort::Purge() +{ + XrdFrmFileset *fsp, *csp; + int i, j, aBeg[4] = {DYent, HRent, MNent, SCent}; + + for (i = 0; i < 4; i++) + for (j = aBeg[i]; j >= 0; j--) + {if ((fsp = FSTab[i][j])) + while((csp = fsp)) {fsp = fsp->Next; delete csp;} + } + Reset(); +} + +/******************************************************************************/ +/* Private: R e s e t */ +/* */ +/******************************************************************************/ + +void XrdFrmTSort::Reset() +{ + +// Clear the base table and set base time +// + memset(FSTab, 0, sizeof(FSTab)); + DYent = HRent = MNent = SCent = -1; + baseT = time(0); + numEnt = 0; +} diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmTSort.hh b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmTSort.hh new file mode 100644 index 0000000000000000000000000000000000000000..710b04bcfe1e4dbc682f3a8642615aa4d3b580e9 --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmTSort.hh @@ -0,0 +1,53 @@ +#ifndef __FRMTSORT__ +#define __FRMTSORT__ +/******************************************************************************/ +/* */ +/* X r d F r m T S o r t . h h */ +/* */ +/* (c) 2009 by the Board of Trustees of the Leland Stanford, Jr., University */ +/* All Rights Reserved */ +/* Produced by Andrew Hanushevsky for Stanford University under contract */ +/* DE-AC02-76-SFO0515 with the Department of Energy */ +/******************************************************************************/ + +// $Id$ + +class XrdFrmFileset; + +class XrdFrmTSort +{ +public: + +int Add(XrdFrmFileset *fsp); + +int Count() {return numEnt;} + +XrdFrmFileset *Oldest(); + +void Purge(); + + XrdFrmTSort(int szSort=0) : sortSZ(szSort) {Reset();} + ~XrdFrmTSort() {Purge();} + +private: +int Bin(XrdFrmFileset *fsp, int j, int Shift); +XrdFrmFileset *Insert(XrdFrmFileset *newP, XrdFrmFileset *oldP); +void Reset(); + +static const int SCshift = 0; +static const int MNshift = 6; +static const int HRshift = 12; +static const int tMask = 0x3f; +static const int dVal = 24*60*60; + +XrdFrmFileset *FSTab[4][64]; +time_t baseT; +int sortSZ; +int numEnt; + +int DYent; // [0,DYent] +int HRent; // [1,HRent] +int MNent; // [2,MNent] +int SCent; // [3,SCent] +}; +#endif diff --git a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmTrace.hh b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmTrace.hh index 15f693a53ecda4efa5d668a3956572d78c674dcb..ab7e1b3960bed5d9dddfd0dc3372962ff65f38b4 100644 --- a/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmTrace.hh +++ b/net/xrootd/src/xrootd/src/XrdFrm/XrdFrmTrace.hh @@ -35,8 +35,7 @@ {Trace.Beg(epname, Req.User); cerr <<y; Trace.End();} #define TRACEX(y) {Trace.Beg(0,epname); cerr <<y; Trace.End();} - -#define EPNAME(x) const char *epname = x; +#define EPNAME(x) static const char *epname = x; #else @@ -46,6 +45,10 @@ #endif +#define VMSG(a,...) if (Config.Verbose) Say.Emsg(a,__VA_ARGS__); + +#define VSAY(a,...) if (Config.Verbose) Say.Say(a,__VA_ARGS__); + namespace XrdFrm { extern XrdSysLogger Logger; diff --git a/net/xrootd/src/xrootd/src/XrdOdc/XrdOdcResp.cc b/net/xrootd/src/xrootd/src/XrdOdc/XrdOdcResp.cc index 6a6609a9b0f48de28999489c8f35c21a3d8320c8..e499907fa24b3ace62c1a22f6ea87651dd7deb22 100644 --- a/net/xrootd/src/xrootd/src/XrdOdc/XrdOdcResp.cc +++ b/net/xrootd/src/xrootd/src/XrdOdc/XrdOdcResp.cc @@ -83,11 +83,13 @@ void XrdOdcResp::Recycle() // Put this object on the free queue // - myMutex.Lock(); - next = nextFree; - nextFree = this; - numFree++; - myMutex.UnLock(); + if (numFree >= maxFree) delete this; + else {myMutex.Lock(); + next = nextFree; + nextFree = this; + numFree++; + myMutex.UnLock(); + } } /******************************************************************************/ @@ -97,6 +99,7 @@ void XrdOdcResp::Recycle() void XrdOdcResp::Reply(const char *Man, char *msg) { EPNAME("Reply") + XrdOucEICB *theCB; int Result, msgval; char *colon, *opaque; @@ -174,9 +177,11 @@ void XrdOdcResp::Reply(const char *Man, char *msg) // SyncCB.Wait(); -// Invoke the callback; it must explicitly invoke delete on our upcast object. +// Invoke the callback; telling it to call us back for recycling // - ErrCB->Done(Result, (XrdOucErrInfo *)this); + theCB = ErrCB; + ErrCB = (XrdOucEICB *)this; + theCB->Done(Result, (XrdOucErrInfo *)this); } /******************************************************************************/ diff --git a/net/xrootd/src/xrootd/src/XrdOdc/XrdOdcResp.hh b/net/xrootd/src/xrootd/src/XrdOdc/XrdOdcResp.hh index 32001222485824239836691cc8d0d87ce1e6a46f..de6972aec4df3e58267c47d7a63b9923411bffc5 100644 --- a/net/xrootd/src/xrootd/src/XrdOdc/XrdOdcResp.hh +++ b/net/xrootd/src/xrootd/src/XrdOdc/XrdOdcResp.hh @@ -43,27 +43,27 @@ XrdSysSemaphore respSync; /* X r d O d c R e s p */ /******************************************************************************/ -class XrdOdcResp : public XrdOucErrInfo +class XrdOdcResp : public XrdOucErrInfo, public XrdOucEICB { public: friend class XrdOdcRespQ; static XrdOdcResp *Alloc(XrdOucErrInfo *erp, int msgid); + void Done(int &Result, XrdOucErrInfo *eInfo) {Recycle();} + inline int ID() {return myID;} void Reply(const char *Man, char *reply); + int Same(unsigned long long arg1, unsigned long long arg2) + {return 0;} + static void setDelay(int repdly) {RepDelay = repdly;} XrdOdcResp() : XrdOucErrInfo(UserID) {next = 0;} ~XrdOdcResp() {} -void operator delete(void *p) - {if (XrdOdcResp::numFree >= XrdOdcResp::maxFree) free(p); - else ((XrdOdcResp *)p)->Recycle(); - } - private: void Recycle(); diff --git a/net/xrootd/src/xrootd/src/XrdOfs/XrdOfs.cc b/net/xrootd/src/xrootd/src/XrdOfs/XrdOfs.cc index 25e9977117af61e9744ced64f381f28646c5c7e0..3a8c3c1c2bedc85dcf554d3a3734ae36efe1e1e2 100644 --- a/net/xrootd/src/xrootd/src/XrdOfs/XrdOfs.cc +++ b/net/xrootd/src/xrootd/src/XrdOfs/XrdOfs.cc @@ -227,7 +227,7 @@ int XrdOfsDirectory::open(const char *dir_path, // In */ { EPNAME("opendir"); - XrdOucEnv Open_Env(info); + XrdOucEnv Open_Env(info,0,client); int retc; // Trace entry @@ -422,7 +422,7 @@ int XrdOfsFile::open(const char *path, // In mode_t theMode = Mode & S_IAMB; int retc, isPosc = 0, crOpts = 0, isRW = 0, open_flag = 0; int find_flag = open_mode & (SFS_O_NOWAIT | SFS_O_RESET); - XrdOucEnv Open_Env(info); + XrdOucEnv Open_Env(info,0,client); // Trace entry // @@ -1194,7 +1194,7 @@ int XrdOfs::chmod(const char *path, // In EPNAME("chmod"); mode_t acc_mode = Mode & S_IAMB; const char *tident = einfo.getErrUser(); - XrdOucEnv chmod_Env(info); + XrdOucEnv chmod_Env(info,0,client); int retc; XTRACE(chmod, path, ""); @@ -1261,7 +1261,7 @@ int XrdOfs::exists(const char *path, // In struct stat fstat; int retc; const char *tident = einfo.getErrUser(); - XrdOucEnv stat_Env(info); + XrdOucEnv stat_Env(info,0,client); XTRACE(exists, path, ""); // Apply security, as needed @@ -1339,13 +1339,17 @@ int XrdOfs::fsctl(const int cmd, // if (opcode == SFS_FSCTL_LOCATE) {struct stat fstat; + const char *Path, *locArg; char rType[3], *Resp[] = {rType, locResp}; - AUTHORIZE(client,0,AOP_Stat,"locate",args,einfo); + if (*args == '*') {Path = args+1; locArg = args;} + else if (cmd & SFS_O_TRUNC) {Path = args; locArg = (char *)"*";} + else Path = locArg = args; + AUTHORIZE(client,0,AOP_Stat,"locate",Path,einfo); if (Finder && Finder->isRemote() - && (retc = Finder->Locate(einfo, args, find_flag))) + && (retc = Finder->Locate(einfo, locArg, find_flag))) return fsError(einfo, retc); - if ((retc = XrdOfsOss->Stat(args, &fstat))) - return XrdOfsFS.Emsg(epname, einfo, retc, "locate", args); + if ((retc = XrdOfsOss->Stat(Path, &fstat))) + return XrdOfsFS.Emsg(epname, einfo, retc, "locate", Path); rType[0] = ((fstat.st_mode & S_IFBLK) == S_IFBLK ? 's' : 'S'); rType[1] = (fstat.st_mode & S_IWUSR ? 'w' : 'r'); rType[2] = '\0'; @@ -1371,7 +1375,7 @@ int XrdOfs::fsctl(const int cmd, if (opcode == SFS_FSCTL_STATLS) {const char *path; char pbuff[1024], *opq = (char *) index(args, '?'); - XrdOucEnv statls_Env(opq ? opq+1 : 0); + XrdOucEnv statls_Env(opq ? opq+1 : 0,0,client); if (!opq) path = args; else {int plen = opq-args; if (plen >= (int)sizeof(pbuff)) plen = sizeof(pbuff)-1; @@ -1474,7 +1478,7 @@ int XrdOfs::mkdir(const char *path, // In mode_t acc_mode = Mode & S_IAMB; int retc, mkpath = Mode & SFS_O_MKPTH; const char *tident = einfo.getErrUser(); - XrdOucEnv mkdir_Env(info); + XrdOucEnv mkdir_Env(info,0,client); XTRACE(mkdir, path, ""); // Apply security, as needed @@ -1560,7 +1564,7 @@ int XrdOfs::remove(const char type, // In EPNAME("remove"); int retc, Opt; const char *tident = einfo.getErrUser(); - XrdOucEnv rem_Env(info); + XrdOucEnv rem_Env(info,0,client); XTRACE(remove, path, type); // Apply security, as needed @@ -1628,8 +1632,8 @@ int XrdOfs::rename(const char *old_name, // In EPNAME("rename"); int retc; const char *tident = einfo.getErrUser(); - XrdOucEnv old_Env(infoO); - XrdOucEnv new_Env(infoN); + XrdOucEnv old_Env(infoO,0,client); + XrdOucEnv new_Env(infoN,0,client); XTRACE(rename, new_name, "old fn=" <<old_name <<" new "); // Apply security, as needed @@ -1692,7 +1696,7 @@ int XrdOfs::stat(const char *path, // In EPNAME("stat"); int retc; const char *tident = einfo.getErrUser(); - XrdOucEnv stat_Env(info); + XrdOucEnv stat_Env(info,0,client); XTRACE(stat, path, ""); // Apply security, as needed @@ -1737,7 +1741,7 @@ int XrdOfs::stat(const char *path, // In struct stat buf; int retc; const char *tident = einfo.getErrUser(); - XrdOucEnv stat_Env(info); + XrdOucEnv stat_Env(info,0,client); XTRACE(stat, path, ""); // Apply security, as needed @@ -1783,7 +1787,7 @@ int XrdOfs::truncate(const char *path, // In { EPNAME("truncate"); const char *tident = einfo.getErrUser(); - XrdOucEnv trunc_Env(info); + XrdOucEnv trunc_Env(info,0,client); int retc; XTRACE(truncate, path, ""); diff --git a/net/xrootd/src/xrootd/src/XrdOfs/XrdOfsEvr.cc b/net/xrootd/src/xrootd/src/XrdOfs/XrdOfsEvr.cc index af2038029b807e336da3d5b546034c6bf41a46a0..51d9aa9b93bf2065d9bd832b5443e1398d0288c1 100644 --- a/net/xrootd/src/xrootd/src/XrdOfs/XrdOfsEvr.cc +++ b/net/xrootd/src/xrootd/src/XrdOfs/XrdOfsEvr.cc @@ -25,7 +25,8 @@ const char *XrdOfsEvrCVSID = "$Id$"; #include "XrdOuc/XrdOucTrace.hh" #include "XrdNet/XrdNetOpts.hh" #include "XrdNet/XrdNetSocket.hh" - +#include "XrdSys/XrdSysHeaders.hh" + /******************************************************************************/ /* E x t e r n a l L i n k a g e s */ /******************************************************************************/ @@ -171,7 +172,7 @@ int XrdOfsEvr::Init(XrdSysError *eobj, XrdCmsClient *trgp) void XrdOfsEvr::recvEvents() { - static const char *epname = "recvEvent"; + EPNAME("recvEvent"); const char *tident = 0; char *lp,*tp; @@ -321,7 +322,7 @@ void XrdOfsEvr::sendEvent(theEvent *ep) // to it just in case a client is in-transit // while((cp = ep->aClient)) - {einfo = new XrdOucErrInfo(cp->User, cp->evtCB, cp->evtCBarg); + {einfo = new XrdOucErrInfo(cp->User, 0, cp->evtCBarg); einfo->setErrInfo(ep->finalRC, (ep->finalMsg ? ep->finalMsg : "")); cp->evtCB->Done(Result, einfo); ep->aClient = cp->Next; diff --git a/net/xrootd/src/xrootd/src/XrdOss/XrdOss.hh b/net/xrootd/src/xrootd/src/XrdOss/XrdOss.hh index a415c2ae5ee1c6dbb73a48e058724b6cc5ebd012..939165d64e53e77d1e0adfe53fbaf998110ecf66 100644 --- a/net/xrootd/src/xrootd/src/XrdOss/XrdOss.hh +++ b/net/xrootd/src/xrootd/src/XrdOss/XrdOss.hh @@ -14,8 +14,10 @@ #include <dirent.h> #include <errno.h> +#include <strings.h> #include <sys/stat.h> #include <sys/types.h> +#include <string.h> class XrdOucEnv; class XrdSysLogger; @@ -137,6 +139,11 @@ virtual int Stats(char *bp, int bl) {return 0;} virtual int StatVS(XrdOssVSInfo *sP, const char *sname=0, int updt=0) {return -ENOTSUP;} +virtual int Lfn2Pfn(const char *Path, char *buff, int blen) + {if ((int)strlen(Path) >= blen) return -ENAMETOOLONG; + strcpy(buff, Path); return 0; + } + XrdOss() {} virtual ~XrdOss() {} }; diff --git a/net/xrootd/src/xrootd/src/XrdOss/XrdOssApi.cc b/net/xrootd/src/xrootd/src/XrdOss/XrdOssApi.cc index 38f78c37b53aee41623848a89dde55a95cc93f3a..d34e5201bf958dd78274c5571e90a02341df5185 100644 --- a/net/xrootd/src/xrootd/src/XrdOss/XrdOssApi.cc +++ b/net/xrootd/src/xrootd/src/XrdOss/XrdOssApi.cc @@ -53,7 +53,6 @@ const char *XrdOssApiCVSID = "$Id$"; #ifdef XRDOSSCX #include "oocx_CXFile.h" #endif -// IOS_USING_DECLARATION_MARKER - BaBar iostreams migration, do not touch this line! /******************************************************************************/ /* E r r o r R o u t i n g O b j e c t */ @@ -153,6 +152,18 @@ int XrdOssSys::Init(XrdSysLogger *lp, const char *configfn) return XrdOssOK; } +/******************************************************************************/ +/* L f n 2 P f n */ +/******************************************************************************/ + +int XrdOssSys::Lfn2Pfn(const char *oldp, char *newp, int blen) +{ + if (lcl_N2N) return -(lcl_N2N->lfn2pfn(oldp, newp, blen)); + if ((int)strlen(oldp) >= blen) return -ENAMETOOLONG; + strcpy(newp, oldp); + return 0; +} + /******************************************************************************/ /* G e n L o c a l P a t h */ /******************************************************************************/ @@ -406,9 +417,7 @@ int XrdOssSys::Truncate(const char *path, unsigned long long size) */ int XrdOssDir::Opendir(const char *dir_path) { -#ifndef NODEBUG - const char *epname = "Opendir"; -#endif + EPNAME("Opendir"); char actual_path[MAXPATHLEN+1], *local_path, *remote_path; unsigned long long isremote; int retc; @@ -432,11 +441,10 @@ int XrdOssDir::Opendir(const char *dir_path) // If this is a local filesystem request, open locally. // - if (!isremote) + if (!isremote || (pflags & XRDEXP_NODREAD)) {TRACE(Opendir, "lcl path " <<local_path <<" (" <<dir_path <<")"); - if (!(lclfd = opendir((char *)local_path))) return -errno; - isopen = 1; - return XrdOssOK; + if ((lclfd = opendir((char *)local_path))) {isopen = 1; return XrdOssOK;} + else if (!isremote) return -errno; } // Generate remote path @@ -447,27 +455,19 @@ int XrdOssDir::Opendir(const char *dir_path) else remote_path = actual_path; else remote_path = (char *)dir_path; -// Trace this remote request -// - TRACE(Opendir, "rmt path " <<remote_path <<" (" <<dir_path <<")"); + TRACE(Opendir, "rmt path " << remote_path <<" (" << dir_path <<")"); -// If we need not read the actual directory, just check if it exists +// If NOCHECK is in effect and we have an mss meta-cmd, just do a stat // - if (pflags & XRDEXP_NODREAD) + if (!(pflags & XRDEXP_NOCHECK) && XrdOssSS->MSSgwCmd) {struct stat fstat; - if (stat(local_path, &fstat)) - {if (pflags & XRDEXP_NOCHECK) fstat.st_mode = S_IFDIR; - else {if (!XrdOssSS->MSSgwCmd) return -errno; - if ((retc = XrdOssSS->MSS_Stat(remote_path, &fstat))) - return retc; - } - } + if ((retc = XrdOssSS->MSS_Stat(remote_path,&fstat))) return retc; if (!(S_ISDIR(fstat.st_mode))) return -ENOTDIR; - isopen = -1; + isopen = 1; return XrdOssOK; } -// This is a remote directory and we must read it. Perform remote open +// Open the directory at the remote location. // if (!(mssfd = XrdOssSS->MSS_Opendir(remote_path, retc))) return retc; isopen = 1; @@ -936,6 +936,7 @@ int XrdOssFile::Open_ufs(const char *path, int Oflag, int Mode, unsigned long long popts) { EPNAME("Open_ufs") + static const int isWritable = O_WRONLY|O_RDWR; int myfd, newfd, retc; #ifndef NODEBUG char *ftype = (char *)" path="; @@ -955,6 +956,13 @@ int XrdOssFile::Open_ufs(const char *path, int Oflag, int Mode, do { myfd = open(path, Oflag|O_LARGEFILE, Mode);} while( myfd < 0 && errno == EINTR); +// If the file is marked purgeable or migratable and we may modify this file, +// then get a shared lock on the file to keep it from being migrated or purged +// while it is open. +// + if (popts & XRDEXP_PURGE || (popts & XRDEXP_MIG && Oflag & isWritable)) + ufs_file.Serialize(myfd, XrdOssSHR); + // Chck if file is compressed // if (myfd < 0) myfd = -errno; diff --git a/net/xrootd/src/xrootd/src/XrdOss/XrdOssApi.hh b/net/xrootd/src/xrootd/src/XrdOss/XrdOssApi.hh index ea691c2a95ddf13d4486abb866c108f408094754..a21d3041507756c4ac7bec2ca9232b9ddb769f08 100644 --- a/net/xrootd/src/xrootd/src/XrdOss/XrdOssApi.hh +++ b/net/xrootd/src/xrootd/src/XrdOss/XrdOssApi.hh @@ -135,6 +135,7 @@ int GenRemotePath(const char *, char *); int Init(XrdSysLogger *, const char *); int IsRemote(const char *path) {return (RPList.Find(path) & XRDEXP_REMOTE) != 0;} +int Lfn2Pfn(const char *Path, char *buff, int blen); int Mkdir(const char *, mode_t mode, int mkpath=0); int Mkpath(const char *, mode_t mode); unsigned long long PathOpts(const char *path) {return RPList.Find(path);} diff --git a/net/xrootd/src/xrootd/src/XrdOss/XrdOssCache.cc b/net/xrootd/src/xrootd/src/XrdOss/XrdOssCache.cc index 00fb862fa5e962c463d6f8374f82ddd4e829f693..b448bcbcc85cc3c41ae7a331654a6b80dff98105 100644 --- a/net/xrootd/src/xrootd/src/XrdOss/XrdOssCache.cc +++ b/net/xrootd/src/xrootd/src/XrdOss/XrdOssCache.cc @@ -429,8 +429,8 @@ int XrdOssCache::Alloc(XrdOssCache::allocInfo &aInfo) else if (!fuzAlloc) {if (curfree > maxfree) {fsp_sel = fsp; maxfree = curfree;}} else {diffree = (!(curfree + maxfree) ? 0.0 - : static_cast<double>(::llabs(maxfree - curfree)) / - static_cast<double>( maxfree + curfree)); + : static_cast<double>(XRDABS(maxfree - curfree)) / + static_cast<double>( maxfree + curfree)); if (diffree > fuzAlloc) {fsp_sel = fsp; maxfree = curfree;} } } while((fsp = fsp->next) != fspend); diff --git a/net/xrootd/src/xrootd/src/XrdOss/XrdOssConfig.cc b/net/xrootd/src/xrootd/src/XrdOss/XrdOssConfig.cc index 453926692935f38d19031446dba22497ec6f35fc..4cb147785cc0afe7388d575147fa3d45ea9fbf68 100644 --- a/net/xrootd/src/xrootd/src/XrdOss/XrdOssConfig.cc +++ b/net/xrootd/src/xrootd/src/XrdOss/XrdOssConfig.cc @@ -63,6 +63,8 @@ extern XrdOssSys *XrdOssSS; extern XrdOucTrace OssTrace; +XrdOucPListAnchor *XrdOssRPList; + /******************************************************************************/ /* E r r o r T e x t */ /******************************************************************************/ @@ -282,6 +284,10 @@ int XrdOssSys::Configure(const char *configfn, XrdSysError &Eroute) // if (!NoGo) Config_Display(Eroute); +// Export the real path list (for frm et. al.) +// + XrdOssRPList = &RPList; + // All done, close the stream and return the return code. // val = (NoGo ? (char *)"failed." : (char *)"completed."); diff --git a/net/xrootd/src/xrootd/src/XrdOss/XrdOssLock.cc b/net/xrootd/src/xrootd/src/XrdOss/XrdOssLock.cc index ee33e0a996bcf68a39b33e577372cf77cccaa710..fac75964fb89c12b9259d9c312c44035108ebff1 100644 --- a/net/xrootd/src/xrootd/src/XrdOss/XrdOssLock.cc +++ b/net/xrootd/src/xrootd/src/XrdOss/XrdOssLock.cc @@ -155,7 +155,7 @@ int XrdOssLock::Serialize(const char *fn, int lkwant) // Now lock the file and return the file descriptor. // - if ((rc = XLock(lkwant))) + if ((rc = XLock(lkfd, lkwant))) {char *mp; close(lkfd); lkfd = -1; if (rc == EWOULDBLOCK) return -EWOULDBLOCK; @@ -270,7 +270,7 @@ int XrdOssLock::UnSerialize(int opts) // Release the lock if we need to. // - if (!(opts & XrdOssREGRADE)) XLock(0); + if (!(opts & XrdOssREGRADE)) XLock(lkfd, 0); else dosleep = 0; // Based on execution option, perform the required action. @@ -279,7 +279,7 @@ int XrdOssLock::UnSerialize(int opts) switch(xopts) {case XrdOssLEAVE: break; case XrdOssRETRY: do {if (dosleep) nanosleep(&naptime, 0); - if (! (rc = XLock(opts)) ) break; + if (! (rc = XLock(lkfd, opts)) ) break; dosleep = 1; } while( rc == EWOULDBLOCK && !(opts & XrdOssNOWAIT) && maxtry--); @@ -324,13 +324,13 @@ int XrdOssLock::Build_LKFN(char *buff, int blen, const char *fn, int ftype) /* X L o c k */ /******************************************************************************/ -int XrdOssLock::XLock(int opts) +int XrdOssLock::XLock(int lkFD, int opts) { FLOCK_t lock_args; // Make sure we have a lock outstanding // - if (lkfd < 0) return XrdOssOK; + if (lkFD < 0) return XrdOssOK; // Establish locking options // @@ -341,7 +341,7 @@ int XrdOssLock::XLock(int opts) // Perform action. // - if (fcntl(lkfd, (opts & XrdOssNOWAIT ? F_SETLK : F_SETLKW), + if (fcntl(lkFD, (opts & XrdOssNOWAIT ? F_SETLK : F_SETLKW), &lock_args)) return errno; return XrdOssOK; } diff --git a/net/xrootd/src/xrootd/src/XrdOss/XrdOssLock.hh b/net/xrootd/src/xrootd/src/XrdOss/XrdOssLock.hh index 67f81ac19b94a6ab9bdb234d60b7f8a0692e61e4..790bb58921433a389efe282fdd0445cb429afba0 100644 --- a/net/xrootd/src/xrootd/src/XrdOss/XrdOssLock.hh +++ b/net/xrootd/src/xrootd/src/XrdOss/XrdOssLock.hh @@ -17,6 +17,7 @@ class XrdOssLock public: int Serialize(const char *, int); +int Serialize(int lkFD, int Opt) {return XLock(lkFD, Opt);} int NoSerialize(const char *, int); int ReSerialize(const char *, const char *); int UnSerialize(int opts=0); @@ -28,7 +29,7 @@ private: int lkfd; // Lock file handle -int XLock(int); +int XLock(int, int); int Build_LKFN(char *, int, const char *, int); }; diff --git a/net/xrootd/src/xrootd/src/XrdOss/XrdOssStat.cc b/net/xrootd/src/xrootd/src/XrdOss/XrdOssStat.cc index 8326e368d41d991ac67d8fd1f2dd4f7ff2882ca2..f3aec8fe7862d53d307189070ed6ac18ab0c4e07 100644 --- a/net/xrootd/src/xrootd/src/XrdOss/XrdOssStat.cc +++ b/net/xrootd/src/xrootd/src/XrdOss/XrdOssStat.cc @@ -83,8 +83,8 @@ int XrdOssSys::Stat(const char *path, struct stat *buff, int opts) // The file may be offline in a mass storage system, check if this is possible // - if (!IsRemote(path)) return -errno; - if (opts & XRDOSS_resonly || !MSSgwCmd) return -ENOMSG; + if (!IsRemote(path) || opts & XRDOSS_resonly) return -errno; + if (!MSSgwCmd) return -ENOMSG; // Generate remote path // diff --git a/net/xrootd/src/xrootd/src/XrdOss/XrdOssTrace.hh b/net/xrootd/src/xrootd/src/XrdOss/XrdOssTrace.hh index db880e79cdd6f3c45e88e9f59d029fdbf68affea..2e254527c7a585bfeee3afcc24e69eec0a493a9a 100644 --- a/net/xrootd/src/xrootd/src/XrdOss/XrdOssTrace.hh +++ b/net/xrootd/src/xrootd/src/XrdOss/XrdOssTrace.hh @@ -38,7 +38,7 @@ #define DEBUG(y) if (QTRACE(Debug)) \ {OssTrace.Beg(epname); cerr <<y; OssTrace.End();} -#define EPNAME(x) const char *epname = x; +#define EPNAME(x) static const char *epname = x; #else diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucEnv.cc b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucEnv.cc index a946b017ba5a772ff4070c7d9887824ccbcf0351..45a18defd19159fed451a8a6ee068baa851f0222 100644 --- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucEnv.cc +++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucEnv.cc @@ -21,7 +21,9 @@ const char *XrdOucEnvCVSID = "$Id$"; /* C o n s t r u c t o r */ /******************************************************************************/ -XrdOucEnv::XrdOucEnv(const char *vardata, int varlen) : env_Hash(8,13) +XrdOucEnv::XrdOucEnv(const char *vardata, int varlen, + const XrdSecEntity *secent) + : env_Hash(8,13), secEntity(secent) { char *vdp, varsave, *varname, *varvalu; diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucEnv.hh b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucEnv.hh index fff132c79880705f2585c4760fada333b3371fa4..17f487261adffbe4c230d6b7c2718c2cf448619a 100644 --- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucEnv.hh +++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucEnv.hh @@ -18,6 +18,8 @@ #endif #include "XrdOuc/XrdOucHash.hh" +class XrdSecEntity; + class XrdOucEnv { public: @@ -61,16 +63,22 @@ static int Export(const char *Var, int Val); // char *Delimit(char *value); +// secEnv() returns the security environment; which may be a null pointer. +// +inline const XrdSecEntity *secEnv() {return secEntity;} + // Use the constructor to define the initial variable settings. The passed // string is duplicated and the copy can be retrieved using Env(). // - XrdOucEnv(const char *vardata=0, int vardlen=0); + XrdOucEnv(const char *vardata=0, int vardlen=0, + const XrdSecEntity *secent=0); ~XrdOucEnv() {if (global_env) free((void *)global_env);} private: XrdOucHash<char> env_Hash; +const XrdSecEntity *secEntity; char *global_env; int global_len; }; diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucErrInfo.hh b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucErrInfo.hh index 0a15e2fd3317927b23cfc9cd6a51e229ddd7844a..9f7c725a9691e3b581cf56a2299f55a1472dc4dd 100644 --- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucErrInfo.hh +++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucErrInfo.hh @@ -120,14 +120,19 @@ public: // Done() is invoked when the requested operation completes. Arguments are: // Result - the original function's result (may be changed). -// eInfo - Associated error information. The callback function must -// manually delete this object when it is through! While icky -// this allows callback functions to be asynchronous. +// eInfo - Associated error information. The eInfo object may not be +// modified until it's own callback Done() method is called, if +// supplied. If the callback function in eInfo is zero, then the +// eInfo object is deleted by the invoked callback. Otherwise, +// that method must be invoked by this callback function after +// the actual callback message is sent. This allows the callback +// requestor to do post-processing and be asynchronous. +// // virtual void Done(int &Result, //I/O: Function result XrdOucErrInfo *eInfo)=0; // In: Error Info -// Same() is invoked to determine if two argtuments refer to the same user. +// Same() is invoked to determine if two arguments refer to the same user. // True is returned if so, false, otherwise. // virtual int Same(unsigned long long arg1, unsigned long long arg2)=0; diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucNSWalk.cc b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucNSWalk.cc index f0c257e53baeaa866bccbea2a219a28b0e9fb749..79f0111e378627dc0f6fd721f7653d8f2d7c4b34 100644 --- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucNSWalk.cc +++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucNSWalk.cc @@ -27,9 +27,9 @@ const char *XrdOucNSWalkCVSID = "$Id$"; /******************************************************************************/ XrdOucNSWalk::XrdOucNSWalk(XrdSysError *erp, const char *dpath, - const char *lkfn, int opts) + const char *lkfn, int opts, + XrdOucTList *xlist) { - // Set the required fields // eDest = erp; @@ -40,6 +40,15 @@ XrdOucNSWalk::XrdOucNSWalk(XrdSysError *erp, const char *dpath, DPfd = LKfd = -1; errOK= opts & skpErrs; DEnts= 0; + edCB = 0; + +// Copy the exclude list if one exists +// + if (!xlist) XList = 0; + else while(xlist) + {XList = new XrdOucTList(xlist->text,xlist->ival,XList); + xlist = xlist->next; + } } /******************************************************************************/ @@ -53,6 +62,8 @@ XrdOucNSWalk::~XrdOucNSWalk() if (LKFn) free(LKFn); while((tP = DList)) {DList = tP->next; delete tP;} + + while((tP = XList)) {XList = tP->next; delete tP;} } /******************************************************************************/ @@ -74,6 +85,7 @@ XrdOucNSWalk::NSEnt *XrdOucNSWalk::Index(int &rc, const char **dPath) rc = Build(); if (LKfd >= 0) close(LKfd); if (DEnts || (rc && !errOK)) break; + if (edCB && isEmpty) edCB->isEmpty(&dStat, DPath, LKFn); } // Return the result @@ -128,6 +140,11 @@ int XrdOucNSWalk::Build() } theEnt; struct dirent *dp; int rc = 0, getLI = Opts & retLink; + int nEnt = 0, xLKF = 0, chkED = (edCB != 0) && (LKFn != 0); + +// Initialize the empty flag prior to doing anything else +// + isEmpty = 0; // If we can optimize with a directory file descriptor, get one // @@ -149,17 +166,19 @@ int XrdOucNSWalk::Build() errno = 0; while((dp = readdir(theEnt.D))) {if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue; - strcpy(File, dp->d_name); + strcpy(File, dp->d_name); nEnt++; if (!theEnt.P) theEnt.P = new NSEnt(); rc = getStat(theEnt.P, getLI); switch(theEnt.P->Type) {case NSEnt::isDir: - if (Opts & Recurse && (!getLI || !isSymlink())) + if (Opts & Recurse && (!getLI || !isSymlink()) + && (!XList || !inXList(File))) DList = new XrdOucTList(DPath, 0, DList); if (!(Opts & retDir)) continue; break; case NSEnt::isFile: - if (!(Opts & retFile)) continue; + if ((chkED && !xLKF && (xLKF = !strcmp(File, LKFn))) + || !(Opts & retFile)) continue; break; case NSEnt::isLink: if ((rc = getLink(theEnt.P))) @@ -184,6 +203,13 @@ int XrdOucNSWalk::Build() *File = '\0'; if ((rc = errno) && !errOK) {eDest->Emsg("Build", rc, "reading directory", DPath); return rc;} + +// Check if we need to do a callback for an empty directory +// + if (edCB && xLKF == nEnt && !DEnts) + {if (!fstat(DPfd, &dStat)) isEmpty = 1; + else eDest->Emsg("Build", errno, "stating directory", DPath); + } return 0; } @@ -247,6 +273,27 @@ do{rc = doLstat ? lstat(DPath, &(eP->Stat)) : stat(DPath, &(eP->Stat)); return 0; } +/******************************************************************************/ +/* i n X L i s t */ +/******************************************************************************/ + +int XrdOucNSWalk::inXList(const char *dName) +{ + XrdOucTList *xTP = XList, *pTP = 0; + +// Search for the directory entry +// + while(xTP && strcmp(DPath, xTP->text)) {pTP = xTP; xTP = xTP->next;} + +// If not found return false. Otherwise, delete the entry and return true. +// + if (!xTP) return 0; + if (pTP) pTP->next = xTP->next; + else XList = xTP->next; + delete xTP; + return 1; +} + /******************************************************************************/ /* i s S y m l i n k */ /******************************************************************************/ diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucNSWalk.hh b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucNSWalk.hh index 33ca9370433f79f61bf7d60efc0e84ff78769c50..6d930ca7490a9e402fee3b1ee707dad58394a52e 100644 --- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucNSWalk.hh +++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucNSWalk.hh @@ -59,6 +59,23 @@ Etype Type; // One of the above. If isLink then Link is invalid! // NSEnt *Index(int &rc, const char **dPath=0); +// The CallBack class is used to intercept empty directories. When set by a +// call to setCallBack(); should an empty directory (i.e., one with no entries +// or only with a lock file) in encountered a call is made to to the isEmpty() +// method. If lkFn is zero, the directory is empty; otherwise, lkFn is the name +// of the singleton lock file. To unset the callback use setCallBack(0); +// +class CallBack +{public: +virtual +void isEmpty(struct stat *dStat, const char *dPath, const char *lkFn)=0; + + CallBack() {} +virtual ~CallBack() {} +}; + +void setCallBack(CallBack *cbP=0) {edCB = cbP;} + // The following are processing options passed to the constructor // static const int retDir = 0x0001; // Return directories (implies retStat) @@ -76,13 +93,15 @@ static const int skpErrs= 0x8000; // Skip any entry causing an error XrdOucNSWalk(XrdSysError *erp, // Error msg object. If 0->silent const char *dname, // Initial directory path const char *LKfn=0, // Lock file name (see note below) - int opts=retAll); // Options (see above) + int opts=retAll, // Options (see above) + XrdOucTList *xP=0); // 1st Level dir exclude list ~XrdOucNSWalk(); // Note: When Lkfn is supplied and it exists in a directory about to be indexed // then the file is opened in r/w mode and an exclusive lock is obtained. // If either fails, the the directory is not indexed and Index() will -// return null pointer with rc != 0. +// return null pointer with rc != 0. Note that the lkfn is not returned +// as a directory entry if an empty directory call back has been set. private: void addEnt(XrdOucNSWalk::NSEnt *eP); @@ -90,13 +109,17 @@ int Build(); int getLink(XrdOucNSWalk::NSEnt *eP); int getStat(XrdOucNSWalk::NSEnt *eP, int doLstat=0); int getStat(); +int inXList(const char *dName); int isSymlink(); int LockFile(); void setPath(char *newpath); XrdSysError *eDest; XrdOucTList *DList; +XrdOucTList *XList; struct NSEnt *DEnts; +struct stat dStat; +CallBack *edCB; char DPath[1032]; char *File; char *LKFn; @@ -104,5 +127,6 @@ int LKfd; int DPfd; int Opts; int errOK; +int isEmpty; }; #endif diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucPList.hh b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucPList.hh index c217c7952308459227bf4090e9c7415105e1f29f..351707ecced3b3b22dbf319baa2ccf11e3a05bf2 100644 --- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucPList.hh +++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucPList.hh @@ -22,18 +22,16 @@ public: inline unsigned long long Flag() {return flags;} inline XrdOucPList *Next() {return next;} inline char *Path() {return path;} +inline int Plen() {return pathlen;} inline int PathOK(const char *pd, const int pl) {return pl >= pathlen && !strncmp(pd, path, pathlen);} inline void Set(unsigned long long fval) {flags = fval;} - XrdOucPList(const char *pathdata="", unsigned long long fvals=0) - {next = 0; - pathlen = strlen(pathdata); - path = strdup(pathdata); - flags = fvals;} - + XrdOucPList(const char *pd="", unsigned long long fv=0) + : flags(fv), next(0), path(strdup(pd)), + pathlen(strlen(pd)), reserved(0) {} ~XrdOucPList() {if (path) free(path);} @@ -41,10 +39,11 @@ friend class XrdOucPListAnchor; private: -XrdOucPList *next; unsigned long long flags; +XrdOucPList *next; char *path; int pathlen; +int reserved; }; class XrdOucPListAnchor : public XrdOucPList diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucProg.cc b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucProg.cc index 5f76e6fd27fa2f468d61f3a6e463ffb9db406f6c..85fb71d9b19de3621d9e351e8d4b18d7cba165e3 100644 --- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucProg.cc +++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucProg.cc @@ -115,7 +115,7 @@ int XrdOucProg::Run(XrdOucStream *Sp, const char *arg1, const char *arg2, // Execute the command // - if (Sp->Exec(myArgs, 1)) + if (Sp->Exec(myArgs, 1, theEFD)) {rc = Sp->LastError(); if (eDest) eDest->Emsg("Run", rc, "execute", Arg[0]); return -rc; @@ -228,10 +228,11 @@ int XrdOucProg::Start() // Create a stream for this command (it is an eror if we are already started) // if (myStream) return EBUSY; - if (!(myStream = new XrdOucStream())) return ENOMEM; + if (!(myStream = new XrdOucStream(eDest))) return ENOMEM; // Execute the command and let it linger // + theEFD = 0; return Run(myStream); } diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucProg.hh b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucProg.hh index cd1640e950e4219c8ceae8796de373a2d88f5091..feb10765a165c27c53df1b14b8a2b4a1f72a73f9 100644 --- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucProg.hh +++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucProg.hh @@ -27,7 +27,7 @@ public: // XrdOucProg(XrdSysError *errobj=0) {eDest = errobj; myStream = 0; - ArgBuff = Arg[0] = 0; numArgs = 0; + ArgBuff = Arg[0] = 0; numArgs = 0; theEFD = -1; } ~XrdOucProg(); @@ -86,5 +86,6 @@ private: char *Arg[64]; int numArgs; int lenArgs; + int theEFD; }; #endif diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucStream.cc b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucStream.cc index b57e76065106edc07a1e474122966e4542c6df12..fa7c81d185b61cc7f8a26e27c080ac173685ee2f 100644 --- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucStream.cc +++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucStream.cc @@ -231,7 +231,7 @@ void XrdOucStream::Echo() /* E x e c */ /******************************************************************************/ -int XrdOucStream::Exec(const char *theCmd, int inrd) +int XrdOucStream::Exec(const char *theCmd, int inrd, int efd) { int j; char *cmd, *origcmd, *parm[MaxARGC]; @@ -254,15 +254,14 @@ int XrdOucStream::Exec(const char *theCmd, int inrd) // Continue with normal processing // - j = Exec(parm, inrd); + j = Exec(parm, inrd, efd); free(origcmd); return j; } -int XrdOucStream::Exec(char **parm, int inrd) +int XrdOucStream::Exec(char **parm, int inrd, int efd) { - int fildes[2], Child_in = -1, Child_out = -1; - int Child_log = (Eroute ? Eroute->logger()->xlogFD() : -1); + int fildes[2], Child_in = -1, Child_out = -1, Child_log = -1; // Create a pipe. Minimize file descriptor leaks. // @@ -284,12 +283,18 @@ int XrdOucStream::Exec(char **parm, int inrd) } } else {Child_out = FD; Child_in = FE;} + // Handle the standard error file descriptor + // + if (!efd) Child_log = (Eroute ? dup(Eroute->logger()->originalFD()) : -1); + else if (efd > 0) Child_log = efd; + // Fork a process first so we can pick up the next request. We also // set the process group in case the chi;d hasn't been able to do so. // if ((child = fork())) { close(Child_out); if (inrd) close(Child_in ); + if (!efd && Child_log >= 0) close(Child_log); if (child < 0) return Err(Exec, errno, "fork request process for", parm[0]); setpgid(child, child); @@ -580,7 +585,7 @@ int XrdOucStream::GetRest(char *theBuff, int Blen, int lowcase) while ((tp = GetWord(lowcase))) {tlen = strlen(tp); if (tlen+1 >= Blen) return 0; - if (myBuff != theBuff) *myBuff++ = ' '; + if (myBuff != theBuff) {*myBuff++ = ' '; Blen--;} strcpy(myBuff, tp); Blen -= tlen; myBuff += tlen; } diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucStream.hh b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucStream.hh index 65d6b7b3f8c20d96217e3075f9850d9edd345301..46545c0c3037d801dca0ba3eb41bbc405905aa19 100644 --- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucStream.hh +++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucStream.hh @@ -71,10 +71,14 @@ void Echo(); // will return the standard output of the executed command. If inrd=1 then // standardin is redirected so that subqseuent Put() calls write to the // process via standard in. When inrd=-1 then the current attached FD's are -// used to redirect STDIN and STDOUT of the child process. -// -int Exec(const char *, int inrd=0); -int Exec( char **, int inrd=0); +// used to redirect STDIN and STDOUT of the child process. Standard error +// is handled as determined by the efd argument: +// efd < 0 -> The current stderr file decriptor is unchanged. +// efd = 0 -> The stderr file descriptor is set to the original logging FD +// efd > 0 -> The stderr file descriptor is set to the value of efd. +// +int Exec(const char *, int inrd=0, int efd=0); +int Exec( char **, int inrd=0, int efd=0); // Get the file descriptor number associated with a stream // diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucString.hh b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucString.hh index 79631d361dba74207a8f76014ca7cb423ce69ed5..5a57713291433014424117919f7c83a028d33aa4 100644 --- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucString.hh +++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucString.hh @@ -364,7 +364,7 @@ ostream &operator<< (ostream &, const XrdOucString s); XrdOucString const operator+(const char *s1, const XrdOucString s2); XrdOucString const operator+(const char c, const XrdOucString s); -XrdOucString const operator+(const int i, const XrdOucString s); +XrdOucString const operator+(const int i, const XrdOucString s); #endif diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucTList.hh b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucTList.hh index 5d412d505b2b77bbdc6d66a6d71ba2a9185634e2..a353ba3d4feadb743c2f05bd77d15ce5a24a3c94 100644 --- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucTList.hh +++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucTList.hh @@ -22,10 +22,32 @@ public: XrdOucTList *next; char *text; +union +{ +long long dval; +int ival[2]; +short sval[4]; +char cval[8]; int val; +}; + + XrdOucTList(const char *tval, long long *dv,XrdOucTList *np=0) + {next=np; text = (tval ? strdup(tval) : 0); dval=*dv;} XrdOucTList(const char *tval=0, int num=0, XrdOucTList *np=0) - {text = (tval ? strdup(tval) : 0); val=num; next=np;} + {next=np; text = (tval ? strdup(tval) : 0); val=num;} + + XrdOucTList(const char *tval, int iv[2], XrdOucTList *np=0) + {next=np; text = (tval ? strdup(tval) : 0); + memcpy(sval, iv, sizeof(ival));} + + XrdOucTList(const char *tval, short sv[4], XrdOucTList *np=0) + {next=np; text = (tval ? strdup(tval) : 0); + memcpy(sval, sv, sizeof(sval));} + + XrdOucTList(const char *tval, char cv[8], XrdOucTList *np=0) + {text = (tval ? strdup(tval) : 0); next=np; + memcpy(cval, cv, sizeof(cval));} ~XrdOucTList() {if (text) free(text);} }; diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucUtils.cc b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucUtils.cc index ed84253d1b39a4f3b41d95c743d223f097dcd40b..915557347e4f9e8de843a78b8694ae79016d6d34 100644 --- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucUtils.cc +++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucUtils.cc @@ -14,6 +14,7 @@ const char *XrdOucUtilsCVSID = "$Id$"; #include <ctype.h> #include <errno.h> +#include <stdio.h> #ifdef WIN32 #include <direct.h> @@ -143,6 +144,33 @@ int XrdOucUtils::doIf(XrdSysError *eDest, XrdOucStream &Config, return (val != 0); } +/******************************************************************************/ +/* f m t B y t e s */ +/******************************************************************************/ + +int XrdOucUtils::fmtBytes(long long val, char *buff, int bsz) +{ + static const long long Kval = 1024LL; + static const long long Mval = 1024LL*1024LL; + static const long long Gval = 1024LL*1024LL*1024LL; + static const long long Tval = 1024LL*1024LL*1024LL*1024LL; + char sName = ' '; + int resid; + +// Get correct scaling +// + if (val < 1024) return snprintf(buff, bsz, "%lld", val); + if (val < Mval) {val = val*10/Kval; sName = 'K';} + else if (val < Gval) {val = val*10/Mval; sName = 'M';} + else if (val < Tval) {val = val*10/Gval; sName = 'G';} + else {val = val*10/Tval; sName = 'T';} + resid = val%10LL; val = val/10LL; + +// Format it +// + return snprintf(buff, bsz, "%lld.%d%c", val, resid, sName); +} + /******************************************************************************/ /* g e n P a t h */ /******************************************************************************/ diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucUtils.hh b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucUtils.hh index f72269ee47fb686cc6fbac967e2b781f9990b6d4..8cbaba4b3594a5d1d4c85445f952528f81bef481 100644 --- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOucUtils.hh +++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOucUtils.hh @@ -30,6 +30,8 @@ static int doIf(XrdSysError *eDest, XrdOucStream &Config, const char *what, const char *hname, const char *nname, const char *pname); +static int fmtBytes(long long val, char *buff, int bsz); + static char *genPath(const char *path, const char *inst, const char *psfx=0); static int genPath(char *buff, int blen, const char *path, const char *psfx=0); diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOuca2x.cc b/net/xrootd/src/xrootd/src/XrdOuc/XrdOuca2x.cc index 641aac8fab9f85824176736b0e413d989456e03c..6e2d8488704184e517660a1aba411810a86f449a 100644 --- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOuca2x.cc +++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOuca2x.cc @@ -51,7 +51,7 @@ int XrdOuca2x::a2i(XrdSysError &Eroute, const char *emsg, const char *item, /* a 2 l l */ /******************************************************************************/ -long long XrdOuca2x::a2ll(XrdSysError &Eroute, const char *emsg, const char *item, +int XrdOuca2x::a2ll(XrdSysError &Eroute, const char *emsg, const char *item, long long *val, long long minv, long long maxv) { char *eP; @@ -117,11 +117,55 @@ int XrdOuca2x::a2fm(XrdSysError &Eroute, const char *emsg, const char *item, return 0; } +/******************************************************************************/ +/* a 2 s p */ +/******************************************************************************/ + +int XrdOuca2x::a2sp(XrdSysError &Eroute, const char *emsg, const char *item, + long long *val, long long minv, long long maxv) +{ + char *pp, buff[120]; + int i; + + if (!item || !*item) + {Eroute.Emsg("a2x", emsg, "value not specified"); return -1;} + + i = strlen(item); + if (item[i-1] != '%') return a2sz(Eroute, emsg, item, val, minv, maxv); + + errno = 0; + *val = strtoll(item, &pp, 10); + + if (errno || *pp != '%') + {Eroute.Emsg("a2x", emsg, item, "is not a number"); + return -1; + } + + if (maxv < 0) maxv = 100; + + if (*val > maxv) + {sprintf(buff, "may not be greater than %lld%%", maxv); + Eroute.Emsg("a2x", emsg, item, buff); + return -1; + } + + if (minv < 0) minv = 0; + + if (*val > maxv) + {sprintf(buff, "may not be less than %lld%%", minv); + Eroute.Emsg("a2x", emsg, item, buff); + return -1; + } + + *val = -*val; + return 0; +} + /******************************************************************************/ /* a 2 s z */ /******************************************************************************/ -long long XrdOuca2x::a2sz(XrdSysError &Eroute, const char *emsg, const char *item, +int XrdOuca2x::a2sz(XrdSysError &Eroute, const char *emsg, const char *item, long long *val, long long minv, long long maxv) { long long qmult; char *eP, *fP = (char *)item + strlen(item) - 1; @@ -224,8 +268,8 @@ int XrdOuca2x::Emsg(XrdSysError &Eroute, const char *etxt1, const char *item, return -1; } -long long XrdOuca2x::Emsg(XrdSysError &Eroute, const char *etxt1, const char *item, - const char *etxt2, long long val) +int XrdOuca2x::Emsg(XrdSysError &Eroute, const char *etxt1, const char *item, + const char *etxt2, long long val) {char buff[256]; sprintf(buff, etxt2, val); Eroute.Emsg("a2x", etxt1, item, buff); diff --git a/net/xrootd/src/xrootd/src/XrdOuc/XrdOuca2x.hh b/net/xrootd/src/xrootd/src/XrdOuc/XrdOuca2x.hh index d8f9b581890433fcb64cf319870b1c95161f760b..cc545dd0383bae5e35ba1b512e88fc3ab4356ee8 100644 --- a/net/xrootd/src/xrootd/src/XrdOuc/XrdOuca2x.hh +++ b/net/xrootd/src/xrootd/src/XrdOuc/XrdOuca2x.hh @@ -20,19 +20,20 @@ class XrdOuca2x { public: -static int a2i( XrdSysError &, const char *emsg, const char *item, int *val, int minv=-1, int maxv=-1); -static long long a2ll(XrdSysError &, const char *emsg, const char *item, long long *val, long long minv=-1, long long maxv=-1); -static int a2fm(XrdSysError &, const char *emsg, const char *item, int *val, int minv); -static int a2fm(XrdSysError &, const char *emsg, const char *item, int *val, int minv, int maxv); -static long long a2sz(XrdSysError &, const char *emsg, const char *item, long long *val, long long minv=-1, long long maxv=-1); -static int a2tm(XrdSysError &, const char *emsg, const char *item, int *val, int minv=-1, int maxv=-1); -static int a2vp(XrdSysError &, const char *emsg, const char *item, int *val, int minv=-1, int maxv=-1); +static int a2i( XrdSysError &, const char *emsg, const char *item, int *val, int minv=-1, int maxv=-1); +static int a2ll(XrdSysError &, const char *emsg, const char *item, long long *val, long long minv=-1, long long maxv=-1); +static int a2fm(XrdSysError &, const char *emsg, const char *item, int *val, int minv); +static int a2fm(XrdSysError &, const char *emsg, const char *item, int *val, int minv, int maxv); +static int a2sp(XrdSysError &, const char *emsg, const char *item, long long *val, long long minv=-1, long long maxv=-1); +static int a2sz(XrdSysError &, const char *emsg, const char *item, long long *val, long long minv=-1, long long maxv=-1); +static int a2tm(XrdSysError &, const char *emsg, const char *item, int *val, int minv=-1, int maxv=-1); +static int a2vp(XrdSysError &, const char *emsg, const char *item, int *val, int minv=-1, int maxv=-1); private: -static int Emsg(XrdSysError &Eroute, const char *etxt1, const char *item, - const char *etxt2, int val); -static long long Emsg(XrdSysError &Eroute, const char *etxt1, const char *item, - const char *etxt2, long long val); +static int Emsg(XrdSysError &Eroute, const char *etxt1, const char *item, + const char *etxt2, int val); +static int Emsg(XrdSysError &Eroute, const char *etxt1, const char *item, + const char *etxt2, long long val); }; #endif diff --git a/net/xrootd/src/xrootd/src/XrdPosix/GNUmakefile b/net/xrootd/src/xrootd/src/XrdPosix/GNUmakefile index 0d22f9dce02a4d61994e693b1d78832d5fd9cabf..7574982095db81dad8eb4fa0d1da80c5edc20d35 100644 --- a/net/xrootd/src/xrootd/src/XrdPosix/GNUmakefile +++ b/net/xrootd/src/xrootd/src/XrdPosix/GNUmakefile @@ -1,4 +1,4 @@ -# $Id: GNUmakefile,v 1.22 2009/03/17 20:54:29 abh Exp $ +# $Id: GNUmakefile,v 1.25 2010/01/06 09:33:41 abh Exp $ #------------------------------------------------------------------------------# # C o m m o n V a r i a b l e s # @@ -122,7 +122,7 @@ $(OBJDIR)/XrdPosixPreload32.o: XrdPosixPreload32.cc XrdPosixLinkage.hh \ $(OBJDIR)/XrdPosixXrootd.o: XrdPosixXrootd.cc XrdPosixXrootd.hh \ XrdClient.hh XrdPosixExtern.hh \ - XrdSysHeaders.hh \ + XrdSysHeaders.hh XrdPosixCallBack.hh \ XrdPosixOsDep.hh @echo Compiling XrdPosixXrootd.cc $(ECHO)$(CC) -c $(CFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdPosixXrootd.o XrdPosixXrootd.cc diff --git a/net/xrootd/src/xrootd/src/XrdPosix/XrdPosix.cc b/net/xrootd/src/xrootd/src/XrdPosix/XrdPosix.cc index 16ce7539bd4ef2c0b7a2390cb9cf597d922244a6..68151fe4d4db88619940bd30d5b044a43e675c38 100644 --- a/net/xrootd/src/xrootd/src/XrdPosix/XrdPosix.cc +++ b/net/xrootd/src/xrootd/src/XrdPosix/XrdPosix.cc @@ -438,7 +438,8 @@ FILE *XrdPosix_Fopen(const char *path, const char *mode) // Now open the file // - if ((fd = Xroot.Open(myPath, omode, 0, 1)) < 0) return 0; + if ((fd = Xroot.Open(myPath, omode | XrdPosixXrootd::isStream , 0)) < 0) + return 0; // First obtain a free stream // diff --git a/net/xrootd/src/xrootd/src/XrdPosix/XrdPosixCallBack.hh b/net/xrootd/src/xrootd/src/XrdPosix/XrdPosixCallBack.hh new file mode 100644 index 0000000000000000000000000000000000000000..c62be482f80f2a18affb11c8b68d44224748946c --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdPosix/XrdPosixCallBack.hh @@ -0,0 +1,41 @@ +#ifndef __POSIX_CALLBACK_HH__ +#define __POSIX_CALLBACK_HH__ +/******************************************************************************/ +/* */ +/* X r d P o s i x C a l l B a c k . h h */ +/* */ +/* (c) 2010 by the Board of Trustees of the Leland Stanford, Jr., University */ +/* All Rights Reserved */ +/* Produced by Andrew Hanushevsky for Stanford University under contract */ +/* DE-AC02-76-SFO0515 with the Department of Energy */ +/******************************************************************************/ + +// $Id$ + +// This abstract class defines the callback interface for file open() calls. +// It is passed when using the XrdPosixXrootd::Open() call. When passed, the +// open request will be done in the background. When a callback object is +// supplied, Open() will *always* return -1. However, if started successfully, +// Open() will return -1 with errno set to EINPROGRESS. Otherwise, errno will +// contain the reason the Open() request immediately failed. Upon completion, +// the callback's Compete() method is invoked. The Result parameter will either +// be a non-negative file descriptor or -errno indicating that the Open() +// failed. Note that the caller is responsible for deleting the callback object +// after it has been invoked. Note that callbacks will be executed in a +// separate thread unless open() is called with O_SYNC or maxThreads is zero. +// WARNING: If O_SYNC or maxThreads is zero, then the callback must *not* +// issue any filesystem calls using the supplied file descriptor. +// Ignoring this will produce undetermined results including possible +// deadlock. Synchrnous callbacks are only meant to support private +// thread management. + +class XrdPosixCallBack +{ +public: + +virtual void Complete(int Result) = 0; + + XrdPosixCallBack() {} +virtual ~XrdPosixCallBack() {} +}; +#endif diff --git a/net/xrootd/src/xrootd/src/XrdPosix/XrdPosixXrootd.cc b/net/xrootd/src/xrootd/src/XrdPosix/XrdPosixXrootd.cc index f0d131e0e576bc7353f4d010d8b6406c219fe6e5..5ef5cd5dd2de7911dcae209058ca49f32468f617 100644 --- a/net/xrootd/src/xrootd/src/XrdPosix/XrdPosixXrootd.cc +++ b/net/xrootd/src/xrootd/src/XrdPosix/XrdPosixXrootd.cc @@ -2,7 +2,7 @@ /* */ /* X r d P o s i x X r o o t d . c c */ /* */ -/* (c) 2005 by the Board of Trustees of the Leland Stanford, Jr., University */ +/* (c) 2010 by the Board of Trustees of the Leland Stanford, Jr., University */ /* All Rights Reserved */ /* Produced by Andrew Hanushevsky for Stanford University under contract */ /* DE-AC02-76-SFO0515 with the Department of Energy */ @@ -20,13 +20,15 @@ const char *XrdPosixXrootdCVSID = "$Id$"; #include <sys/uio.h> #include "XrdClient/XrdClient.hh" -#include "XrdClient/XrdClientEnv.hh" #include "XrdClient/XrdClientAdmin.hh" +#include "XrdClient/XrdClientCallback.hh" +#include "XrdClient/XrdClientEnv.hh" #include "XrdClient/XrdClientUrlInfo.hh" #include "XrdClient/XrdClientVector.hh" #include "XrdSys/XrdSysHeaders.hh" #include "XrdSys/XrdSysPlatform.hh" #include "XrdOuc/XrdOucString.hh" +#include "XrdPosix/XrdPosixCallBack.hh" #include "XrdPosix/XrdPosixLinkage.hh" #include "XrdPosix/XrdPosixXrootd.hh" @@ -99,7 +101,7 @@ private: /* X r d P o s i x F i l e C l a s s */ /******************************************************************************/ -class XrdPosixFile +class XrdPosixFile : public XrdClientCallback { public: @@ -123,11 +125,20 @@ long long setOffset(long long offs) void Lock() {myMutex.Lock();} void UnLock() {myMutex.UnLock();} -int FD; +void OpenComplete(XrdClientAbs *clientP, void *cbArg, bool res) + {if (cbDone) return; + if (XrdPosixXrootd::OpenCB(res, this, cbArg)) cbDone=1; + else delete this; + } XrdClientStatInfo stat; +XrdPosixCallBack *theCB; +XrdPosixFile *Next; +int FD; +int cbResult; - XrdPosixFile(int fd, const char *path); + XrdPosixFile(int fd, const char *path, + XrdPosixCallBack *cbP=0, int cbsync=0); ~XrdPosixFile(); private: @@ -135,6 +146,7 @@ private: XrdSysMutex myMutex; long long currOffset; int doClose; +int cbDone; }; @@ -161,10 +173,18 @@ int XrdPosixXrootd::highDir = -1; int XrdPosixXrootd::lastDir = -1; int XrdPosixXrootd::devNull = -1; int XrdPosixXrootd::pllOpen = 0; +int XrdPosixXrootd::maxThreads= 0; int XrdPosixXrootd::Debug = -2; XrdPosixXrootd XrdPosixXrootd; +/******************************************************************************/ +/* T h r e a d I n t e r f a c e s */ +/******************************************************************************/ + +void *XrdPosixXrootdCB(void *carg) + {XrdPosixXrootd::OpenCB(); return (void *)0;} + /******************************************************************************/ /* X r d P o s i x A d m i n N e w C l a s s */ /******************************************************************************/ @@ -294,15 +314,23 @@ dirent64 *XrdPosixDir::nextEntry(dirent64 *dp) /* C o n s t r u c t o r */ /******************************************************************************/ -XrdPosixFile::XrdPosixFile(int fd, const char *path) - : FD(fd), +XrdPosixFile::XrdPosixFile(int fd, const char *path, XrdPosixCallBack *cbP, + int isSync) + : theCB(cbP), + Next(0), + FD(fd), + cbResult(0), currOffset(0), - doClose(0) + doClose(0), + cbDone(0) { + static int OneVal = 1; + XrdClientCallback *myCB = (cbP ? (XrdClientCallback *)this : 0); + void *cbArg = (isSync ? (void *)&OneVal : 0); // Allocate a new client object // - if (!(XClient = new XrdClient(path))) stat.size = 0; + if (!(XClient = new XrdClient(path, myCB, cbArg))) stat.size = 0; } /******************************************************************************/ @@ -327,7 +355,7 @@ XrdPosixFile::~XrdPosixFile() /* C o n s t r u c t o r */ /******************************************************************************/ -XrdPosixXrootd::XrdPosixXrootd(int fdnum, int dirnum) +XrdPosixXrootd::XrdPosixXrootd(int fdnum, int dirnum, int thrnum) { extern XrdPosixLinkage Xunix; static int initDone = 0; @@ -348,6 +376,7 @@ XrdPosixXrootd::XrdPosixXrootd(int fdnum, int dirnum) // before any XrdClient library routines are called. // initEnv(); + maxThreads = thrnum; // Compute size of table // @@ -477,6 +506,38 @@ int XrdPosixXrootd::Closedir(DIR *dirp) return 0; } +/******************************************************************************/ +/* e n d P o i n t */ +/******************************************************************************/ + +int XrdPosixXrootd::endPoint(int FD, char *Buff, int Blen) +{ + XrdPosixFile *fp; + XrdClientUrlInfo fURL; + +// Find the file object +// + if (!(fp = findFP(FD))) return 0; + +// Obtain the current url from the file +// + fURL = fp->XClient->GetCurrentUrl(); + fp->UnLock(); + +// Make sure url is valid +// + if (!fURL.IsValid()) return -ENOTCONN; + +// Format host and port number, check if result is too long +// + if (snprintf(Buff, Blen, "%s:%d", fURL.Host.c_str(), fURL.Port) >= Blen) + return -ENAMETOOLONG; + +// All done +// + return fURL.Port; +} + /******************************************************************************/ /* F s t a t */ /******************************************************************************/ @@ -639,10 +700,11 @@ int XrdPosixXrootd::Mkdir(const char *path, mode_t mode) /* O p e n */ /******************************************************************************/ -int XrdPosixXrootd::Open(const char *path, int oflags, mode_t mode, int Stream) +int XrdPosixXrootd::Open(const char *path, int oflags, mode_t mode, + XrdPosixCallBack *cbP) { XrdPosixFile *fp; - int retc = 0, fd, XOflags, XMode; + int isSync, retc = 0, fd, XOflags, XMode; // Translate option bits to the appropraite values. Always // make directory path for new file. @@ -658,12 +720,13 @@ int XrdPosixXrootd::Open(const char *path, int oflags, mode_t mode, int Stream) // Obtain a new filedscriptor from the system. Use the fd to track the file. // if ((fd = dup(devNull)) < 0) return -1; - if (Stream && fd > 255) {close(fd); errno = EMFILE; return -1;} + if (oflags & isStream && fd > 255) {close(fd); errno = EMFILE; return -1;} + isSync = (maxThreads == 0) || (oflags & O_SYNC); // Allocate the new file object // myMutex.Lock(); - if (fd > lastFD || !(fp = new XrdPosixFile(fd, path))) + if (fd > lastFD || !(fp = new XrdPosixFile(fd, path, cbP, isSync))) {errno = EMFILE; myMutex.UnLock(); return -1;} myFiles[fd] = fp; if (fd > highFD) highFD = fd; @@ -675,8 +738,8 @@ int XrdPosixXrootd::Open(const char *path, int oflags, mode_t mode, int Stream) // Open the file // - if (!fp->XClient->Open(XMode, XOflags, pllOpen) - || (fp->XClient->LastServerResp()->status) != kXR_ok) + if (!fp->XClient->Open(XMode, XOflags, (cbP ? 1 : pllOpen)) + || (!cbP && fp->XClient->LastServerResp()->status) != kXR_ok) {retc = Fault(fp, 0); myMutex.Lock(); myFiles[fd] = 0; @@ -686,6 +749,13 @@ int XrdPosixXrootd::Open(const char *path, int oflags, mode_t mode, int Stream) return -1; } +// If this is a callback open then just return EINPROGRESS +// + if (cbP) + {errno = EINPROGRESS; + return -1; + } + // Get the file size // fp->isOpen(); @@ -696,6 +766,95 @@ int XrdPosixXrootd::Open(const char *path, int oflags, mode_t mode, int Stream) return fd; } +/******************************************************************************/ +/* O p e n C B */ +/******************************************************************************/ + +int XrdPosixXrootd::OpenCB(int res, XrdPosixFile *fp, void *cbArg) +{ + int retc, ec; + +// Diagnose any error +// + if (!res || (fp->XClient->LastServerResp()->status) != kXR_ok) + {ec = Fault(fp, 0); retc = -1; + myMutex.Lock(); + myFiles[fp->FD] = 0; + myMutex.UnLock(); + } else { + fp->isOpen(); + fp->XClient->Stat(&fp->stat); + retc = fp->FD; + ec = 0; + } + +// Check if this is a sync callback and, if so, do it now. Otherwise, try +// via the callback manager. +// + if (retc < 0 || cbArg) + {errno = ec; + fp->theCB->Complete(retc < 0 ? -ec : retc); + } else OpenCB(fp, retc, ec); + return retc != -1; +} + +/******************************************************************************/ + +int XrdPosixXrootd::OpenCB(XrdPosixFile *fp, int rCode, int eCode) +{ + static XrdSysMutex cbMutex; + static XrdSysSemaphore cbReady(0); + static XrdPosixFile *First = 0, *Last = 0; + static int Waiting = 0, numThreads = 0; + XrdPosixFile *cbFP; + int rc = 0; + +// If this is a feeder thread, look for some work +// + if (!fp) + do {cbMutex.Lock(); + if (!First && numThreads > 1) + {numThreads--; cbMutex.UnLock(); return 0;} + while(!(cbFP = First)) + {Waiting = 1; + cbMutex.UnLock(); cbReady.Wait(); cbMutex.Lock(); + Waiting = 0; + } + if (!(First = cbFP->Next)) Last = 0; + cbMutex.UnLock(); + if (cbFP->cbResult < 0) errno = cbFP->cbResult; + cbFP->theCB->Complete(cbFP->cbResult); + } while(1); + +// Establish the final result +// + fp->cbResult = (rCode < 0 ? -eCode : rCode); + +// Lock our data structure and queue this element +// + cbMutex.Lock(); + if (Last) fp->Next = Last; + else First = fp; + Last = fp; + +// See if we should start a thread +// + if (!Waiting && numThreads < maxThreads) + {pthread_t tid; + if ((rc = XrdSysThread::Run(&tid, XrdPosixXrootdCB, (void *)0, + 0, "Callback thread"))) + cerr <<"XrdPosix: Unable to create callback thread; " + <<strerror(rc) <<endl; + else numThreads++; + } + +// All done +// + cbReady.Post(); + cbMutex.UnLock(); + return rc; +} + /******************************************************************************/ /* O p e n d i r */ /******************************************************************************/ @@ -1249,6 +1408,7 @@ void XrdPosixXrootd::initEnv() {"XRDPOSIX_RCUP", NAME_REMUSEDCACHEBLKS, 0}, {"XRDPOSIX_RDTTL", NAME_LBSERVERCONN_TTL, 0}, {"XRDPOSIX_RTO", NAME_REQUESTTIMEOUT, 0}, + {"XRDPOSIX_TTO", NAME_TRANSACTIONTIMEOUT, 0}, {"XRDPSOIX_PSPC", NAME_MULTISTREAMCNT, 0}, {"XRDPSOIX_CTO", NAME_CONNECTTIMEOUT, 0}, {"XRDPSOIX_CRDELAY", NAME_RECONNECTWAIT, 0}, @@ -1387,7 +1547,7 @@ int XrdPosixXrootd::Fault(XrdPosixFile *fp, int complete) cerr <<"XrdPosix: " <<etext <<endl; } - if (!complete) return rc; + if (!complete) return ecode; fp->UnLock(); errno = ecode; return rc; diff --git a/net/xrootd/src/xrootd/src/XrdPosix/XrdPosixXrootd.hh b/net/xrootd/src/xrootd/src/XrdPosix/XrdPosixXrootd.hh index 05991cfb5e2dfb03153699fd9c7afb95839a96ec..1cb14f97c60f65a1a15c216e49939ed180b2673f 100644 --- a/net/xrootd/src/xrootd/src/XrdPosix/XrdPosixXrootd.hh +++ b/net/xrootd/src/xrootd/src/XrdPosix/XrdPosixXrootd.hh @@ -4,7 +4,7 @@ /* */ /* X r d P o s i x X r o o t d */ /* */ -/* (c) 2005 by the Board of Trustees of the Leland Stanford, Jr., University */ +/* (c) 2010 by the Board of Trustees of the Leland Stanford, Jr., University */ /* All Rights Reserved */ /* Produced by Andrew Hanushevsky for Stanford University under contract */ /* DE-AC02-76-SFO0515 with the Department of Energy */ @@ -29,6 +29,7 @@ #include "XrdPosix/XrdPosixOsDep.hh" #include "XrdSys/XrdSysPthread.hh" +class XrdPosixCallBack; class XrdPosixFile; class XrdPosixDir; @@ -55,7 +56,10 @@ static off_t Lseek(int fildes, off_t offset, int whence); static int Mkdir(const char *path, mode_t mode); -static int Open(const char *path, int oflag, mode_t mode=0, int Stream=0); +static const int isStream = 0x40000000; // Internal for Open oflag + +static int Open(const char *path, int oflag, mode_t mode=0, + XrdPosixCallBack *cbP=0); static DIR* Opendir(const char *path); @@ -103,6 +107,8 @@ static ssize_t Writev(int fildes, const struct iovec *iov, int iovcnt); // static int Access(const char *path, int amode); +static int endPoint(int FD, char *Buff, int Blen); + static bool isXrootdDir(DIR *dirp); static int mapError(int rc); @@ -110,6 +116,9 @@ static int mapError(int rc); static inline bool myFD(int fd) {return fd <= highFD && myFiles && myFiles[fd];} +static int OpenCB(int res, XrdPosixFile *fp, void *cbArg); +static int OpenCB(XrdPosixFile *fp=0, int rC=0, int eC=0); + static long long QueryOpaque(const char*, char*, int); static void setDebug(int val); @@ -120,7 +129,7 @@ static void setEnv(const char *var, long val); static int Debug; - XrdPosixXrootd(int maxfd=255, int maxdir=255); + XrdPosixXrootd(int maxfd=255, int maxdir=255, int maxthr=255); ~XrdPosixXrootd(); private: @@ -143,5 +152,6 @@ static int lastDir; static int highDir; static int devNull; static int pllOpen; +static int maxThreads; }; #endif diff --git a/net/xrootd/src/xrootd/src/XrdSec/Makefile.am b/net/xrootd/src/xrootd/src/XrdSec/Makefile.am index 590593c26920f5eea78efff51d6fccbbe428ef9c..9d5ffafaeb6a450eb5d449fe6dd8b0f2b1a00323 100644 --- a/net/xrootd/src/xrootd/src/XrdSec/Makefile.am +++ b/net/xrootd/src/xrootd/src/XrdSec/Makefile.am @@ -4,12 +4,12 @@ ## ## Initial version: 1.8.2005 ## -## Version info: $Id: Makefile.am,v 1.7 2008/12/11 02:48:46 abh Exp $ -## Checked in by $Author: abh $ +## Version info: $Id: Makefile.am,v 1.8 2010/01/12 15:04:06 ganis Exp $ +## Checked in by $Author: ganis $ ####################################################################### xrootdsecincdir = $(includedir)/xrootd/XrdSec -xrootdsecinc_HEADERS = XrdSecInterface.hh XrdSecEntity.hh +xrootdsecinc_HEADERS = XrdSecInterface.hh XrdSecEntity.hh XrdSecTLayer.hh lib_LTLIBRARIES = libXrdSec.la diff --git a/net/xrootd/src/xrootd/src/XrdSec/XrdSecTrace.hh b/net/xrootd/src/xrootd/src/XrdSec/XrdSecTrace.hh index 2e0e0f4157b9fc63ca2dfddd6d4236e2282b9d75..86e1adb68916f96e4eae7f7ae96e561268c35e22 100644 --- a/net/xrootd/src/xrootd/src/XrdSec/XrdSecTrace.hh +++ b/net/xrootd/src/xrootd/src/XrdSec/XrdSecTrace.hh @@ -26,7 +26,7 @@ #define DEBUG(y) if (QTRACE(Debug)) \ {SecTrace->Beg(epname); cerr <<y; SecTrace->End();} -#define EPNAME(x) const char *epname = x; +#define EPNAME(x) static const char *epname = x; #else diff --git a/net/xrootd/src/xrootd/src/XrdSecgsi/XrdSecgsiTrace.hh b/net/xrootd/src/xrootd/src/XrdSecgsi/XrdSecgsiTrace.hh index ad5cdf36f1ad0d31aba5d94756e11b4283d5a29e..a666e01f7c225beec553e258b4adf36a630d0120 100644 --- a/net/xrootd/src/xrootd/src/XrdSecgsi/XrdSecgsiTrace.hh +++ b/net/xrootd/src/xrootd/src/XrdSecgsi/XrdSecgsiTrace.hh @@ -20,7 +20,7 @@ cerr <<y; gsiTrace->End();}} #define TRACE(act,x) if (QTRACE(act)) PRINT(x) #define DEBUG(y) TRACE(Debug,y) -#define EPNAME(x) const char *epname = x; +#define EPNAME(x) static const char *epname = x; #else diff --git a/net/xrootd/src/xrootd/src/XrdSeckrb5/GNUmakefile b/net/xrootd/src/xrootd/src/XrdSeckrb5/GNUmakefile index ff781e24e3c5e6c112f46911d28a6087a0db7aa3..0a10f3cf84a015c96aa6e7673fa8965023956675 100644 --- a/net/xrootd/src/xrootd/src/XrdSeckrb5/GNUmakefile +++ b/net/xrootd/src/xrootd/src/XrdSeckrb5/GNUmakefile @@ -1,4 +1,4 @@ -# $Id: GNUmakefile,v 1.14 2008/09/24 17:01:15 ganis Exp $ +# $Id: GNUmakefile,v 1.15 2009/12/02 13:04:28 ganis Exp $ #------------------------------------------------------------------------------# # C o m m o n V a r i a b l e s # diff --git a/net/xrootd/src/xrootd/src/XrdSeckrb5/XrdSecProtocolkrb5.cc b/net/xrootd/src/xrootd/src/XrdSeckrb5/XrdSecProtocolkrb5.cc index 1de1352723ab4ca076b1c8b5dd036ec683e6c681..65410e130419c984954445e3e4cf8f1fbad52c77 100644 --- a/net/xrootd/src/xrootd/src/XrdSeckrb5/XrdSecProtocolkrb5.cc +++ b/net/xrootd/src/xrootd/src/XrdSeckrb5/XrdSecProtocolkrb5.cc @@ -42,8 +42,6 @@ extern "C" { #include "XrdOuc/XrdOucTokenizer.hh" #include "XrdSec/XrdSecInterface.hh" #include "XrdSys/XrdSysPriv.hh" -#include "XrdOuc/XrdOucString.hh" -#include "XrdSut/XrdSutAux.hh" /******************************************************************************/ /* D e f i n e s */ @@ -60,7 +58,7 @@ extern "C" { #define XrdSecMAXPATHLEN 4096 -#define CLDBG(x) if (options & XrdSecDEBUG) cerr <<"Seckrb5: " <<x <<endl; +#define CLDBG(x) if (client_options & XrdSecDEBUG) cerr <<"Seckrb5: " <<x <<endl; #define CLPRT(x) cerr <<"Seckrb5: " <<x <<endl; typedef krb5_error_code krb_rc; @@ -86,6 +84,7 @@ static char *getPrincipal() {return Principal;} static int Init(XrdOucErrInfo *einfo, char *KP=0, char *kfn=0); static void setOpts(int opts) {options = opts;} +static void setClientOpts(int opts) {client_options = opts;} static void setParms(char *param) {Parms = param;} static void setExpFile(char *expfile) {if (expfile) @@ -107,6 +106,7 @@ static void setExpFile(char *expfile) Entity.name = CName; Step = 0; AuthContext = 0; + AuthClientContext = 0; Ticket = 0; Creds = 0; } @@ -120,10 +120,14 @@ private: static int Fatal(XrdOucErrInfo *erp,int rc,const char *msg1,char *KP=0,int krc=0); static int get_krbCreds(char *KP, krb5_creds **krb_creds); -static XrdSysMutex krbContext; // Client or server -static int options; // Client or server -static krb5_context krb_context; // Client or server -static krb5_ccache krb_ccache; // Client or server +static XrdSysMutex krbContext; // Server +static XrdSysMutex krbClientContext;// Client +static int options; // Server +static int client_options;// Client +static krb5_context krb_context; // Server +static krb5_context krb_client_context; // Client +static krb5_ccache krb_client_ccache; // Client +static krb5_ccache krb_ccache; // Server static krb5_keytab krb_keytab; // Server static uid_t krb_kt_uid;// Server static gid_t krb_kt_gid;// Server @@ -142,6 +146,7 @@ char CName[256]; // Kerberos limit char *Service; // Target principal for client char Step; // Indicates at which step we are krb5_auth_context AuthContext; // Authetication context +krb5_auth_context AuthClientContext; // Authetication context krb5_ticket *Ticket; // Ticket associated to client authentication krb5_creds *Creds; // Client: credentials }; @@ -150,11 +155,15 @@ krb5_creds *Creds; // Client: credentials /* S t a t i c D a t a */ /******************************************************************************/ -XrdSysMutex XrdSecProtocolkrb5::krbContext; // Client or server - -int XrdSecProtocolkrb5::options = 0; // Client or Server -krb5_context XrdSecProtocolkrb5::krb_context; // Client or server -krb5_ccache XrdSecProtocolkrb5::krb_ccache; // Client or server +XrdSysMutex XrdSecProtocolkrb5::krbContext; // Server +XrdSysMutex XrdSecProtocolkrb5::krbClientContext; // Client + +int XrdSecProtocolkrb5::client_options = 0;// Client +int XrdSecProtocolkrb5::options = 0; // Server +krb5_context XrdSecProtocolkrb5::krb_context; // Server +krb5_context XrdSecProtocolkrb5::krb_client_context; // Client +krb5_ccache XrdSecProtocolkrb5::krb_client_ccache; // Client +krb5_ccache XrdSecProtocolkrb5::krb_ccache; // Server krb5_keytab XrdSecProtocolkrb5::krb_keytab = NULL; // Server uid_t XrdSecProtocolkrb5::krb_kt_uid = 0; // Server gid_t XrdSecProtocolkrb5::krb_kt_gid = 0; // Server @@ -175,6 +184,7 @@ void XrdSecProtocolkrb5::Delete() if (Creds) krb5_free_creds(krb_context, Creds); if (Ticket) krb5_free_ticket(krb_context, Ticket); if (AuthContext) krb5_auth_con_free(krb_context, AuthContext); + if (AuthClientContext) krb5_auth_con_free(krb_client_context, AuthClientContext); if (Entity.host) free(Entity.host); if (Service) free(Service); delete this; @@ -191,7 +201,7 @@ XrdSecCredentials *XrdSecProtocolkrb5::getCredentials(XrdSecParameters *noparm, int bsz; krb_rc rc; krb5_data outbuf; - + CLDBG("getCredentials"); // Supply null credentials if so needed for this protocol // if (!Service) @@ -206,48 +216,55 @@ XrdSecCredentials *XrdSecProtocolkrb5::getCredentials(XrdSecParameters *noparm, sprintf(ccname, "KRB5CCNAME=FILE:/tmp/krb5cc_%d", geteuid()); putenv(strdup(ccname)); } - + CLDBG(getenv("KRB5CCNAME")); + // Initialize the context and get the cache default. // - if ((rc = krb5_init_context(&krb_context))) + if ((rc = krb5_init_context(&krb_client_context))) {Fatal(error, ENOPROTOOPT, "Kerberos initialization failed", Service, rc); return (XrdSecCredentials *)0; } + CLDBG("init context"); // Obtain the default cache location // - if ((rc = krb5_cc_default(krb_context, &krb_ccache))) + if ((rc = krb5_cc_default(krb_client_context, &krb_client_ccache))) {Fatal(error, ENOPROTOOPT, "Unable to locate cred cache", Service, rc); return (XrdSecCredentials *)0; } + CLDBG("cc cache default"); // Check if the server asked for a forwardable ticket // char *pfwd = 0; if ((pfwd = (char *) strstr(Service,",fwd"))) { - options |= XrdSecEXPTKN; + client_options |= XrdSecEXPTKN; *pfwd = 0; } // Clear outgoing ticket and lock the kerberos context // outbuf.length = 0; outbuf.data = 0; - krbContext.Lock(); + + CLDBG("context lock"); + krbClientContext.Lock(); + CLDBG("context locked"); // If this is not the first call, we are asked to send over a delegated ticket: // we must create it first // we save it into a file and return signalling the end of the hand-shake // + if (Step > 0) {if ((rc = get_krbFwdCreds(Service, &outbuf))) - {krbContext.UnLock(); + {krbClientContext.UnLock(); Fatal(error, ESRCH, "Unable to get forwarded credentials", Service, rc); return (XrdSecCredentials *)0; } else {bsz = XrdSecPROTOIDLEN+outbuf.length; if (!(buff = (char *)malloc(bsz))) - {krbContext.UnLock(); + {krbClientContext.UnLock(); Fatal(error, ENOMEM, "Insufficient memory for credentials.", Service); return (XrdSecCredentials *)0; } @@ -256,7 +273,7 @@ XrdSecCredentials *XrdSecProtocolkrb5::getCredentials(XrdSecParameters *noparm, (const void *)outbuf.data, (size_t)outbuf.length); CLDBG("Returned " <<bsz <<" bytes of creds; p=" <<Service); if (outbuf.data) free(outbuf.data); - krbContext.UnLock(); + krbClientContext.UnLock(); return new XrdSecCredentials(buff, bsz); } } @@ -267,14 +284,14 @@ XrdSecCredentials *XrdSecProtocolkrb5::getCredentials(XrdSecParameters *noparm, // Get a service ticket for this principal // - const char *reinitcmd = (options & XrdSecEXPTKN) ? "kinit -f" : "kinit"; + const char *reinitcmd = (client_options & XrdSecEXPTKN) ? "kinit -f" : "kinit"; bool notdone = 1; bool reinitdone = 0; while (notdone) {if ((rc = (krb_rc)get_krbCreds(Service, &Creds))) - { if (!(options & XrdSecINITTKN) || reinitdone) - {krbContext.UnLock(); - const char *m = (!(options & XrdSecINITTKN)) ? + { if (!(client_options & XrdSecINITTKN) || reinitdone) + {krbClientContext.UnLock(); + const char *m = (!(client_options & XrdSecINITTKN)) ? "No or invalid credentials" : "Unable to get credentials"; Fatal(error, ESRCH, m, Service, rc); return (XrdSecCredentials *)0; @@ -287,10 +304,10 @@ XrdSecCredentials *XrdSecProtocolkrb5::getCredentials(XrdSecParameters *noparm, continue; } } - if (options & XrdSecEXPTKN) + if (client_options & XrdSecEXPTKN) {// Make sure the ticket is forwardable if (!(Creds->ticket_flags & TKT_FLG_FORWARDABLE)) - { if (options & XrdSecINITTKN) + { if (client_options & XrdSecINITTKN) { // Need to re-init CLPRT("Existing ticket is not forwardable: re-init "); rc = system(reinitcmd); @@ -299,7 +316,7 @@ XrdSecCredentials *XrdSecProtocolkrb5::getCredentials(XrdSecParameters *noparm, reinitdone = 1; continue; } else { - krbContext.UnLock(); + krbClientContext.UnLock(); Fatal(error, ESRCH, "Existing ticket is not forwardable: cannot continue", Service, rc); return (XrdSecCredentials *)0; @@ -312,15 +329,15 @@ XrdSecCredentials *XrdSecProtocolkrb5::getCredentials(XrdSecParameters *noparm, // Set the RET_TIME flag in the authentication context // - if ((rc = krb5_auth_con_init(krb_context, &AuthContext))) - {krbContext.UnLock(); + if ((rc = krb5_auth_con_init(krb_client_context, &AuthClientContext))) + {krbClientContext.UnLock(); Fatal(error, ESRCH, "Unable to init a new auth context", Service, rc); return (XrdSecCredentials *)0; } // Generate a kerberos-style authentication message // - rc = krb5_mk_req_extended(krb_context, &AuthContext, + rc = krb5_mk_req_extended(krb_client_context, &AuthClientContext, AP_OPTS_USE_SESSION_KEY,(krb5_data *)0, Creds,&outbuf); // Check if all succeeded. If so, copy the ticket into the buffer. We wish @@ -330,7 +347,7 @@ XrdSecCredentials *XrdSecProtocolkrb5::getCredentials(XrdSecParameters *noparm, if (!rc) {bsz = XrdSecPROTOIDLEN+outbuf.length; if (!(buff = (char *)malloc(bsz))) - {krbContext.UnLock(); + {krbClientContext.UnLock(); Fatal(error, ENOMEM, "Insufficient memory for credentials.", Service); return (XrdSecCredentials *)0; } @@ -339,14 +356,14 @@ XrdSecCredentials *XrdSecProtocolkrb5::getCredentials(XrdSecParameters *noparm, (const void *)outbuf.data, (size_t)outbuf.length); CLDBG("Returned " <<bsz <<" bytes of creds; p=" <<Service); if (outbuf.data) free(outbuf.data); - krbContext.UnLock(); + krbClientContext.UnLock(); return new XrdSecCredentials(buff, bsz); } // Diagnose the failure // if (outbuf.data) free(outbuf.data); - krbContext.UnLock(); + krbClientContext.UnLock(); Fatal(error, EACCES, "Unable to get credentials", Service, rc); return (XrdSecCredentials *)0; } @@ -386,16 +403,28 @@ int XrdSecProtocolkrb5::Authenticate(XrdSecCredentials *cred, return -1; } + CLDBG("protocol check"); + + char printit[4096]; + sprintf(printit,"Step is %d",Step); + CLDBG(printit); // If this is not the first call the buffer contains a forwarded token: // we save it into a file and return signalling the end of the hand-shake // if (Step > 0) {if ((rc = exp_krbTkn(cred, error))) iferror = (char *)"Unable to export the token to file"; - if (rc && iferror) + if (rc && iferror) { + krbContext.UnLock(); return Fatal(error, EINVAL, iferror, Principal, rc); + } + krbContext.UnLock(); + return 0; } + + krbContext.UnLock(); + CLDBG("protocol check"); // Increment the step // @@ -407,13 +436,18 @@ int XrdSecProtocolkrb5::Authenticate(XrdSecCredentials *cred, // Create a kerberos style ticket and obtain the kerberos mutex // + + CLDBG("Context Lock"); + inbuf.length = cred->size -XrdSecPROTOIDLEN; inbuf.data = &cred->buffer[XrdSecPROTOIDLEN]; + krbContext.Lock(); // Check if whether the IP address in the credentials must match that of // the incomming host. // + CLDBG("Context Locked"); if (!(XrdSecProtocolkrb5::options & XrdSecNOIPCHK)) {struct sockaddr_in *ip = (struct sockaddr_in *)&hostaddr; // The above is a hack but K5 does it this way @@ -487,18 +521,28 @@ int XrdSecProtocolkrb5::Init(XrdOucErrInfo *erp, char *KP, char *kfn) // Create a kerberos context. There is one such context per protocol object. // + +// If we have no principal then this is a client-side call +// + if (!KP) { + if ((rc = krb5_init_context(&krb_client_context))) + return Fatal(erp, ENOPROTOOPT, "Kerberos initialization failed", KP, rc); + + // Obtain the default cache location + // + if ((rc = krb5_cc_default(krb_context, &krb_client_ccache))) + return Fatal(erp, ENOPROTOOPT, "Unable to locate cred cache", KP, rc); + return 0; + } + if ((rc = krb5_init_context(&krb_context))) return Fatal(erp, ENOPROTOOPT, "Kerberos initialization failed", KP, rc); // Obtain the default cache location // - if ((rc = krb5_cc_default(krb_context, &krb_ccache))) + if ((rc = krb5_cc_default(krb_client_context, &krb_client_ccache))) return Fatal(erp, ENOPROTOOPT, "Unable to locate cred cache", KP, rc); -// If we have no principal then this is a client-side call -// - if (!KP) return 0; - // Try to resolve the keyfile name // if (kfn && *kfn) @@ -587,7 +631,7 @@ int XrdSecProtocolkrb5::Fatal(XrdOucErrInfo *erp, int rc, const char *msg, /* g e t _ k r b C r e d s */ /******************************************************************************/ -// Warning! The krbContext lock must be held prior to calling this routine +// Warning! The krbClientContext lock must be held prior to calling this routine int XrdSecProtocolkrb5::get_krbCreds(char *KP, krb5_creds **krb_creds) { @@ -601,30 +645,30 @@ int XrdSecProtocolkrb5::get_krbCreds(char *KP, krb5_creds **krb_creds) // Setup the "principal/instance@realm" // - if ((rc = krb5_parse_name(krb_context,KP,&the_principal))) + if ((rc = krb5_parse_name(krb_client_context,KP,&the_principal))) {CLDBG("get_krbCreds: Cannot parse service name;" <<krb_etxt(rc)); return rc; } // Copy the current target principal into the credentials // - if ((rc = krb5_copy_principal(krb_context, the_principal, &mycreds.server))) + if ((rc = krb5_copy_principal(krb_client_context, the_principal, &mycreds.server))) {CLDBG("get_krbCreds: err copying principal to creds; " <<krb_etxt(rc)); return rc; } // Get our principal name // - if ((rc = krb5_cc_get_principal(krb_context, krb_ccache, &mycreds.client))) - {krb5_free_cred_contents(krb_context, &mycreds); + if ((rc = krb5_cc_get_principal(krb_client_context, krb_client_ccache, &mycreds.client))) + {krb5_free_cred_contents(krb_client_context, &mycreds); CLDBG("get_krbCreds: err copying client name to creds; " <<krb_etxt(rc)); return rc; } // Now get the credentials (free our local info) // - rc = krb5_get_credentials(krb_context, 0, krb_ccache, &mycreds, krb_creds); - krb5_free_cred_contents(krb_context, &mycreds); + rc = krb5_get_credentials(krb_client_context, 0, krb_client_ccache, &mycreds, krb_creds); + krb5_free_cred_contents(krb_client_context, &mycreds); // Check if all went well // @@ -643,21 +687,21 @@ int XrdSecProtocolkrb5::get_krbFwdCreds(char *KP, krb5_data *outdata) // Fill-in our principal // - if ((rc = krb5_cc_get_principal(krb_context, krb_ccache, &client))) + if ((rc = krb5_cc_get_principal(krb_client_context, krb_client_ccache, &client))) {CLDBG("get_krbFwdCreds: err filling client principal; " <<krb_etxt(rc)); return rc; } // Fill-in target (service) principal // - if ((rc = krb5_parse_name(krb_context, KP, &server))) + if ((rc = krb5_parse_name(krb_client_context, KP, &server))) {CLDBG("get_krbFwdCreds: Cannot parse service principal;" <<krb_etxt(rc)); return rc; } // Set the timestamp in the authentication context // - if ((rc = krb5_auth_con_setflags(krb_context, AuthContext, + if ((rc = krb5_auth_con_setflags(krb_client_context, AuthClientContext, KRB5_AUTH_CONTEXT_RET_TIME))) {CLDBG("Unable to set KRB5_AUTH_CONTEXT_RET_TIME" " in the authentication context" << krb_etxt(rc)); @@ -666,8 +710,8 @@ int XrdSecProtocolkrb5::get_krbFwdCreds(char *KP, krb5_data *outdata) // Acquire a TGT for use at a remote host system // - if ((rc = krb5_fwd_tgt_creds(krb_context, AuthContext, 0 /*host*/, - client, server, krb_ccache, true, + if ((rc = krb5_fwd_tgt_creds(krb_client_context, AuthClientContext, 0 /*host*/, + client, server, krb_client_ccache, true, outdata))) {CLDBG("get_krbFwdCreds: err getting forwarded ticket;" <<krb_etxt(rc)); return rc; @@ -688,20 +732,43 @@ int XrdSecProtocolkrb5::exp_krbTkn(XrdSecCredentials *cred, XrdOucErrInfo *erp) // Create the cache filename, expanding the keywords, if needed // - XrdOucString ccfn(XrdSecProtocolkrb5::ExpFile); - -// Resolve place-holders, if any -// - if (XrdSutResolve(ccfn, Entity.host, Entity.vorg, Entity.grps, Entity.name) != 0) - return rc; + char ccfile[XrdSecMAXPATHLEN]; + strcpy(ccfile, XrdSecProtocolkrb5::ExpFile); + int nlen = strlen(ccfile); + char *pusr = (char *) strstr(&ccfile[0], "<user>"); + if (pusr) + {int ln = strlen(CName); + if (ln != 6) { + // Adjust the space + int lm = strlen(ccfile) - (int)(pusr + 6 - &ccfile[0]); + memmove(pusr+ln, pusr+6, lm); + } + // Copy the name + memcpy(pusr, CName, ln); + // Adjust the length + nlen += (ln - 6); + } + char *puid = (char *) strstr(&ccfile[0], "<uid>"); struct passwd *pw = getpwnam(CName); - if (!pw) - return rc; - if (ccfn.find("<uid>") != STR_NPOS) - {XrdOucString suid; if (pw) suid += (int) pw->pw_uid; - ccfn.replace("<uid>", suid.c_str()); - } - char *ccfile = (char *) ccfn.c_str(); + if (puid) + {char cuid[20] = {0}; + if (pw) + sprintf(cuid, "%d", pw->pw_uid); + int ln = strlen(cuid); + if (ln != 5) { + // Adjust the space + int lm = strlen(ccfile) - (int)(puid + 5 - &ccfile[0]); + memmove(puid+ln, pusr+5, lm); + } + // Copy the name + memcpy(puid, cuid, ln); + // Adjust the length + nlen += (ln - 5); + } + +// Terminate to the new length +// + ccfile[nlen] = 0; // Point the received creds // @@ -784,18 +851,26 @@ char *XrdSecProtocolkrb5Init(const char mode, char parmbuff[1024]; XrdOucTokenizer inParms(parmbuff); int options = XrdSecNOIPCHK; + static bool serverinitialized = false; // For client-side one-time initialization, we only need to set debug flag and // initialize the kerberos context and cache location. // - if (mode == 'c') - {int opts = 0; + if ((mode == 'c') || (serverinitialized)) + { + CLPRT("Initializing the client"); + int opts = 0; if (getenv("XrdSecDEBUG")) opts |= XrdSecDEBUG; if (getenv("XrdSecKRB5INITTKN")) opts |= XrdSecINITTKN; - XrdSecProtocolkrb5::setOpts(opts); + XrdSecProtocolkrb5::setClientOpts(opts); return (XrdSecProtocolkrb5::Init(erp) ? (char *)0 : (char *)""); } + if (!serverinitialized) { + serverinitialized = true; + } + + CLPRT("Initializing the server"); // Duplicate the parms // if (parms) strlcpy(parmbuff, parms, sizeof(parmbuff)); @@ -866,6 +941,7 @@ char *XrdSecProtocolkrb5Init(const char mode, // Now initialize the server // + options |= XrdSecDEBUG; XrdSecProtocolkrb5::setExpFile(ExpFile); XrdSecProtocolkrb5::setOpts(options); if (!XrdSecProtocolkrb5::Init(erp, KPrincipal, Keytab)) diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/GNUmakefile b/net/xrootd/src/xrootd/src/XrdSecssl/GNUmakefile new file mode 100644 index 0000000000000000000000000000000000000000..093a235b9bb7e4a8cd9f2a3ca7880978fd0f396b --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdSecssl/GNUmakefile @@ -0,0 +1,150 @@ +# $Id: GNUmakefile,v 1.3 2010/01/29 15:50:18 ganis Exp $ + +#------------------------------------------------------------------------------# +# C o m m o n V a r i a b l e s # +#------------------------------------------------------------------------------# + +include ../GNUmake.env + +MORELIBS = -L$(LIBDIR) -lXrdSec $(LIBSSL) $(LIBDIR)/libsslGridSite.a \ + $(LIBDIR)/libXrdOuc.a $(LIBDIR)/libXrdSys.a $(LIBDIR)/libXrdNet.a $(LIBXML) + +LIBDEPS = $(LIBDIR)/libsslGridSite.a $(LIBDIR)/libXrdOuc.a $(LIBDIR)/libXrdSys.a $(LIBDIR)/libXrdNet.a + +NCXXFLAGS = $(INCSSL) $(SSLEXTRACFLAGS) -DOPENSSL_NO_KRB5 $(subst deprecated,deprecated-declarations,$(CFLAGS)) +NCFLAGS = $(INCXML) $(INCSSL) $(SSLEXTRACFLAGS) -DOPENSSL_NO_KRB5 -U_FORTIFY_SOURCE $(subst deprecated,deprecated-declarations,$(CFLAGS)) + +#------------------------------------------------------------------------------# +# C o m p o n e n t s # +#------------------------------------------------------------------------------# + +CCOMPILE = gcc + +SOURCES = \ + XrdSecProtocolssl.cc \ + XrdSecProtocolsslTest.cc \ + libsslGridSite/grst_asn1.c \ + libsslGridSite/grst_err.c \ + libsslGridSite/grst_gacl.c \ + libsslGridSite/grst_http.c \ + libsslGridSite/grst_verifycallback.c \ + libsslGridSite/grst_x509.c \ + libsslGridSite/grst_xacml.c \ + +OBJECTS = \ + $(OBJDIR)/XrdSecProtocolssl.o + +OBJECTA = \ + $(OBJDIR)/XrdSecProtocolsslTest.o + +OBJGRST = \ + $(OBJDIR)/grst_asn1.o \ + $(OBJDIR)/grst_err.o \ + $(OBJDIR)/grst_gacl.o \ + $(OBJDIR)/grst_http.o \ + $(OBJDIR)/grst_verifycallback.o \ + $(OBJDIR)/grst_x509.o \ + $(OBJDIR)/grst_xacml.o\ + +LIBGRST = $(LIBDIR)/libsslGridSite.a +LIBRARY = $(LIBDIR)/libXrdSecssl.$(TYPESHLIB) + +SECSSLTEST = $(BINDIR)/xrdsecssltest + +TARGETS = $(LIBGRST) $(LIBRARY) $(SECSSLTEST) + +#------------------------------------------------------------------------------# +# S e a r c h P a t h s # +#------------------------------------------------------------------------------# + +vpath XrdOuc% ../XrdOuc +vpath XrdSec% ../XrdSec +vpath XrdSys% ../XrdSys +vpath XrdNet% ../XrdNet + +#------------------------------------------------------------------------------# +# I n i t i a l R u l e s # +#------------------------------------------------------------------------------# + +include ../GNUmake.options + +anything: $(TARGETS) + @echo Make XrdSecssl done. + +#------------------------------------------------------------------------------# +# D e p e n d e n c i e s # +#------------------------------------------------------------------------------# + +$(LIBRARY): $(OBJECTS) $(LIBDEPS) + @echo Creating shared library $(LIBRARY) + $(ECHO)$(CC) $(OBJECTS) $(LDSO) $(MORELIBS) $(LIBS) -o $(LIBRARY); + +$(LIBGRST): $(OBJGRST) + @echo Creating archive $(LIBGRST) + $(ECHO)rm -f $(LIBGRST) + $(ECHO)if [ "$(TYPE)" = "SunOS" -a "$(CC)" = "CC" ]; then \ + if [ "x$(SUNCACHE)" != "x" ]; then \ + $(CC) -xar -o $(LIBGRST) $(OBJDIR)$(SUNCACHE)/*/*.o; \ + else \ + $(CC) -xar -o $(LIBGRST) $(OBJGRST); \ + fi; \ + else \ + ar -rc $(LIBGRST) $(OBJGRST); \ + ranlib $(LIBGRST); \ + fi + +$(SECSSLTEST): $(OBJECTA) $(LIBDEPS) + @echo Creating executable $(SECSSLTEST) + $(ECHO)$(LD) $(LDOP) $(OBJECTA) -L$(LIBDIR) -lXrdSecssl $(MORELIBS) $(LIBS) -o $(SECSSLTEST) + +$(OBJDIR)/XrdSecProtocolssl.o: XrdSecProtocolssl.cc XrdSecProtocolssl.hh XrdSecInterface.hh \ + XrdSysHeaders.hh XrdNetDNS.hh XrdOucErrInfo.hh XrdOucHash.hh \ + XrdOucString.hh XrdOucTrace.hh XrdOucTokenizer.hh XrdSysPthread.hh \ + XrdSysLogger.hh XrdSecInterface.hh XrdSecTLayer.hh \ + XrdSecEntity.hh XrdOucErrInfo.hh XrdSecTLayer.hh \ + libsslGridSite/grst_asn1.c libsslGridSite/grst_err.c \ + libsslGridSite/grst_gacl.c libsslGridSite/grst_http.c \ + libsslGridSite/grst_verifycallback.c libsslGridSite/grst_x509.c \ + libsslGridSite/grst_xacml.c libsslGridSite/gridsite.h + @echo Compiling XrdSecProtocolssl.cc + $(ECHO)$(CC) -c $(NCXXFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdSecProtocolssl.o XrdSecProtocolssl.cc + +$(OBJDIR)/XrdSecProtocolsslTest.o: XrdSecProtocolsslTest.cc XrdSecProtocolssl.hh XrdSecInterface.hh \ + XrdSysHeaders.hh XrdNetDNS.hh XrdOucErrInfo.hh XrdOucHash.hh \ + XrdOucString.hh XrdOucTrace.hh XrdOucTokenizer.hh XrdSysPthread.hh \ + XrdSysLogger.hh XrdSecInterface.hh XrdSecTLayer.hh \ + XrdSecEntity.hh XrdOucErrInfo.hh XrdSecTLayer.hh \ + libsslGridSite/grst_asn1.c libsslGridSite/grst_err.c \ + libsslGridSite/grst_gacl.c libsslGridSite/grst_http.c \ + libsslGridSite/grst_verifycallback.c libsslGridSite/grst_x509.c \ + libsslGridSite/grst_xacml.c libsslGridSite/gridsite.h + @echo Compiling XrdSecProtocolsslTest.cc + $(ECHO)$(CC) -c $(NCXXFLAGS) $(INCLUDE) -o $(OBJDIR)/XrdSecProtocolsslTest.o XrdSecProtocolsslTest.cc + +$(OBJDIR)/grst_asn1.o: libsslGridSite/grst_asn1.c libsslGridSite/gridsite.h + @echo Compiling grst_asn1.c + $(ECHO)$(CCOMPILE) -c $(NCFLAGS) $(INCLUDE) -o $(OBJDIR)/grst_asn1.o libsslGridSite/grst_asn1.c + +$(OBJDIR)/grst_err.o: libsslGridSite/grst_err.c libsslGridSite/gridsite.h + @echo Compiling grst_err.c + $(ECHO)$(CCOMPILE) -c $(NCFLAGS) $(INCLUDE) -o $(OBJDIR)/grst_err.o libsslGridSite/grst_err.c + +$(OBJDIR)/grst_gacl.o: libsslGridSite/grst_gacl.c libsslGridSite/gridsite.h + @echo Compiling grst_gacl.c + $(ECHO)$(CCOMPILE) -c $(NCFLAGS) $(INCLUDE) -o $(OBJDIR)/grst_gacl.o libsslGridSite/grst_gacl.c + +$(OBJDIR)/grst_http.o: libsslGridSite/grst_http.c libsslGridSite/gridsite.h + @echo Compiling grst_http.c + $(ECHO)$(CCOMPILE) -c $(NCFLAGS) $(INCLUDE) -o $(OBJDIR)/grst_http.o libsslGridSite/grst_http.c + +$(OBJDIR)/grst_verifycallback.o: libsslGridSite/grst_verifycallback.c libsslGridSite/gridsite.h + @echo Compiling grst_verifycallback.c + $(ECHO)$(CCOMPILE) -c $(NCFLAGS) $(INCLUDE) -o $(OBJDIR)/grst_verifycallback.o libsslGridSite/grst_verifycallback.c + +$(OBJDIR)/grst_x509.o: libsslGridSite/grst_x509.c libsslGridSite/gridsite.h + @echo Compiling grst_x509.c + $(ECHO)$(CCOMPILE) -c $(NCFLAGS) $(INCLUDE) -o $(OBJDIR)/grst_x509.o libsslGridSite/grst_x509.c + +$(OBJDIR)/grst_xacml.o: libsslGridSite/grst_xacml.c libsslGridSite/gridsite.h + @echo Compiling grst_xacml.c + $(ECHO)$(CCOMPILE) -c $(NCFLAGS) $(INCLUDE) -o $(OBJDIR)/grst_xacml.o libsslGridSite/grst_xacml.c diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/Makefile.am b/net/xrootd/src/xrootd/src/XrdSecssl/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..4d67c0a009079fc6a9f48f59061e02b0e3a3039e --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdSecssl/Makefile.am @@ -0,0 +1,44 @@ +####################################################################### +## Makefile.am for xrootd +## +## +## Initial version: 1.8.2005 +## +## Version info: $Id$ +## Checked in by $Author: ganis $ +####################################################################### + +SUBDIRS = libsslGridSite + +bin_PROGRAMS = xrdsecssltest + +lib_LTLIBRARIES = libXrdSecssl.la + +INCLUDES = -I$(top_srcdir)/src -I${XROOTD_INCDIR}/ -IlibsslGridSite ${OPENSSL_INCDIR} +libXrdSecssl_la_SOURCES = XrdSecProtocolssl.cc XrdSecProtocolssl.hh XrdSecProtocolsslTrace.hh +libXrdSecssl_la_LIBADD = +libXrdSecssl_la_LDFLAGS = -L${XROOTD_LIBDIR}/ -lXrdSec -lXrdSys -lXrdOuc -lXrdNet ${SSLLINKLIB} libsslGridSite/libsslGridSite.la ${OPENSSL_LIBDIR} -lcrypto -lssl ${XML2_LIBDIR} -lxml2 -module +libXrdSecssl_la_CXXFLAGS = -Wall -Werror -Wno-deprecated-declarations ${CXXFLAGS} +libXrdSecssl_la_LIBTOOLFLAGS = --tag=disable-static + +xrdsecssltest_SOURCES = XrdSecProtocolsslTest.cc +xrdsecssltest_LDADD = libXrdSecssl.la + + + +EXTRA_DIST = bootstrap.sh getAutotools.sh README acinclude.m4 configure.ac GNUmakefile.classic + +rpm: dist + cp $(DIST_ARCHIVES) /usr/src/redhat/SOURCES/ + rpmbuild -bb xrootd-secssl.spec + +srpm: dist + rpmbuild -bs xrootd-secssl.spec + +test: xrdsecssltest + if [ -d "/etc/grid-security/certificates" ] && [ -e "/etc/grid-security/hostcert.pem" ] && [ -e "/etc/grid-security/hostkey.pem" ] && [ -e "/tmp/x509up_u$$UID" ] ; then\ + ( echo "Starting Server ..." && ./xrdsecssltest server >& /tmp/xrdsecssltest-server.log & ) && echo "Starting Client ..." && sleep 1 && ./xrdsecssltest client 2> /tmp/xrdsecssltest-client.log ; /usr/bin/killall -9 xrdsecssltest lt-xrdsecssltest >& /dev/null || echo Server stopped! ; \ + else \ + echo "Error: you need /etc/grid-security/certificates /etc/grid-security/hostcert.pem /etc/grid-security /tmp/x509up_u$$UID";\ + fi + diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/README b/net/xrootd/src/xrootd/src/XrdSecssl/README new file mode 100644 index 0000000000000000000000000000000000000000..e6cae81aa68817b81f18658d09f9e94078250dde --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdSecssl/README @@ -0,0 +1,43 @@ +libXrdSecProtocolssl: +======================= +This library implements SSL authentication with Proxy/VOMS support for xrootd. The Verify/VOMS code is taken from Apache mod_ssl & mod_gridsite/libgridsite. +The SSL handshake is done via the virtual socket Layer in xrootd. The implementation supports session reuse. Session information is stored on client side in files like /tmp/xssl_<uid>:<desthost>.<destdomain> + +Currently only the server verifies the client certificate/revocation lists. + + +Configuratione Options: +======================= + +server: +-------------- +-d:<debug level> : debug level (default 0) +-cadir:<certdir> : CA directory (default /etc/grid-services/certificates) +-cert:<certfile> : Service certificate (default /etc/grid-services/hostcert.pem for server) +-key:<keyfile> : Service key (default /etc/grid-services/hostkey.pem for server) +-ca:<level> : CA verification level (default 10) +-t:<session lifetime> : SSL Session lifetime in seconds (default 86400 sec = 24 hours) +-export:<dir> : Directory where forwared proxies are stored (default <unset> = don't store) +-gridmapfile:<file> : Grid Mapfile (default /etc/grid-security/grid-mapfile) +-vomsmapfile:<file> : VOMS Mapfile (default /etc/grid-security/voms-mapfile) +-mapuser:1|0 : apply grid map file for user mapping (default 0) +-mapgroup:1|0 : apply voms map file for group mapping (default 0) +-mapcernuser:1|0 : apply automatic mapping of CERN users to AFS accounts +-mapnobody:1|0 : if a user cannot be mapped this would map him to nobody if set, otherwise authentication failure + +client: (Env-Variables) + +XrdSecNoSSL : disables usage of SSL protocol even if server offered +XrdSecDEBUG : debug level (default 0) +XrdSecSSLCADIR : CA directory (default /etc/grid-services/certificates) +XrdSecSSLUSERCERT : client certificate (default proxy /tmp/x509up_u$UID) +XrdSecSSLUSERKEY : client key (default proxy /tmp/x509up_u$UID) +XrdSecSSLPROXYFORWARD : foward the proxy certificate to the remote service +XrdSecSSLVERIFYDEPTH : verification level depth (default 10) +XrdSecSSLSESSION : enable/disable ssl session reuse (default 1 [yes]) + +# for Globus compatibility +X509_CERT_DIR : CA directory if XrdSecSSLCADIR is not defined +X509_USER_KEY : client key if XrdSecSSLUSERKEY is not defined +X509_USER_CERT : client certificate if XrdSecSSLUSERCERT is not defined + diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/XrdSecProtocolssl.cc b/net/xrootd/src/xrootd/src/XrdSecssl/XrdSecProtocolssl.cc new file mode 100644 index 0000000000000000000000000000000000000000..dad70e60e506bf18ccf27492ddc6c6810cc1f5fd --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdSecssl/XrdSecProtocolssl.cc @@ -0,0 +1,1430 @@ +/******************************************************************************/ +/* */ +/* X r d S e c P r o t o c o l s s l . c c */ +/* */ +/* (c) 2007 by the Board of Trustees of the Leland Stanford, Jr., University */ +/* All Rights Reserved */ +/* Produced by Andrew Hanushevsky for Stanford University under contract */ +/* DE-AC02-76-SFO0515 with the Department of Energy */ +/******************************************************************************/ + +// $Id$ + + +const char *XrdSecProtocolsslCVSID = "$Id$"; + +#include "XrdSecProtocolssl.hh" + +char* XrdSecProtocolssl::sslcadir=0; +char* XrdSecProtocolssl::sslvomsdir=0; +int XrdSecProtocolssl::verifydepth=10; +int XrdSecProtocolssl::verifyindex=0; +char* XrdSecProtocolssl::sslkeyfile=0; +char* XrdSecProtocolssl::sslserverkeyfile=0; +char XrdSecProtocolssl::sslserverexportpassword[EXPORTKEYSTRENGTH+1]; +char* XrdSecProtocolssl::sslcertfile=0; +char* XrdSecProtocolssl::sslproxyexportdir=(char*)0; +bool XrdSecProtocolssl::sslproxyexportplain=1; +int XrdSecProtocolssl::debug=0; +time_t XrdSecProtocolssl::sslsessionlifetime=86400; +bool XrdSecProtocolssl::isServer=0; +bool XrdSecProtocolssl::forwardProxy=0; +bool XrdSecProtocolssl::allowSessions=1; +char* XrdSecProtocolssl::SessionIdContext = (char*)"xrootdssl"; +char* XrdSecProtocolssl::gridmapfile = (char*) "/etc/grid-security/grid-mapfile"; +bool XrdSecProtocolssl::mapuser = false; +bool XrdSecProtocolssl::mapnobody = false; +char* XrdSecProtocolssl::vomsmapfile = (char*) "/etc/grid-security/voms-mapfile"; +bool XrdSecProtocolssl::mapgroup = false; +bool XrdSecProtocolssl::mapcerncertificates = false; + +X509_STORE* XrdSecProtocolssl::store=0; +X509_LOOKUP* XrdSecProtocolssl::lookup=0; +SSL_CTX* XrdSecProtocolssl::ctx=0; +XrdSysError XrdSecProtocolssl::ssleDest(0, "secssl_"); +XrdSysLogger XrdSecProtocolssl::Logger; +time_t XrdSecProtocolssl::storeLoadTime; + +XrdSysMutex XrdSecsslSessionLock::sessionmutex; +XrdOucHash<XrdOucString> XrdSecProtocolssl::gridmapstore; +XrdOucHash<XrdOucString> XrdSecProtocolssl::vomsmapstore; +XrdOucHash<XrdOucString> XrdSecProtocolssl::stringstore; +XrdSysMutex XrdSecProtocolssl::StoreMutex; +XrdSysMutex XrdSecProtocolssl::GridMapMutex; +XrdSysMutex XrdSecProtocolssl::VomsMapMutex; +XrdSysMutex* XrdSecProtocolssl::CryptoMutexPool[PROTOCOLSSL_MAX_CRYPTO_MUTEX]; + +/******************************************************************************/ +/* T h r e a d - S a f e n e s s F u n c t i o n s */ +/******************************************************************************/ +static unsigned long protocolssl_id_callback(void) { + return (unsigned long)XrdSysThread::ID(); +} + +void protocolssl_lock(int mode, int n, const char *file, int line) +{ + if (mode & CRYPTO_LOCK) { + if (XrdSecProtocolssl::CryptoMutexPool[n]) { + XrdSecProtocolssl::CryptoMutexPool[n]->Lock(); + } + } else { + if (XrdSecProtocolssl::CryptoMutexPool[n]) { + XrdSecProtocolssl::CryptoMutexPool[n]->UnLock(); + } + } +} + + +/******************************************************************************/ +/* C l i e n t O r i e n t e d F u n c t i o n s */ +/******************************************************************************/ + +int secprotocolssl_pem_cb(char *buf, int size, int rwflag, void *password) +{ + memset(buf,0,size); + memcpy(buf,XrdSecProtocolssl::sslserverexportpassword,EXPORTKEYSTRENGTH+1); + return EXPORTKEYSTRENGTH; +} + +void XrdSecProtocolssl::GetEnvironment() { + EPNAME("GetEnvironment"); + // default the cert/key file to the standard proxy locations + char proxyfile[1024]; + sprintf(proxyfile,"/tmp/x509up_u%d",(int)geteuid()); + + if (sslproxyexportdir) { + sprintf(proxyfile,"%s/x509up_u%d",sslproxyexportdir,(int)geteuid()); + } + + if (XrdSecProtocolssl::sslcertfile) { free (XrdSecProtocolssl::sslcertfile); } + if (XrdSecProtocolssl::sslkeyfile) { free (XrdSecProtocolssl::sslkeyfile); } + + XrdSecProtocolssl::sslcertfile = strdup(proxyfile); + XrdSecProtocolssl::sslkeyfile = strdup(proxyfile); + + char *cenv = getenv("XrdSecDEBUG"); + // debug + if (cenv) + if (cenv[0] >= 49 && cenv[0] <= 57) XrdSecProtocolssl::debug = atoi(cenv); + + // directory with CA certificates + cenv = getenv("XrdSecSSLCADIR"); + if (cenv) { + if (XrdSecProtocolssl::sslcadir) { free (XrdSecProtocolssl::sslcadir); } + XrdSecProtocolssl::sslcadir = strdup(cenv); + } + else { + // accept X509_CERT_DIR + cenv = getenv("X509_CERT_DIR"); + if (cenv) { + if (XrdSecProtocolssl::sslcadir) { free (XrdSecProtocolssl::sslcadir); } + XrdSecProtocolssl::sslcadir = strdup(cenv); + } + } + // directory with VOMS certificates + cenv = getenv("XrdSecSSLVOMSDIR"); + if (cenv) { + if (XrdSecProtocolssl::sslvomsdir) { free (XrdSecProtocolssl::sslvomsdir); } + XrdSecProtocolssl::sslvomsdir = strdup(cenv); + } + + + // file with user cert + cenv = getenv("XrdSecSSLUSERCERT"); + if (cenv) { + if (XrdSecProtocolssl::sslcertfile) { free (XrdSecProtocolssl::sslcertfile); } + XrdSecProtocolssl::sslcertfile = strdup(cenv); + } else { + // accept X509_USER_CERT + cenv = getenv("X509_USER_CERT"); + if (cenv) { + if (XrdSecProtocolssl::sslcertfile) { free (XrdSecProtocolssl::sslcertfile); } + XrdSecProtocolssl::sslcertfile = strdup(cenv); + } else { + // accept X509_USER_PROXY + cenv = getenv("X509_USER_PROXY"); + if (cenv) { + if (XrdSecProtocolssl::sslcertfile) { free (XrdSecProtocolssl::sslcertfile); } + XrdSecProtocolssl::sslcertfile = strdup(cenv); + } + } + } + + // file with user key + cenv = getenv("XrdSecSSLUSERKEY"); + if (cenv) { + if (XrdSecProtocolssl::sslkeyfile) { free (XrdSecProtocolssl::sslkeyfile); } + XrdSecProtocolssl::sslkeyfile = strdup(cenv); + } else { + // accept X509_USER_KEY + cenv = getenv("X509_USER_KEY"); + if (cenv) { + if (XrdSecProtocolssl::sslkeyfile) { free (XrdSecProtocolssl::sslkeyfile); } + XrdSecProtocolssl::sslkeyfile = strdup(cenv); + } else { + // accept X509_USER_PROXY + cenv = getenv("X509_USER_PROXY"); + if (cenv) { + if (XrdSecProtocolssl::sslkeyfile) { free (XrdSecProtocolssl::sslkeyfile); } + XrdSecProtocolssl::sslkeyfile = strdup(cenv); + } + } + } + // verify depth + cenv = getenv("XrdSecSSLVERIFYDEPTH"); + if (cenv) + XrdSecProtocolssl::verifydepth = atoi(cenv); + + // proxy forwarding + cenv = getenv("XrdSecSSLPROXYFORWARD"); + if (cenv) + XrdSecProtocolssl::forwardProxy = atoi(cenv); + + // ssl session reuse + cenv = getenv("XrdSecSSLSESSION"); + if (cenv) + XrdSecProtocolssl::allowSessions = atoi(cenv); + + TRACE(Authen,"====> debug = " << XrdSecProtocolssl::debug); + TRACE(Authen,"====> cadir = " << XrdSecProtocolssl::sslcadir); + TRACE(Authen,"====> keyfile = " << XrdSecProtocolssl::sslkeyfile); + TRACE(Authen,"====> certfile = " << XrdSecProtocolssl::sslcertfile); + TRACE(Authen,"====> verify depth = " << XrdSecProtocolssl::verifydepth); +} + +int XrdSecProtocolssl::Fatal(XrdOucErrInfo *erp, const char *msg, int rc) +{ + const char *msgv[8]; + int k, i = 0; + + msgv[i++] = "Secssl: "; //0 + msgv[i++] = msg; //1 + + if (erp) erp->setErrInfo(rc, msgv, i); + else {for (k = 0; k < i; k++) cerr <<msgv[k]; + cerr <<endl; + } + + return -1; +} + +/******************************************************************************/ +/* g e t C r e d e n t i a l s */ +/******************************************************************************/ + + +void +XrdSecProtocolssl::secClient(int theFD, XrdOucErrInfo *error) { + + EPNAME("secClient"); + + char *nossl = getenv("XrdSecNoSSL"); + if (nossl) { + error->setErrInfo(ENOENT,"SSL is disabled by force"); + return ; + } + + XrdSecProtocolssl::GetEnvironment(); + + error->setErrInfo(0,""); + SSLMutex.Lock(); + + int err; + char* str; + SSL_METHOD *meth; + SSL_SESSION *session=0; + + SSL_load_error_strings(); + SSLeay_add_ssl_algorithms(); + meth = (SSL_METHOD*) TLSv1_client_method(); + + ERR_load_crypto_strings(); + + XrdOucString sslsessionfile=""; + XrdOucString sslsessionid=""; + + sslsessionfile = "/tmp/xssl_"; + sslsessionid += (int)geteuid(); + sslsessionid += ":"; + sslsessionid += host.c_str(); + sslsessionfile += sslsessionid; + + XrdSecsslSessionLock sessionlock; + sessionlock.SoftLock(); + + if (allowSessions) { + struct stat sessionstat; + + if (!stat(sslsessionfile.c_str(),&sessionstat)) { + // session exists ... I try to read it + FILE* fp = fopen(sslsessionfile.c_str(), "r"); + if (fp) { + if (sessionlock.HardLock(sslsessionfile.c_str())) { + session = PEM_read_SSL_SESSION(fp, NULL, NULL, NULL); + fclose(fp); + sessionlock.HardUnLock(); + } + } + + if (session) { + + DEBUG("info: ("<<__FUNCTION__<<") Session loaded from " << sslsessionfile.c_str()); + char session_id[1024]; + for (int i=0; i< (int)session->session_id_length; i++) { + sprintf(session_id+(i*2),"%02x",session->session_id[i]); + } + + unsigned char buf[5],*p; + unsigned long l; + + p=buf; + l=session->cipher_id; + l2n(l,p); + + DEBUG("Info: ("<<__FUNCTION__<<") Session Id: "<< session_id << " Verify: " << session->verify_result << " (" << X509_verify_cert_error_string(session->verify_result) << ")"); + } else { + DEBUG("info: ("<<__FUNCTION__<<") Session load failed from " << sslsessionfile.c_str()); + ERR_print_errors_fp(stderr); + } + } + } + + clientctx = SSL_CTX_new (meth); + + + SSL_CTX_set_options(clientctx, SSL_OP_ALL | SSL_OP_NO_SSLv2); + + if (!clientctx) { + Fatal(error,"Cannot do SSL_CTX_new",-1); + exit(2); + } + + if (!XrdSecProtocolssl::sslproxyexportplain) { + // set a password callback here + SSL_CTX_set_default_passwd_cb(clientctx, secprotocolssl_pem_cb); + SSL_CTX_set_default_passwd_cb_userdata(clientctx, XrdSecProtocolssl::sslserverexportpassword); + } + + if (SSL_CTX_use_certificate_chain_file(clientctx, sslcertfile) <= 0) { + ERR_print_errors_fp(stderr); + Fatal(error,"Cannot use certificate file",-1); + if (theFD>=0) {close(theFD); theFD=-1;} + if (clientctx) SSL_CTX_free (clientctx); + SSLMutex.UnLock(); + return; + } + + if (SSL_CTX_use_PrivateKey_file(clientctx, sslkeyfile, SSL_FILETYPE_PEM) <= 0) { + ERR_print_errors_fp(stderr); + Fatal(error,"Cannot use private key file",-1); + if (theFD>=0) {close(theFD); theFD=-1;} + if (clientctx) SSL_CTX_free (clientctx); + SSLMutex.UnLock(); + return; + } + + + if (!SSL_CTX_check_private_key(clientctx)) { + fprintf(stderr,"Error: (%s) Private key does not match the certificate public key\n",__FUNCTION__); + Fatal(error,"Private key does not match the certificate public key",-1); + if (theFD>=0) {close(theFD); theFD=-1;} + if (clientctx) SSL_CTX_free (clientctx); + SSLMutex.UnLock(); + return; + } else { + DEBUG("Private key check passed ..."); + } + SSL_CTX_load_verify_locations(clientctx, NULL,sslcadir); + SSL_CTX_set_verify(clientctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, GRST_callback_SSLVerify_wrapper); + + SSL_CTX_set_cert_verify_callback(clientctx, GRST_verify_cert_wrapper, (void *) NULL); + + grst_cadir = sslcadir; + grst_vomsdir = sslvomsdir; + + grst_depth=verifydepth; + SSL_CTX_set_verify_depth(clientctx, verifydepth); + + if (session) { + SSL_CTX_add_session(clientctx,session); + } + + + ssl = SSL_new (clientctx); + SSL_set_purpose(ssl,X509_PURPOSE_ANY); + if (session) { + SSL_set_session(ssl, session); + } + + sessionlock.SoftUnLock(); + sessionlock.HardUnLock(); + + if (!ssl) { + Fatal(error,"Cannot do SSL_new",-1); + exit(6); + } + + SSL_set_fd (ssl, theFD); + err = SSL_connect (ssl); + + if (err!=1) { + // we want to see the error message from the server side + ERR_print_errors_fp(stderr); + if (theFD>=0) {close(theFD);theFD=-1;} + if (clientctx) SSL_CTX_free (clientctx); + SSLMutex.UnLock(); + return; + } + + if (!session) + session = SSL_get1_session(ssl); + + /* Get the cipher - opt */ + + TRACE(Authen,"SSL connection uses cipher: "<<SSL_get_cipher (ssl)); + + /* Get server's certificate (note: beware of dynamic allocation) - opt */ + + server_cert = SSL_get_peer_certificate (ssl); + + if (!server_cert) { + TRACE(Authen,"Server didn't provide certificate"); + } + + XrdOucString rdn; + + str = X509_NAME_oneline (X509_get_subject_name (server_cert),0,0); + rdn = str; + TRACE(Authen,"Server certificate subject:\t" << str); + OPENSSL_free (str); + + str = X509_NAME_oneline (X509_get_issuer_name (server_cert),0,0); + TRACE(Authen,"Server certificate issuer: \t" << str); + OPENSSL_free (str); + + X509_free (server_cert); + server_cert=0; + + + /******************************************/ + /* this is called only to cleanup objects */ + /******************************************/ + + /* get the grst_chain */ + GRSTx509Chain *grst_chain = (GRSTx509Chain*) SSL_get_app_data(ssl); + + if (grst_chain) { + GRST_print_ssl_creds((void*) grst_chain); + char* vr = GRST_get_voms_roles_and_free((void*) grst_chain); + SSL_set_app_data(ssl,0); + if (vr) { + free(vr); + } + } + + + + if (forwardProxy) { + if (!strcmp(sslkeyfile,sslcertfile)) { + // this is a cert & key in one file atleast ... looks like proxy + int fd = open(sslkeyfile,O_RDONLY); + if (fd>=0) { + int nread = read(fd,proxyBuff, sizeof(proxyBuff)); + if (nread>=0) { + TRACE(Authen,"Uploading my Proxy ...\n"); + err = SSL_write(ssl, proxyBuff,nread); + if (err!= nread) { + Fatal(error,"Cannot forward proxy",-1); + if (theFD>=0) {close(theFD); theFD=-1;} + if (clientctx) SSL_CTX_free (clientctx); + if (session) SSL_SESSION_free(session); + SSLMutex.UnLock(); + return; + } + char ok[16]; + err = SSL_read(ssl,ok, 3); + if (err != 3) { + Fatal(error,"Didn't receive OK",-1); + if (theFD>=0) {close(theFD); theFD=-1;} + if (clientctx) SSL_CTX_free (clientctx); + if (session) SSL_SESSION_free(session); + SSLMutex.UnLock(); + return; + } + } else { + close(fd); + Fatal(error,"Cannot read proxy file to forward",-1); + if (theFD>=0) {close(theFD);theFD=-1;} + if (clientctx) SSL_CTX_free (clientctx); + if (session) SSL_SESSION_free(session); + SSLMutex.UnLock(); + return; + } + } else { + Fatal(error,"Cannot read proxy file to forward",-1); + if (theFD>=0) {close(theFD);theFD=-1;} + if (clientctx) SSL_CTX_free (clientctx); + if (session) SSL_SESSION_free(session); + SSLMutex.UnLock(); + return; + } + close(fd); + } + } + + if (allowSessions && session) { + char session_id[1024]; + for (int i=0; i< (int)session->session_id_length; i++) { + sprintf(session_id+(i*2),"%02x",session->session_id[i]); + } + + if (session->cipher) { + DEBUG("Info: ("<<__FUNCTION__<<") Session Id: "<< session_id << " Cipher: " << session->cipher->name << " Verify: " << session->verify_result << " (" << X509_verify_cert_error_string(session->verify_result) << ")"); + } else { + DEBUG("Info: ("<<__FUNCTION__<<") Session Id: "<< session_id << " Verify: " << session->verify_result << " (" << X509_verify_cert_error_string(session->verify_result) << ")"); + } + // write out the session + FILE* fp = fopen((const char*)(sslsessionfile.c_str()),"w+"); + if (fp) { + if (sessionlock.HardLock(sslsessionfile.c_str())) { + PEM_write_SSL_SESSION(fp, session); + } + fclose(fp); + sessionlock.HardUnLock(); + DEBUG("info: ("<<__FUNCTION__<<") Session stored to " << sslsessionfile.c_str()); + } + } + + while (!SSL_shutdown(ssl)) { + SSL_shutdown(ssl); + } + + if (ssl) { + SSL_free(ssl);ssl = 0; + } + + if (clientctx) SSL_CTX_free (clientctx); + + if (theFD>=0) {close(theFD);theFD=-1; + } + + if (session) SSL_SESSION_free(session); + + SSLMutex.UnLock(); + return; +} + +/******************************************************************************/ +/* S e r v e r O r i e n t e d M e t h o d s */ +/******************************************************************************/ + + + +/*----------------------------------------------------------------------------*/ +/* this helps to avoid memory leaks by strdup */ +/* we maintain a string hash to keep all used user ids/group ids etc. */ + +char* +STRINGSTORE(const char* __charptr__) { + XrdOucString* yourstring; + if (!__charptr__ ) return (char*)""; + + if ((yourstring = XrdSecProtocolssl::stringstore.Find(__charptr__))) { + return (char*)yourstring->c_str(); + } else { + XrdOucString* newstring = new XrdOucString(__charptr__); + XrdSecProtocolssl::StoreMutex.Lock(); + XrdSecProtocolssl::stringstore.Add(__charptr__,newstring); + XrdSecProtocolssl::StoreMutex.UnLock(); + return (char*)newstring->c_str(); + } +} + +/*----------------------------------------------------------------------------*/ +void MyGRSTerrorLogFunc (char *lfile, int lline, int llevel, char *fmt, ...) { + EPNAME("grst"); + va_list args; + char fullmessage[4096]; + fullmessage[0] = 0; + + va_start(args, fmt); + vsprintf(fullmessage,fmt,args); + va_end(args); + + // just remove linefeeds + XrdOucString sfullmessage = fullmessage; + sfullmessage.replace("\n",""); + + if (llevel <= GRST_LOG_WARNING) { + TRACE(Authen," ("<< lfile << ":" << lline <<"): " << sfullmessage); + } else if (llevel <= GRST_LOG_INFO) { + TRACE(Authen, " ("<< lfile << ":" << lline <<"): " << sfullmessage); + } else { + DEBUG(" ("<< lfile << ":" << lline <<"): " << sfullmessage); + } +} + +/*----------------------------------------------------------------------------*/ +void +XrdSecProtocolssl::secServer(int theFD, XrdOucErrInfo *error) { + int err; + + char* str; + + EPNAME("secServer"); + + XrdSecsslSessionLock sessionlock; + + // check if we should reload the store + if ((time(NULL)-storeLoadTime) > 3600) { + if (store) { + TRACE(Authen,"Reloading X509 Store from " << sslcadir); + X509_STORE_free(store); + store = SSL_X509_STORE_create(NULL, sslcadir); + X509_STORE_set_flags(XrdSecProtocolssl::store,0); + storeLoadTime = time(NULL); + } + } + + SSLMutex.Lock(); + + // SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH); // enable autoclear every 255 connections | SSL_SESS_CACHE_NO_AUTO_CLEAR ); + SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH | SSL_SESS_CACHE_NO_AUTO_CLEAR); + + ssl = SSL_new (ctx); + SSL_set_purpose(ssl,X509_PURPOSE_ANY); + + + TRACE(Authen,"Info: ("<<__FUNCTION__<<") Session Cache has size: " <<SSL_CTX_sess_get_cache_size(ctx)); + + if (!ssl) { + fprintf(stderr,"Error: (%s) failed to create context\n",__FUNCTION__); + TRACE(Authen,"Error: ("<<__FUNCTION__<<") failed to create context"); + exit(5); + } + + SSL_set_fd (ssl, theFD); + + // the accept run's without mutex + err = SSL_accept (ssl); + + if (err!=1) { + long verifyresult = SSL_get_verify_result(ssl); + if (verifyresult != X509_V_OK) { + Fatal(error,X509_verify_cert_error_string(verifyresult),verifyresult); + TRACE(Authen,"Error: ("<<__FUNCTION__<<") failed SSL_accept "); + } else { + Fatal(error,"do SSL_accept",-1); + unsigned long lerr; + while ((lerr=ERR_get_error())) {TRACE(Authen,"SSL Queue error: err=" << lerr << " msg=" << + ERR_error_string(lerr, NULL));Fatal(error,ERR_error_string(lerr,NULL),-1);} + } + if (theFD>=0) {close(theFD); theFD=-1;} + SSLMutex.UnLock(); + return; + } + + SSL_SESSION* session = SSL_get_session(ssl); + + if (session) { + char session_id[1024]; + for (int i=0; i< (int)session->session_id_length; i++) { + sprintf(session_id+(i*2),"%02x",session->session_id[i]); + } + + DEBUG("Info: ("<<__FUNCTION__<<") Session Id: "<< session_id << " Verify: " << session->verify_result << " (" << X509_verify_cert_error_string(session->verify_result) << ")"); + DEBUG("Info: ("<<__FUNCTION__<<") cache items : " << SSL_CTX_sess_number(ctx)); + DEBUG("Info: ("<<__FUNCTION__<<") client connects : " << SSL_CTX_sess_connect(ctx)); + DEBUG("Info: ("<<__FUNCTION__<<") client renegotiates : " << SSL_CTX_sess_connect_renegotiate(ctx)); + DEBUG("Info: ("<<__FUNCTION__<<") client connect finished : " << SSL_CTX_sess_connect_good(ctx)); + DEBUG("Info: ("<<__FUNCTION__<<") server accepts : " << SSL_CTX_sess_accept(ctx)); + DEBUG("Info: ("<<__FUNCTION__<<") server renegotiates : " << SSL_CTX_sess_accept_renegotiate(ctx)); + DEBUG("Info: ("<<__FUNCTION__<<") server accepts finished : " << SSL_CTX_sess_accept_good(ctx)); + DEBUG("Info: ("<<__FUNCTION__<<") session cache hits : " << SSL_CTX_sess_hits(ctx)); + DEBUG("Info: ("<<__FUNCTION__<<") session cache misses : " << SSL_CTX_sess_misses(ctx)); + DEBUG("Info: ("<<__FUNCTION__<<") session cache timeouts : " << SSL_CTX_sess_timeouts(ctx)); + DEBUG("Info: ("<<__FUNCTION__<<") callback cache hits : " << SSL_CTX_sess_cb_hits(ctx)); + DEBUG("Info: ("<<__FUNCTION__<<") cache full overflows : " << SSL_CTX_sess_cache_full(ctx) << " allowed: " << SSL_CTX_sess_get_cache_size(ctx)); + + } + /* get the grst_chain */ + GRSTx509Chain *grst_chain = (GRSTx509Chain*) SSL_get_app_data(ssl); + + XrdOucString vomsroles=""; + XrdOucString clientdn=""; + + if (grst_chain) { + GRST_print_ssl_creds((void*) grst_chain); + char* vr = GRST_get_voms_roles_and_free((void*) grst_chain); + SSL_set_app_data(ssl,0); + if (vr) { + vomsroles = vr; + free(vr); + } + } + + TRACE(Authen,"Authenticated with VOMS roles: "<<vomsroles); + + long verifyresult = SSL_get_verify_result(ssl); + + TRACE(Authen,"Verify result is = "<<verifyresult); + + /* Get the cipher - opt */ + + DEBUG("SSL connection uses cipher " << SSL_get_cipher(ssl)); + + /* Get client's certificate (note: beware of dynamic allocation) - opt */ + + client_cert = SSL_get_peer_certificate (ssl); + + + if (client_cert != NULL) { + str = X509_NAME_oneline (X509_get_subject_name (client_cert), 0, 0); + if (str) { + TRACE(Authen,"client certificate subject: "<< str); + clientdn = str; + OPENSSL_free (str); + } else { + TRACE(Authen,"client certificate subject: none"); + } + + str = X509_NAME_oneline (X509_get_issuer_name (client_cert), 0, 0); + + if (str) { + TRACE(Authen,"client certificate issuer : "<<str); + TRACE(Authen,"Setting dn="<<clientdn<<" roles="<<vomsroles); + OPENSSL_free (str); + } else { + TRACE(Authen,"client certificate issuer : none"); + Fatal(error,"no client issuer",-1); + if (theFD>=0) {close(theFD);theFD=-1;} + SSLMutex.UnLock(); + return; + } + } else { + TRACE(Authen,"Client does not have certificate."); + Fatal(error,"no client certificate",-1); + if (theFD>=0) {close(theFD);theFD=-1;} + SSLMutex.UnLock(); + return; + } + + // receive client proxy - if he send's one + err = SSL_read(ssl,proxyBuff, sizeof(proxyBuff)); + if (err>0) { + TRACE(Authen,"Received proxy buffer with " << err << " bytes"); + proxyBuff[err] = 0; //0 terminate the proxy buffer + Entity.endorsements = proxyBuff; + err = SSL_write(ssl,"OK\n",3); + if (err!=3) { + Fatal(error,"could not send end of handshake OK",-1); + if (theFD>=0) {close(theFD);theFD=-1;} + SSLMutex.UnLock(); + return; + } + // pseudo read to let the client close the connection + char dummy[1]; + err = SSL_read(ssl,dummy, sizeof(dummy)); + } else { + TRACE(Authen,"Received no proxy"); + } + + //while (!SSL_shutdown(ssl)) { + SSL_shutdown(ssl); + // } + + strncpy(Entity.prot,"ssl", sizeof(Entity.prot)); + + /*----------------------------------------------------------------------------*/ + /* mapping interface */ + /*----------------------------------------------------------------------------*/ + + if (!mapuser && !mapcerncertificates) { + // no mapping put the DN + Entity.name = strdup(clientdn.c_str()); + } else { + bool mapped=false; + // map user from grid map file + if (mapcerncertificates) { + // map from CERN DN + if ( (mapcerncertificates) && (clientdn.beginswith("/DC=ch/DC=cern/OU=Organic Units/OU=Users/CN="))) { + XrdOucString certsubject = clientdn; + certsubject.erasefromstart(strlen("/DC=ch/DC=cern/OU=Organic Units/OU=Users/CN=")); + int pos=certsubject.find('/'); + if (pos != STR_NPOS) + certsubject.erase(pos); + Entity.name = strdup(certsubject.c_str()); + mapped=true; + TRACE(Authen,"Found CERN certificate - mapping to AFS account " << certsubject); + } + } + if (!mapped) { + if (mapuser) { + // treatment of old proxy + XrdOucString certsubject = clientdn; + certsubject.replace("/CN=proxy",""); + // treatment of new proxy - leave only the first CN=, cut the rest + int pos = certsubject.find("CN="); + int pos2 = certsubject.find("/",pos); + if (pos2>0) certsubject.erase(pos2); + XrdOucString* gridmaprole; + ReloadGridMapFile(); + GridMapMutex.Lock(); + + if ((gridmaprole = gridmapstore.Find(certsubject.c_str()))) { + Entity.name = strdup(gridmaprole->c_str()); + Entity.role = 0; + } else { + Entity.name = strdup((char*)"nobody"); + Entity.role = 0; + if (!XrdSecProtocolssl::mapnobody) { + Fatal(error,"user cannot be mapped",-1); + } + } + GridMapMutex.UnLock(); + } else { + Entity.name = strdup((char*)"nobody"); + Entity.role = 0; + if (!XrdSecProtocolssl::mapnobody) { + Fatal(error,"user cannot be mapped",-1); + } + } + } + } + + + if (!mapgroup) { + if (vomsroles.length()) { + // no mapping put the VOMS groups and role + Entity.grps = strdup(vomsroles.c_str()); + + XrdOucString vomsrole = vomsroles.c_str(); + + if (vomsroles.length()) { + int dp = vomsrole.find(":"); + if (dp != STR_NPOS) { + vomsrole.assign(vomsroles,0,dp-1); + } + Entity.role = strdup(vomsrole.c_str()); + } else { + Entity.role = strdup(""); + } + } else { + // map the group from the passwd/group file + struct passwd* pwd; + struct group* grp; + StoreMutex.Lock(); + if ( (pwd = getpwnam(Entity.name)) && (grp = getgrgid(pwd->pw_gid))) { + Entity.grps = strdup(grp->gr_name); + Entity.role = strdup(grp->gr_name); + } + StoreMutex.UnLock(); + } + } else { + // map groups & role from VOMS mapfile + XrdOucString defaultgroup=""; + XrdOucString allgroups=""; + if (VomsMapGroups(vomsroles.c_str(), allgroups,defaultgroup)) { + if (!strcmp(allgroups.c_str(),":")) { + // map the group from the passwd/group file + struct passwd* pwd; + struct group* grp; + StoreMutex.Lock(); + if ( (pwd = getpwnam(Entity.name)) && (grp = getgrgid(pwd->pw_gid))) { + allgroups = grp->gr_name; + defaultgroup = grp->gr_name; + } + StoreMutex.UnLock(); + } + Entity.grps = strdup(allgroups.c_str()); + Entity.role = strdup(defaultgroup.c_str()); + } else { + Fatal(error,"incomplete VOMS mapping",-1); + } + } + + /*----------------------------------------------------------------------------*/ + /* proxy forwarding */ + /*----------------------------------------------------------------------------*/ + + if (sslproxyexportdir && Entity.endorsements) { + StoreMutex.Lock(); + // get the UID of the entity name + struct passwd* pwd; + XrdOucString outputproxy = sslproxyexportdir; outputproxy+="/x509up_u"; + if ( (pwd = getpwnam(Entity.name)) ) { + outputproxy += (int)pwd->pw_uid; + } else { + outputproxy += Entity.name; + } + XrdOucString outputproxytmp = outputproxy; + outputproxytmp += (int) rand(); + + if (XrdSecProtocolssl::sslproxyexportplain) { + int fd = open (outputproxytmp.c_str(),O_CREAT| O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR); + if (fd>0) { + if ( ((int)write(fd,Entity.endorsements,strlen(Entity.endorsements))) != (int)strlen(Entity.endorsements)) { + unlink(outputproxytmp.c_str()); + Fatal(error,"cannot export(write) user proxy",-1); + } else { + TRACE(Identity,"Exported proxy buffer of " << Entity.name << " to file " << outputproxy.c_str()); + } + if ( rename(outputproxytmp.c_str(),outputproxy.c_str()) ) { + unlink(outputproxytmp.c_str()); + Fatal(error,"cannot rename temporary export proxy",-1); + } + close(fd); + } else { + Fatal(error,"cannot export(open) user proxy",-1); + } + } else { + EVP_PKEY* pkey=NULL; + X509* x509=NULL; + // we protect the private key with our session password + BIO* bp = BIO_new_mem_buf( (void *)Entity.endorsements, strlen(Entity.endorsements)+1); + FILE* fout = fopen (outputproxytmp.c_str(),"w+"); + if (!fout) { + Fatal(error,"cannot export user proxy - unable to open proxy file",-1); + } else { + if (bp) { + pkey = PEM_read_bio_PrivateKey(bp, &pkey,0,0); + BIO_free(bp); + if (!pkey) { + Fatal(error,"cannot export user proxy - unable to read key/cert from BIO",-1); + } else { + int wk = PEM_write_PrivateKey(fout, pkey, EVP_des_ede3_cbc(),(unsigned char*)XrdSecProtocolssl::sslserverexportpassword,EXPORTKEYSTRENGTH,0,0); + EVP_PKEY_free(pkey); + + if (!wk) { + Fatal(error,"cannot export user proxy - unable to write private key",-1); + } else { + // deal with the certificates + char* certificatebuffer = 0; + certificatebuffer = Entity.endorsements; + while ((certificatebuffer = strstr(certificatebuffer,"-----BEGIN CERTIFICATE-----"))) { + // we point to the next certificate to export in memory + BIO* bp = BIO_new_mem_buf( (void *)certificatebuffer, strlen(certificatebuffer)+1); + if (bp) { + x509 = NULL; + x509 = PEM_read_bio_X509(bp, &x509,0,0); + BIO_free(bp); + if (x509) { + int wc = PEM_write_X509(fout,x509); + X509_free(x509); + if (!wc) { + Fatal(error,"cannto export user proxy - unable to write certificate",-1); + break; + } + } + } else { + Fatal(error,"cannot export user proxy - unable to allocate BIO to read private key",-1); + } + certificatebuffer++; + } + } + } + } else { + Fatal(error,"cannot export user proxy - unable to allocate BIO to read private key",-1); + } + + fclose(fout); + if ( rename(outputproxytmp.c_str(),outputproxy.c_str()) ) { + unlink(outputproxytmp.c_str()); + Fatal(error,"cannot rename temporary export proxy",-1); + } + } + } + + StoreMutex.UnLock(); + } + + + TRACE(Identity,"[usermapping] name=|" << Entity.name << "| role=|" << (Entity.role?Entity.role:"-") << "| grps=|"<< (Entity.grps?Entity.grps:"-") << "| DN=|" << clientdn.c_str() << "| VOMS=|" << vomsroles.c_str() << "|"); + + if (ssl) { + SSL_free(ssl);ssl = 0; + } + + if (theFD>=0) { + close(theFD); + } + + SSLMutex.UnLock(); + return; +} + +int +XrdSecProtocolssl::GenerateSession(const SSL* ssl, unsigned char *id, unsigned int *id_len) { + EPNAME("GenerateSession"); + unsigned int count = 0; + do { + RAND_pseudo_bytes(id, *id_len); + /* Prefix the session_id with the required prefix. NB: If our + * prefix is too long, clip it - but there will be worse effects + * anyway, eg. the server could only possibly create 1 session + * ID (ie. the prefix!) so all future session negotiations will + * fail due to conflicts. */ + memcpy(id, "xrootdssl", + (strlen("xrootdssl") < *id_len) ? + strlen("xrootdssl") : *id_len); + TRACE(Authen,"Generated SSID **********************"); + } + while(SSL_has_matching_session_id(ssl, id, *id_len) && + (++count < MAX_SESSION_ID_ATTEMPTS)); + if(count >= MAX_SESSION_ID_ATTEMPTS) + return 0; + return 1; +} + +int +XrdSecProtocolssl::NewSession(SSL* ssl, SSL_SESSION *session) { + EPNAME("NewSession"); + TRACE(Authen,"Creating new Session"); + char session_id[1024]; + for (int i=0; i< (int)session->session_id_length; i++) { + sprintf(session_id+(i*2),"%02x",session->session_id[i]); + } + DEBUG("Info: ("<<__FUNCTION__<<") Session Id: "<< session_id << " Verify: " << session->verify_result << " (" << X509_verify_cert_error_string(session->verify_result) << ")"); + + SSL_set_timeout(session, sslsessionlifetime); + return 0; +} + +/*----------------------------------------------------------------------------*/ +void +XrdSecProtocolssl::ReloadGridMapFile() +{ + EPNAME("ReloadGridMapFile"); + + static time_t GridMapMtime=0; + static time_t GridMapCheckTime=0; + int now = time(NULL); + + if ((!GridMapCheckTime) || ((now >GridMapCheckTime + 60)) ) { + // load it for the first time or again + struct stat buf; + if (!::stat(gridmapfile,&buf)) { + if (buf.st_mtime != GridMapMtime) { + GridMapMutex.Lock(); + // store the last modification time + GridMapMtime = buf.st_mtime; + // store the current time of the check + GridMapCheckTime = now; + // dump the current table + gridmapstore.Purge(); + // open the gridmap file + FILE* mapin = fopen(gridmapfile,"r"); + if (!mapin) { + // error no grid map possible + TRACE(Authen,"Unable to open gridmapfile " << XrdOucString(gridmapfile) << " - no mapping!"); + } else { + char userdnin[4096]; + char usernameout[4096]; + int nitems; + // parse it + while ( (nitems = fscanf(mapin,"\"%[^\"]\" %s\n", userdnin,usernameout)) == 2) { + XrdOucString dn = userdnin; + dn.replace("\"",""); + // leave only the first CN=, cut the rest + int pos = dn.find("CN="); + int pos2 = dn.find("/",pos); + if (pos2>0) dn.erase(pos2); + + if (!gridmapstore.Find(dn.c_str())) { + gridmapstore.Add(dn.c_str(), new XrdOucString(usernameout)); + TRACE(Authen, "gridmapfile Mapping Added: " << dn.c_str() << " |=> " << usernameout); + } + } + fclose(mapin); + } + GridMapMutex.UnLock(); + } else { + // the file didn't change, we don't do anything + } + } else { + TRACE(Authen,"Unable to stat gridmapfile " << XrdOucString(gridmapfile) << " - no mapping!"); + } + } +} + +/*----------------------------------------------------------------------------*/ + +void +XrdSecProtocolssl::ReloadVomsMapFile() +{ + EPNAME("ReloadVomsMapFile"); + + static time_t VomsMapMtime=0; + static time_t VomsMapCheckTime=0; + int now = time(NULL); + + if ((!VomsMapCheckTime) || ((now >VomsMapCheckTime + 60 )) ) { + // load it for the first time or again + struct stat buf; + if (!::stat(vomsmapfile,&buf)) { + if (buf.st_mtime != VomsMapMtime) { + VomsMapMutex.Lock(); + // store the last modification time + VomsMapMtime = buf.st_mtime; + // store the current time of the check + VomsMapCheckTime = now; + // dump the current table + vomsmapstore.Purge(); + // open the vomsmap file + FILE* mapin = fopen(vomsmapfile,"r"); + if (!mapin) { + // error no voms map possible + TRACE(Authen,"Unable to open vomsmapfile " << XrdOucString(vomsmapfile) << " - no mapping!"); + } else { + char userdnin[4096]; + char usernameout[4096]; + int nitems; + // parse it + while ( (nitems = fscanf(mapin,"\"%[^\"]\" %s\n", userdnin,usernameout)) == 2) { + XrdOucString dn = userdnin; + dn.replace("\"",""); + if (!vomsmapstore.Find(dn.c_str())) { + vomsmapstore.Add(dn.c_str(), new XrdOucString(usernameout)); + TRACE(Authen,"vomsmapfile Mapping Added: " << dn.c_str() << " |=> " << usernameout); + } + } + fclose(mapin); + } + VomsMapMutex.UnLock(); + } else { + // the file didn't change, we don't do anything + } + } else { + TRACE(Authen,"Unable to stat vomsmapfile " << XrdOucString(vomsmapfile) << " - no mapping!"); + } + } +} + +/*----------------------------------------------------------------------------*/ + +bool +XrdSecProtocolssl::VomsMapGroups(const char* groups, XrdOucString& allgroups, XrdOucString& defaultgroup) +{ + EPNAME("VomsMapGroups"); + // loop over all VOMS groups and replace them according to the mapping + XrdOucString vomsline = groups; + allgroups = ":"; + defaultgroup = ""; + vomsline.replace(":","\n"); + XrdOucTokenizer vomsgroups((char*)vomsline.c_str()); + const char* stoken; + int ntoken=0; + XrdOucString* vomsmaprole; + while( (stoken = vomsgroups.GetLine())) { + if ((vomsmaprole = XrdSecProtocolssl::vomsmapstore.Find(stoken))) { + allgroups += vomsmaprole->c_str(); + allgroups += ":"; + if (ntoken == 0) { + defaultgroup = vomsmaprole->c_str(); + } + ntoken++; + } else { + TRACE(Authen,"No VOMS mapping found for " << XrdOucString(stoken)); + return false; + } + } + return true; +} + +/******************************************************************************/ +/* X r d S e c p r o t o c o l u n i x I n i t */ +/******************************************************************************/ + +extern "C" +{ +char *XrdSecProtocolsslInit(const char mode, + const char *parms, + XrdOucErrInfo *erp) +{ + EPNAME("ProtocolsslInit"); + // Initiate error logging and tracing + XrdSecProtocolssl::ssleDest.logger(&XrdSecProtocolssl::Logger); + + GRSTerrorLogFunc = &MyGRSTerrorLogFunc; + static bool serverinitialized = false; + + // create the tracer + if (!SSLxTrace) + SSLxTrace = new XrdOucTrace(&XrdSecProtocolssl::ssleDest); + + for (int i=0; i< PROTOCOLSSL_MAX_CRYPTO_MUTEX; i++) { + XrdSecProtocolssl::CryptoMutexPool[i] = new XrdSysMutex(); + } + + + // read the configuration options + if ( (mode == 's') && (!serverinitialized) ) { + XrdSecProtocolssl::sslcertfile = strdup("/etc/grid-security/hostcert.pem"); + XrdSecProtocolssl::sslkeyfile = strdup("/etc/grid-security/hostkey.pem"); + XrdSecProtocolssl::sslcadir = strdup("/etc/grid-security/certificates"); + XrdSecProtocolssl::sslvomsdir = (char*)"/etc/grid-security/vomsdir"; + + XrdSecProtocolssl::isServer = 1; + serverinitialized = true; + if (parms){ + // Duplicate the parms + char parmbuff[1024]; + strlcpy(parmbuff, parms, sizeof(parmbuff)); + // + // The tokenizer + XrdOucTokenizer inParms(parmbuff); + char *op; + + while (inParms.GetLine()) { + while ((op = inParms.GetToken())) { + if (!strncmp(op, "-d:",3)) { + XrdSecProtocolssl::debug = atoi(op+3); + } else if (!strncmp(op, "-cadir:",7)) { + XrdSecProtocolssl::sslcadir = strdup(op+7); + } else if (!strncmp(op, "-vomsdir:",6)) { + XrdSecProtocolssl::sslvomsdir = strdup(op+6); + } else if (!strncmp(op, "-cert:",6)) { + XrdSecProtocolssl::sslcertfile = strdup(op+6); + } else if (!strncmp(op, "-key:",5)) { + XrdSecProtocolssl::sslkeyfile = strdup(op+5); + } else if (!strncmp(op, "-ca:",4)) { + XrdSecProtocolssl::verifydepth = atoi(op+4); + } else if (!strncmp(op, "-t:",3)) { + XrdSecProtocolssl::sslsessionlifetime = atoi(op+3); + } else if (!strncmp(op, "-export:",8)) { + XrdSecProtocolssl::sslproxyexportdir = strdup(op+8); + } else if (!strncmp(op, "-gridmapfile:",13)) { + XrdSecProtocolssl::gridmapfile = strdup(op+13); + } else if (!strncmp(op, "-vomsmapfile:",13)) { + XrdSecProtocolssl::vomsmapfile = strdup(op+13); + } else if (!strncmp(op, "-mapuser:",9)) { + XrdSecProtocolssl::mapuser = (bool) atoi(op+9); + } else if (!strncmp(op, "-mapnobody:",11)) { + XrdSecProtocolssl::mapnobody = (bool) atoi(op+11); + } else if (!strncmp(op, "-mapgroup:",10)) { + XrdSecProtocolssl::mapgroup = (bool) atoi(op+10); + } else if (!strncmp(op, "-mapcernuser:",13)) { + XrdSecProtocolssl::mapcerncertificates = (bool) atoi(op+13); + } + } + } + } + } else { + if ( (mode == 'c') || (serverinitialized)) { + if (mode == 'c') { + for (int i=0; i< PROTOCOLSSL_MAX_CRYPTO_MUTEX; i++) { + XrdSecProtocolssl::CryptoMutexPool[i] = 0; + } + XrdSecProtocolssl::sslcertfile = strdup("/etc/grid-security/hostcert.pem"); + XrdSecProtocolssl::sslkeyfile = strdup("/etc/grid-security/hostkey.pem"); + XrdSecProtocolssl::sslcadir = strdup("/etc/grid-security/certificates"); + XrdSecProtocolssl::sslvomsdir = (char*)"/etc/grid-security/vomsdir"; + } + XrdSecProtocolssl::GetEnvironment(); + XrdSecProtocolssl::isServer = 0; + if (serverinitialized) { + XrdSecProtocolssl::sslproxyexportplain = 0; + } + } + } + + if (XrdSecProtocolssl::debug >= 4) { + SSLxTrace->What = TRACE_ALL | TRACE_Debug; + } else if (XrdSecProtocolssl::debug == 3 ) { + SSLxTrace->What |= TRACE_Authen; + SSLxTrace->What |= TRACE_Debug; + SSLxTrace->What |= TRACE_Identity; + } else if (XrdSecProtocolssl::debug == 2) { + SSLxTrace->What = TRACE_Debug; + } else if (XrdSecProtocolssl::debug == 1) { + SSLxTrace->What = TRACE_Identity; + } else SSLxTrace->What = 0; + + // thread-saftey + if (PROTOCOLSSL_MAX_CRYPTO_MUTEX < CRYPTO_num_locks() ) { + fprintf(stderr,"Error: (%s) I don't have enough crypto mutexes as required by crypto_ssl [recompile increasing PROTOCOLSSL_MAX_CRYPTO_MUTEX to %d] \n",__FUNCTION__,CRYPTO_num_locks()); + TRACE(Authen,"Error: I don't have enough crypto mutexes as required by crypto_ssl [recompile increasing PROTOCOLSSL_MAX_CRYPTO_MUTEX to " << (int)CRYPTO_num_locks() << "]"); + } else { + TRACE(Authen,"====> SSL requires " << (int)CRYPTO_num_locks() << " mutexes for thread-safety"); + } + +#if defined(OPENSSL_THREADS) + // thread support enabled + TRACE(Authen,"====> SSL with thread support!"); +#else + fprintf(stderr,"Error: (%s) SSL lacks thread support: Abort!"); + TRACE(Authen,"Error: SSL lacks thread support: Abort!"); +#endif + + // set callback functions + CRYPTO_set_locking_callback(protocolssl_lock); + CRYPTO_set_id_callback(protocolssl_id_callback); + + + + + if (XrdSecProtocolssl::isServer) { + TRACE(Authen,"====> debug = " << XrdSecProtocolssl::debug); + TRACE(Authen,"====> cadir = " << XrdSecProtocolssl::sslcadir); + TRACE(Authen,"====> keyfile = " << XrdSecProtocolssl::sslkeyfile); + TRACE(Authen,"====> certfile = " << XrdSecProtocolssl::sslcertfile); + TRACE(Authen,"====> verify depth = " << XrdSecProtocolssl::verifydepth); + TRACE(Authen,"====> sess.lifetime = " << XrdSecProtocolssl::sslsessionlifetime); + TRACE(Authen,"====> gridmapfile = " << XrdSecProtocolssl::gridmapfile); + TRACE(Authen,"====> vomsmapfile = " << XrdSecProtocolssl::vomsmapfile); + TRACE(Authen,"====> mapuser = " << XrdSecProtocolssl::mapuser); + TRACE(Authen,"====> mapnobody = " << XrdSecProtocolssl::mapnobody); + TRACE(Authen,"====> mapgroup = " << XrdSecProtocolssl::mapgroup); + TRACE(Authen,"====> mapcernuser = " << XrdSecProtocolssl::mapcerncertificates); + } else { + if (XrdSecProtocolssl::debug) { + TRACE(Authen,"====> debug = " << XrdSecProtocolssl::debug); + TRACE(Authen,"====> cadir = " << XrdSecProtocolssl::sslcadir); + TRACE(Authen,"====> keyfile = " << XrdSecProtocolssl::sslkeyfile); + TRACE(Authen,"====> certfile = " << XrdSecProtocolssl::sslcertfile); + TRACE(Authen,"====> verify depth = " << XrdSecProtocolssl::verifydepth); + } + } + + if (XrdSecProtocolssl::isServer) { + XrdSecProtocolssl::sslproxyexportplain=0; // for security reasons a server should not export plain private keys + // check if we can map with a grid map file + if (XrdSecProtocolssl::mapuser && access(XrdSecProtocolssl::gridmapfile,R_OK)) { + fprintf(stderr,"Error: (%s) cannot access gridmapfile %s\n",__FUNCTION__,XrdSecProtocolssl::gridmapfile); + TRACE(Authen,"Error: cannot access gridmapfile "<< XrdOucString(XrdSecProtocolssl::gridmapfile)); + return 0; + } + // check if we can map with a voms map file + if (XrdSecProtocolssl::mapgroup && access(XrdSecProtocolssl::vomsmapfile,R_OK)) { + fprintf(stderr,"Error: (%s) cannot access vomsmapfile %s\n",__FUNCTION__,XrdSecProtocolssl::vomsmapfile); + TRACE(Authen,"Error: cannot access vomsmapfile "<< XrdOucString(XrdSecProtocolssl::vomsmapfile)); + return 0; + } + // check if we can export proxies + XrdOucString exportplain=XrdSecProtocolssl::sslproxyexportdir; + // if the export file starts with plain: we don't write the proxy with a passphrase + if (exportplain.beginswith("plain:")) { + XrdSecProtocolssl::sslproxyexportdir+=6; + XrdSecProtocolssl::sslproxyexportplain=true; + TRACE(Authen,"====> export plain proxy (warning: can be re-used out of daemon context) to dir: " << XrdSecProtocolssl::sslproxyexportdir); + } + if (XrdSecProtocolssl::sslproxyexportdir && access(XrdSecProtocolssl::sslproxyexportdir,R_OK | W_OK)) { + fprintf(stderr,"Error: (%s) cannot read/write proxy export directory %s\n",__FUNCTION__,XrdSecProtocolssl::sslproxyexportdir); + TRACE(Authen,"Error: cannot access proxyexportdir "<< XrdOucString(XrdSecProtocolssl::sslproxyexportdir)); + return 0; + } + } + + if (XrdSecProtocolssl::isServer) { + SSL_METHOD *meth; + // initialize openssl until the context is created + SSL_load_error_strings(); + SSLeay_add_ssl_algorithms(); + + meth = (SSL_METHOD*)SSLv23_server_method(); + + XrdSecProtocolssl::ctx = SSL_CTX_new (meth); + if (!XrdSecProtocolssl::ctx) { + ERR_print_errors_fp(stderr); + return 0; + } + + if (SSL_CTX_use_certificate_file(XrdSecProtocolssl::ctx, XrdSecProtocolssl::sslcertfile, SSL_FILETYPE_PEM) <= 0) { + ERR_print_errors_fp(stderr); + return 0; + } + + if (SSL_CTX_use_PrivateKey_file(XrdSecProtocolssl::ctx,XrdSecProtocolssl::sslkeyfile, SSL_FILETYPE_PEM) <= 0) { + ERR_print_errors_fp(stderr); + return 0; + } + + if (!SSL_CTX_check_private_key(XrdSecProtocolssl::ctx)) { + fprintf(stderr,"Private key does not match the certificate public key\n"); + return 0; + } + + XrdSecProtocolssl::sslserverkeyfile=XrdSecProtocolssl::sslkeyfile; + // use the private server key as password for proxy private key export + memset(XrdSecProtocolssl::sslserverexportpassword,0,EXPORTKEYSTRENGTH); + + unsigned int seed = (unsigned int) (time(NULL) + (unsigned int) random()); + srand(seed); + char rexportkey[16384]; + rexportkey[0]=0; + for (int i=0; i < EXPORTKEYSTRENGTH; i++) { + XrdSecProtocolssl::sslserverexportpassword[i] = (unsigned char)(rand()%256); + if (!XrdSecProtocolssl::sslserverexportpassword[i]) XrdSecProtocolssl::sslserverexportpassword[i]++; + sprintf(rexportkey,"%s%x",rexportkey,XrdSecProtocolssl::sslserverexportpassword[i]); + } + XrdSecProtocolssl::sslserverexportpassword[EXPORTKEYSTRENGTH] = 0; + sprintf((char*)XrdSecProtocolssl::sslserverexportpassword,"1234567890"); + // debug + DEBUG("Created random export key: "<< rexportkey); + SSL_CTX_load_verify_locations(XrdSecProtocolssl::ctx, NULL,XrdSecProtocolssl::sslcadir); + + // create the store + if (!XrdSecProtocolssl::store) { + DEBUG("Created SSL CRL store: " << XrdSecProtocolssl::store); + XrdSecProtocolssl::store = SSL_X509_STORE_create(NULL,XrdSecProtocolssl::sslcadir); + X509_STORE_set_flags(XrdSecProtocolssl::store,0); + XrdSecProtocolssl::storeLoadTime = time(NULL); + } + + XrdSecProtocolssl::ctx->verify_mode = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE|SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + + grst_cadir = XrdSecProtocolssl::sslcadir; + grst_vomsdir = XrdSecProtocolssl::sslvomsdir; + grst_depth = XrdSecProtocolssl::verifydepth; + + SSL_CTX_set_cert_verify_callback(XrdSecProtocolssl::ctx, + GRST_verify_cert_wrapper, + (void *) NULL); + + SSL_CTX_set_verify(XrdSecProtocolssl::ctx, XrdSecProtocolssl::ctx->verify_mode,GRST_callback_SSLVerify_wrapper); + SSL_CTX_set_verify_depth(XrdSecProtocolssl::ctx, XrdSecProtocolssl::verifydepth + 1); + + if(!SSL_CTX_set_generate_session_id(XrdSecProtocolssl::ctx, XrdSecProtocolssl::GenerateSession)) { + TRACE(Authen,"Cannot set session generator"); + return 0; + } + + // SSL_CTX_set_quiet_shutdown(XrdSecProtocolssl::ctx,1); + SSL_CTX_set_options(XrdSecProtocolssl::ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2); + SSL_CTX_set_session_cache_mode(XrdSecProtocolssl::ctx, SSL_SESS_CACHE_BOTH | SSL_SESS_CACHE_NO_AUTO_CLEAR ); + SSL_CTX_set_session_id_context(XrdSecProtocolssl::ctx,(const unsigned char*) XrdSecProtocolssl::SessionIdContext, strlen(XrdSecProtocolssl::SessionIdContext)); + SSL_CTX_sess_set_new_cb(XrdSecProtocolssl::ctx, XrdSecProtocolssl::NewSession); + } + return (char *)""; +} +} + +/******************************************************************************/ +/* X r d S e c P r o t o c o l u n i x O b j e c t */ +/******************************************************************************/ + +extern "C" +{ +XrdSecProtocol *XrdSecProtocolsslObject(const char mode, + const char *hostname, + const struct sockaddr &netaddr, + const char *parms, + XrdOucErrInfo *erp) +{ + XrdSecProtocolssl *prot; + +// Return a new protocol object +// + if (!(prot = new XrdSecProtocolssl(hostname, &netaddr))) + {const char *msg = "Secssl: Insufficient memory for protocol."; + if (erp) erp->setErrInfo(ENOMEM, msg); + else cerr <<msg <<endl; + return (XrdSecProtocol *)0; + } + +// All done +// + return prot; +} +} + diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/XrdSecProtocolssl.hh b/net/xrootd/src/xrootd/src/XrdSecssl/XrdSecProtocolssl.hh new file mode 100644 index 0000000000000000000000000000000000000000..f3d3273d7b084820d7f61db9bed994b290c08c05 --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdSecssl/XrdSecProtocolssl.hh @@ -0,0 +1,246 @@ +/******************************************************************************/ +/* */ +/* X r d S e c P r o t o c o l s s l . h h */ +/* */ +/* (c) 2007 by the Board of Trustees of the Leland Stanford, Jr., University */ +/* All Rights Reserved */ +/* Produced by Andrew Hanushevsky for Stanford University under contract */ +/* DE-AC02-76-SFO0515 with the Department of Energy */ +/******************************************************************************/ + +// $Id$ + +#include <unistd.h> +#include <ctype.h> +#include <errno.h> +#include <iostream> +#include <stdlib.h> +#include <strings.h> +#include <grp.h> +#include <pwd.h> + +#define OPENSSL_THREAD_DEFINES +#include <openssl/opensslconf.h> + +#include <openssl/crypto.h> +#include <openssl/x509v3.h> +#include <openssl/ssl.h> +#include <openssl/pem.h> +#include <openssl/err.h> +#include <openssl/rand.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/file.h> +#include <fcntl.h> +#include <pwd.h> +#include <grp.h> + +#include "XrdNet/XrdNetDNS.hh" +#include "XrdOuc/XrdOucErrInfo.hh" +#include "XrdOuc/XrdOucHash.hh" +#include "XrdOuc/XrdOucString.hh" +#include "XrdOuc/XrdOucTrace.hh" +#include "XrdOuc/XrdOucTokenizer.hh" +#include "XrdSys/XrdSysPthread.hh" +#include "XrdSys/XrdSysLogger.hh" +#include "XrdSec/XrdSecInterface.hh" +#include "XrdSec/XrdSecTLayer.hh" +#include "XrdSecssl/XrdSecProtocolsslTrace.hh" +#include "libsslGridSite/grst_verifycallback.h" +#include "libsslGridSite/gridsite.h" + +#define EXPORTKEYSTRENGTH 10 + +#define PROTOCOLSSL_MAX_CRYPTO_MUTEX 256 + + +// fix for SSL 098 stuff and g++ + +#ifdef R__SSL_GE_098 +#undef PEM_read_SSL_SESSION +#undef PEM_write_SSL_SESSION + +#define PEM_read_SSL_SESSION(fp,x,cb,u) (SSL_SESSION *)PEM_ASN1_read( (void *(*)(void **, const unsigned char **, long int))d2i_SSL_SESSION,PEM_STRING_SSL_SESSION,fp,(void **)x,cb,u) + +#define PEM_write_SSL_SESSION(fp,x) PEM_ASN1_write((int (*)(void*, unsigned char**))i2d_SSL_SESSION, PEM_STRING_SSL_SESSION,fp, (char *)x, NULL,NULL,0,NULL,NULL) +#endif + +#define l2n(l,c) (*((c)++)=(unsigned char)(((l)>>24)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16)&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8)&0xff), \ + *((c)++)=(unsigned char)(((l) )&0xff)) + +#ifdef SUNCC +#define __FUNCTION__ "-unknown-" +#endif + + +static XrdOucTrace *SSLxTrace=0; + +class XrdSecProtocolssl; + +#define MAX_SESSION_ID_ATTEMPTS 10 + +/******************************************************************************/ +/* X r d S e c P r o t o c o l s s l C l a s s */ +/******************************************************************************/ + +class XrdSecsslSessionLock { +private: +static XrdSysMutex sessionmutex; + int sessionfd; + +public: + XrdSecsslSessionLock() {sessionfd=0;} + bool SoftLock() { sessionmutex.Lock();return true;} + bool SoftUnLock() {sessionmutex.UnLock();return true;} +#ifdef SUNCC + bool HardLock(const char* path) {return true;} + bool HardUnLock() {return true;} + ~XrdSecsslSessionLock() {sessionmutex.UnLock();} +#else + bool HardLock(const char* path) {sessionfd = open(path,O_RDWR); if ( (sessionfd>0) && (!flock(sessionfd,LOCK_EX)))return true;return false;} + bool HardUnLock() {if (sessionfd>0) {flock(sessionfd,LOCK_UN);close(sessionfd);sessionfd=0;}return true;} + ~XrdSecsslSessionLock() {if (sessionfd>0) {flock(sessionfd,LOCK_UN);close(sessionfd);};sessionmutex.UnLock();} +#endif + +}; + + + +class XrdSecProtocolssl : public XrdSecTLayer +{ +public: + friend class XrdSecProtocolDummy; // Avoid stupid gcc warnings about destructor + + XrdSecProtocolssl(const char* hostname, const struct sockaddr *ipaddr) : XrdSecTLayer("ssl",XrdSecTLayer::isServer) { + credBuff = 0; + ssl = 0; + Entity.name = 0; + Entity.grps = 0; + Entity.endorsements = 0; + host = hostname; + if (ipaddr) + Entity.host = (XrdNetDNS::getHostName((sockaddr&)*ipaddr)); + else + Entity.host = strdup(""); + proxyBuff[0]=0; + client_cert=0; + server_cert=0; + ssl = 0 ; + clientctx = 0; + } + + + virtual void secClient(int theFD, XrdOucErrInfo *einfo); + virtual void secServer(int theFD, XrdOucErrInfo *einfo=0); + + virtual void Delete() {delete this;} + + + static int GenerateSession(const SSL* ssl, unsigned char *id, unsigned int *id_len); + static int NewSession(SSL* ssl, SSL_SESSION *pNew); + static int GetSession(SSL* ssl, SSL_SESSION *pNew); + + static char* SessionIdContext ; + static char* sslcadir; + static char* sslvomsdir; + static char* sslserverkeyfile; + static char* sslkeyfile; + static char* sslcertfile; + static char* sslproxyexportdir; + static bool sslproxyexportplain; + static char sslserverexportpassword[EXPORTKEYSTRENGTH+1]; + + static char* gridmapfile; + static char* vomsmapfile; + static bool mapuser; + static bool mapnobody; + static bool mapgroup; + static bool mapcerncertificates; + static int debug; + static time_t sslsessionlifetime; + static bool isServer; + static bool forwardProxy; + static bool allowSessions; + static X509_STORE* store; + static X509_LOOKUP* lookup; + static int verifydepth; + static int verifyindex; + int sessionfd; + X509* client_cert; + X509* server_cert; + XrdOucString host; + + // User/Group mapping + static void ReloadGridMapFile(); + static void ReloadVomsMapFile(); + static bool VomsMapGroups(const char* groups, XrdOucString& allgroups, XrdOucString& defaultgroup); + + static void GetEnvironment(); + static XrdOucHash<XrdOucString> gridmapstore; + static XrdOucHash<XrdOucString> vomsmapstore; + static XrdOucHash<XrdOucString> stringstore; + static XrdSysMutex StoreMutex; + static XrdSysMutex VomsMapMutex; + static XrdSysMutex GridMapMutex; + static XrdSysMutex* CryptoMutexPool[PROTOCOLSSL_MAX_CRYPTO_MUTEX]; + // for error logging and tracing + static XrdSysLogger Logger; + static XrdSysError ssleDest; + static time_t storeLoadTime; + + typedef struct { + int verbose_mode; + int verify_depth; + int always_continue; + } sslverify_t; + + char proxyBuff[16384]; + static SSL_CTX* ctx; + SSL_CTX* clientctx; + + XrdSysMutex SSLMutex; +private: + + ~XrdSecProtocolssl() { + if (credBuff) free(credBuff); + if (Entity.name) free(Entity.name); + if (Entity.grps) free(Entity.grps); + if (Entity.role) free(Entity.role); + if (Entity.host) free(Entity.host); + SSLMutex.Lock(); + if (ssl) SSL_free(ssl);ssl=0; + if (client_cert) X509_free(client_cert); + if (server_cert) X509_free(server_cert); + SSLMutex.UnLock(); + } + + static int Fatal(XrdOucErrInfo *erp, const char* msg, int rc); + + + struct sockaddr hostaddr; // Client-side only + char *credBuff; // Credentials buffer (server) + int Step; // Indicates step in authentication + + int sd; + int listen_sd; + struct sockaddr_in sa_serv; + struct sockaddr_in sa_cli; + SSL* ssl; +}; + +extern "C" +{ + char *XrdSecProtocolsslInit(const char mode, + const char *parms, + XrdOucErrInfo *erp); +} + + diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/XrdSecProtocolsslTest.cc b/net/xrootd/src/xrootd/src/XrdSecssl/XrdSecProtocolsslTest.cc new file mode 100644 index 0000000000000000000000000000000000000000..abb94e49805c81c41c187646ef362824a36c76a1 --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdSecssl/XrdSecProtocolsslTest.cc @@ -0,0 +1,135 @@ + +#include <XrdSys/XrdSysLogger.hh> +#include <XrdSec/XrdSecTLayer.hh> +#include <XrdNet/XrdNetSocket.hh> +#include <XrdNet/XrdNetOpts.hh> +#include <XrdSecssl/XrdSecProtocolssl.hh> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/time.h> + +#define TESTLOOP 100 + +int main(int argc, char* argv[]) { + if (argc < 2) { + fprintf(stderr,"Error: you have to define if we are server or client!\n"); + fprintf(stderr,"usage: xrdsslprotocoltest server|client [args]\n"); + exit(-1); + } + + // To silence a warning + EPNAME("main"); + PRINT("dummy"); + + setenv("XrdSecDEBUG","10",1); + XrdSysLogger logger; + XrdSysError eDest(&logger,"ssltest"); + + if (!strcmp("server",argv[1])) { + // server + const char* args="-d:10 -cadir:/etc/grid-security/certificates/"; + if (argv[2]) { + args = argv[2]; + } + + struct sockaddr netaddr; + XrdOucErrInfo error; + + XrdSecProtocolsslInit('s',args, &error); + XrdSecProtocolssl* protocol = new XrdSecProtocolssl("localhost",(const struct sockaddr*)&netaddr); + if (!protocol) { + fprintf(stderr,"Error: cannot create protocol object\n"); + exit(-1); + } + + XrdNetSocket* socket = new XrdNetSocket(&eDest); + socket->Open(0,12345,XRDNET_SERVER); + + while(1) { + // do an infinite handshake loop + int theFd = socket->Accept(); + if (theFd<=0) { + fprintf(stderr,"Accept failed on socket!\n"); + exit(-1); + } + protocol->secServer(theFd, &error); + + fprintf(stderr,"Authentication done: [%d : %s]\n", error.getErrInfo(),error.getErrText()); + close(theFd); + } + + exit(0); + } else { + if (!strcmp("client",argv[1])) { + // client + struct sockaddr netaddr; + XrdOucErrInfo error; + + XrdSecProtocolsslInit('c',"", &error); + XrdSecProtocolssl* protocol = new XrdSecProtocolssl("localhost",(const struct sockaddr*)&netaddr); + if (!protocol) { + fprintf(stderr,"Error: cannot create protocol object\n"); + exit(-1); + } + XrdSecProtocolssl::allowSessions = false; + struct timeval tv1, tv2, tv3; + struct timezone tz; + + gettimeofday(&tv1,&tz); + for (int i=0; i< TESTLOOP; i++) { + XrdNetSocket* socket = new XrdNetSocket(&eDest); + + socket->Open(0,12345); + + int theFd = socket->Detach(); + if (theFd<=0) { + fprintf(stderr,"unable to connect to socket\n"); + fprintf(stdout,"Client aborted: unable to connect to socket\n"); + exit(-1); + } + protocol->secClient(theFd, &error); + if (error.getErrInfo()) { + fprintf(stderr,"Authentication done: [%d : %s]\n", error.getErrInfo(),error.getErrText()); + fprintf(stdout,"Client aborted: authentication failure: [%d : %s]\n", error.getErrInfo(),error.getErrText()); + exit(-1); + } + + delete socket; + } + gettimeofday(&tv2,&tz); + XrdSecProtocolssl::allowSessions = true; + for (int i=0; i< TESTLOOP; i++) { + XrdNetSocket* socket = new XrdNetSocket(&eDest); + + socket->Open(0,12345); + + int theFd = socket->Detach(); + + protocol->secClient(theFd, &error); + if (error.getErrInfo()) { + fprintf(stderr,"Authentication done: [%d : %s]\n", error.getErrInfo(),error.getErrText()); + exit(-1); + } + delete socket; + } + gettimeofday(&tv3,&tz); + + float inta = (((tv2.tv_sec-tv1.tv_sec) * 1000) + (tv2.tv_usec-tv1.tv_usec)/1000.0)/1000.0; + float intb = (((tv3.tv_sec-tv2.tv_sec) * 1000) + (tv3.tv_usec-tv2.tv_usec)/1000.0)/1000.0; + fprintf(stdout,"-----------------------------------------------------------------\n"); + fprintf(stdout,"Tested %d iterations without and with sessions...\n",TESTLOOP); + fprintf(stdout,"-----------------------------------------------------------------\n"); + fprintf(stdout,"Performance without Sessions: %.02f authentications/s\n",TESTLOOP/inta); + fprintf(stdout,"Performance with Sessions: %.02f authentications/s\n",TESTLOOP/intb); + fprintf(stdout,"-----------------------------------------------------------------\n"); + exit(0); + } + } + fprintf(stderr,"Error: you have to define if we are server or client!\n"); + fprintf(stderr,"usage: xrdsslprotocoltest server|client\n"); + exit(-1); +} + + + diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/XrdSecProtocolsslTrace.hh b/net/xrootd/src/xrootd/src/XrdSecssl/XrdSecProtocolsslTrace.hh new file mode 100644 index 0000000000000000000000000000000000000000..68f80913099e5d642f428b52d709cfd181c5ef1f --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdSecssl/XrdSecProtocolsslTrace.hh @@ -0,0 +1,41 @@ +// $Id$ +#ifndef ___SECSSL_TRACE_H___ +#define ___SECSSL_TRACE_H___ +/******************************************************************************/ +/* */ +/* X r d S e c g s i T r a c e . h h */ +/* */ +/* (C) 2008 A.J. Peters, CERN */ +/* */ +/******************************************************************************/ + +#include <XrdOuc/XrdOucTrace.hh> + +#ifndef NODEBUG + +#include <iostream> + +#define QTRACE(act) (SSLxTrace && (SSLxTrace->What & TRACE_ ## act)) +#define PRINT(y) {if (SSLxTrace) {SSLxTrace->Beg(epname); \ + cerr <<y; SSLxTrace->End();}} +#define TRACE(act,x) if (QTRACE(act)) PRINT(x) +#define DEBUG(y) TRACE(Debug,y) +#define EPNAME(x) const char *epname = x; + +#else + +#define QTRACE(x) +#define PRINT(x) +#define TRACE(x,y) +#define DEBUG(x) +#define EPNAME(x) + +#endif + +#define TRACE_ALL 0x000f +#define TRACE_Authenxx 0x0007 +#define TRACE_Authen 0x0004 +#define TRACE_Debug 0x0001 +#define TRACE_Identity 0x0002 + +#endif diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/acinclude.m4 b/net/xrootd/src/xrootd/src/XrdSecssl/acinclude.m4 new file mode 100644 index 0000000000000000000000000000000000000000..d3fee5d2ff1537161c952b791a1cf451c9f65e2d --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdSecssl/acinclude.m4 @@ -0,0 +1,274 @@ +dnl @synopsis ACX_LOCATEFILE(filename,path-list,[action if found],[action if not found]) +dnl +dnl Locates a file in a given search path +dnl +dnl the directory containing the target file is available as acx_founddir +dnl the path-list is available as acx_searchpath +dnl +dnl Author: Derek Feichtinger <derek.feichtinger@cern.ch> +dnl +dnl Version info: $Id: acinclude.m4,v 1.1 2010/01/13 11:41:24 ganis Exp $ +dnl Checked in by $Author: ganis $ +dnl ======================================================================== +AC_DEFUN([ACX_LOCATEFILE],[ + acx_searchpath="$2" + acx_founddir="" + AC_MSG_CHECKING([for $1 in $2]) + for dir in $2 ; do + if test -f "$[]dir/$1" ; then + acx_founddir="$[]dir/" + break + fi + done + if test x"$[]acx_founddir" = x ; then + AC_MSG_RESULT([no]) + ifelse([$4], ,: ,[$4]) + else + AC_MSG_RESULT([found]) + ifelse([$3], ,: ,[$3]) + fi +]) + +dnl @synopsis ACX_MSG_ERROR(error-message) +dnl +dnl like AC_MSG_ERROR, but also prints out some important +dnl environment settings +AC_DEFUN([ACX_MSG_ERROR],[ + AC_MSG_ERROR([$1 + (CPPFLAGS="$[]CPPFLAGS) (LDFLAGS="$[]LDFLAGS") + (CFLAGS="$[]CFLAGS") (CXXFLAGS="$[]CXXFLAGS")]) +]) + +dnl @synopsis ACX_WITH_BASEDIR +dnl ############################################################ +dnl # give the user an easy way to specify a base installation +dnl # directory dir, where headers and libraries are found in +dnl # $dir/include and $dir/lib +AC_DEFUN([ACX_WITH_BASEDIR],[ +AC_ARG_WITH(base-directory, + [ --with-base-directory=PATH add PATH/include and PATH/lib to search paths], + [ + acx_base_incdir="$withval/include" + acx_base_libdir="$withval/lib" + acx_base_bindir="$withval/bin" + BASE_INCDIR="-I$withval/include" + BASE_LIBDIR="-L$withval/lib" + ] + ) +AC_SUBST(BASE_INCDIR) +AC_SUBST(BASE_LIBDIR) +]) + +dnl @synopsis ACX_COLLECT_OPTION(optionname,[optvalue]) +dnl collects option names for an informative printout with +dnl the ACX_PRINTOPTIONS macro. Relies on the existence of +dnl an associated activate_FEATURE variable +AC_DEFUN([ACX_COLLECT_OPTION],[ + acx_optionvar="$[]acx_optionvar $1" + ifelse($2, , :,activate_$1=$2) +#opt activate_$1=default_yes|default_no +]) + +dnl @synopsis ACX_LIBOPTION(optionname,enable-help-text,[yes|no]) +dnl +dnl a default value for activate_FEATURE set before the evaluation of this +dnl macro will be honored. +dnl all options get collected in acx_optionvar for later printout with +dnl the ACX_PRINTOPTIONS macro +dnl specifying one of the --with-feature-*dir options will set the activate +dnl state of the feature to yes, except if it has been deliberately turned off +dnl with the --enable-feature=no or --disable-feature options +AC_DEFUN([ACX_LIBOPTION],[ + if test x"$[]activate_$1" = x; then + ifelse($3, yes,activate_$1=default_yes,activate_$1=default_no) + fi + AC_ARG_ENABLE($1,[[ --enable-$1 $2 (default=$3)]],[[activate_$1=$enableval]],[[:]]) + AC_ARG_WITH($1-libdir,[[ --with-$1-libdir=PATH path containing $1 library]], + [acx_$1_libdir=$[]withval + if test $[]activate_$1 = default_no; then activate_$1=yes;fi + if test $[]activate_$1 = default_yes; then activate_$1=yes;fi + ]) + AC_ARG_WITH($1-incdir,[[ --with-$1-incdir=PATH path containing $1 headers]], + [acx_$1_incdir=$[]withval + if test $[]activate_$1 = default_no; then activate_$1=yes;fi + if test $[]activate_$1 = default_yes; then activate_$1=yes;fi + ]) + + if test x"$[]acx_$1_libdir" != x; then + translit($1,`a-z',`A-Z')_LIBDIR="-L$[]acx_$1_libdir" + fi + if test x"$[]acx_$1_incdir" != x; then + translit($1,`a-z',`A-Z')_INCDIR="-I$[]acx_$1_incdir" + fi + + AC_SUBST(translit($1,`a-z',`A-Z')_LIBDIR) + AC_SUBST(translit($1,`a-z',`A-Z')_INCDIR) + + ACX_COLLECT_OPTION($1) +#opt acx_$1_libdir=PATH +#opt acx_$1_incdir=PATH +]) + +dnl @synopsis ACX_PRINTOPTIONS +dnl +dnl prints a summary about option selections and default files +dnl that have been read. +dnl Relies on the oprions having been recorded by ACX_COLLECT_OPTION +dnl or ACX_LIBOPTION and default files having been read with ACX_LOAD_DEFAULTS +AC_DEFUN([ACX_PRINTOPTIONS],[ + echo + if test x"$[]acx_defaultfiles" != x; then + echo "Defaults read from: $[]acx_defaultfiles" + fi + + echo "SELECTED OPTIONS" + echo "----------------" + for opt in $[]acx_optionvar; do + optvar="activate_$[]opt" + optval="\$$[]optvar" + optval=`eval echo "$[]optval"` + + optinc=acx_"$[]opt"_incdir + optincval="\$$[]optinc" + optincval=`eval echo "$[]optincval"` + + optlib=acx_"$[]opt"_libdir + optlibval="\$$[]optlib" + optlibval=`eval echo "$[]optlibval"` + + defaultval=`expr x"$[]optval" : 'xdefault_\(.*\)'` + if test x"$[]defaultval" != x; then + optval=$[]defaultval + bydefault="(by default)" + else + bydefault=" " + fi + + if test x"$[]optval" != xyes; then + optincval="";optlibval="" + fi + echo "$[]opt: $[]optval $[]bydefault $[]optincval $[]optlibval" + done +]) + +dnl @synopsis ACX_LOAD_DEFAULTS(filename) +dnl +dnl Loads (sources) a file containing default settings. +dnl The location of the file has to be given relative +dnl to the srcdir +AC_DEFUN([ACX_LOAD_DEFAULTS],[ + acx_tmp="$[]srcdir/$1" + AC_MSG_CHECKING([for defaults file $[]acx_tmp]) + if test -r "$[]acx_tmp"; then + AC_MSG_RESULT([found - sourcing...]) + source $[]acx_tmp + acx_defaultfiles="$acx_defaultfiles $[]acx_tmp" + else + AC_MSG_RESULT([NOT FOUND]) + fi +]) + + +dnl AX_HAVE_EPOLL([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl AX_HAVE_EPOLL_PWAIT([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl (c) 2008 Peter Simons <simons@cryp.to> +dnl http://autoconf-archive.cryp.to/ax_have_epoll.html +dnl +dnl modified the acceptable Unix version for AX_HAVE_EPOLL +dnl to be 2,6,12 (original macro had 2.5.45) +AC_DEFUN([AX_HAVE_EPOLL], [dnl + ax_have_epoll_cppflags="${CPPFLAGS}" + AC_CHECK_HEADER([linux/version.h], [CPPFLAGS="${CPPFLAGS} -DHAVE_LINUX_VERSION_H"]) + AC_MSG_CHECKING([for Linux epoll(7) interface]) + AC_CACHE_VAL([ax_cv_have_epoll], [dnl + AC_LINK_IFELSE([dnl + AC_LANG_PROGRAM([dnl +#include <sys/epoll.h> +#ifdef HAVE_LINUX_VERSION_H +# include <linux/version.h> +# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12) +# error linux kernel version is too old to have epoll +# endif +#endif +], [dnl +int fd, rc; +struct epoll_event ev; +fd = epoll_create(128); +rc = epoll_wait(fd, &ev, 1, 0);])], + [ax_cv_have_epoll=yes], + [ax_cv_have_epoll=no])]) + CPPFLAGS="${ax_have_epoll_cppflags}" + AS_IF([test "${ax_cv_have_epoll}" = "yes"], + [AC_MSG_RESULT([yes]) +$1],[AC_MSG_RESULT([no]) +$2]) +])dnl + +AC_DEFUN([AX_HAVE_EPOLL_PWAIT], [dnl + ax_have_epoll_cppflags="${CPPFLAGS}" + AC_CHECK_HEADER([linux/version.h], + [CPPFLAGS="${CPPFLAGS} -DHAVE_LINUX_VERSION_H"]) + AC_MSG_CHECKING([for Linux epoll(7) interface with signals extension]) + AC_CACHE_VAL([ax_cv_have_epoll_pwait], [dnl + AC_LINK_IFELSE([dnl + AC_LANG_PROGRAM([dnl +#ifdef HAVE_LINUX_VERSION_H +# include <linux/version.h> +# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) +# error linux kernel version is too old to have epoll_pwait +# endif +#endif +#include <sys/epoll.h> +#include <signal.h> +], [dnl +int fd, rc; +struct epoll_event ev; +fd = epoll_create(128); +rc = epoll_wait(fd, &ev, 1, 0); +rc = epoll_pwait(fd, &ev, 1, 0, (sigset_t const *)(0));])], + [ax_cv_have_epoll_pwait=yes], + [ax_cv_have_epoll_pwait=no])]) + CPPFLAGS="${ax_have_epoll_cppflags}" + AS_IF([test "${ax_cv_have_epoll_pwait}" = "yes"], + [AC_MSG_RESULT([yes]) +$1],[AC_MSG_RESULT([no]) +$2]) +])dnl + + +dnl AC_SYS_DEV_POLL([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl Dave Benson <daveb@ffem.org> 2008-04-12 +dnl from http://autoconf-archive.cryp.to/ac_sys_dev_poll.html +dnl +AC_DEFUN([AC_SYS_DEV_POLL], [AC_CACHE_CHECK(for /dev/poll support, ac_cv_dev_poll, + AC_TRY_COMPILE([#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/poll.h> +#include <sys/devpoll.h>], +[ + struct dvpoll p; + p.dp_timeout = 0; + p.dp_nfds = 0; + p.dp_fds = (struct pollfd *) 0; + return 0; +], + ac_cv_dev_poll=yes + [$1], + ac_cv_dev_poll=no + [$2] + ) + ) +]) + +AC_DEFUN([ACX_PUT_PRIVATES], +[ + AH_BOTTOM([#ifdef __GNUC__ +#define UNUSED(z) z __attribute__ ((unused)) +#define PRIVATE __attribute__ ((visibility ("hidden"))) +#define PUBLIC __attribute__ ((visibility ("default"))) +#else +#define UNUSED +#define PRIVATE +#define PUBLIC +#endif])]) diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/bootstrap.sh b/net/xrootd/src/xrootd/src/XrdSecssl/bootstrap.sh new file mode 100755 index 0000000000000000000000000000000000000000..65d7f9911b37bdddd940f7edb245afb3d71f7cec --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdSecssl/bootstrap.sh @@ -0,0 +1,6 @@ +#!/bin/sh +libtoolize --copy --force +aclocal +automake -acf +autoconf + diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/configure.ac b/net/xrootd/src/xrootd/src/XrdSecssl/configure.ac new file mode 100644 index 0000000000000000000000000000000000000000..fd5913becb5ff4cbeb45511a35616d7cef9851c4 --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdSecssl/configure.ac @@ -0,0 +1,234 @@ +AC_PREREQ(2.57) +AC_INIT(xrootd-secssl, 4.0.1,[andreas.joachim.peters@cern.ch]) +AC_CONFIG_SRCDIR([XrdSecProtocolsslTrace.hh]) +AC_CANONICAL_TARGET +AM_INIT_AUTOMAKE(1.6 foreign) + +mkdir -p m4 +AC_CONFIG_MACRO_DIR([m4]) + +case "$target_os" in + tiger*) + AC_DEFINE(__macos__) + ;; + darwin*) + AC_DEFINE(__macos__) + ;; + apple*) + AC_DEFINE(__macos__) + ;; + linux*) + # TODO: check why two different macros are used for this + AC_DEFINE(__linux__) + AC_DEFINE(__linux) + ;; + solaris*) + AC_DEFINE(__sun) + CC_PREFERENCE="cc gcc" + CXX_PREFERENCE="CC g++" + DFLT_OPT="-O2" + ;; + *) + AC_MSG_WARN([untested operating system]) + ;; +esac + +libversion=`libtoolize --version | head -1 | cut -d " " -f 4` +case "$target_os" in + darwin*) + if test "x$libversion" = "x1.5.22"; then + AC_MSG_ERROR([broken libtool version $libversion found on this MAC - install newer one from http://www.gnu.org/software/libtool/news +.html]) + fi + ;; +esac + + +echo $libdir | grep lib64 >& /dev/null +if test "$?" = "0" ; then + MARK64=64; +else + MARK64=""; +fi + +AC_SUBST(MARK64) + +AC_DEFINE(_LARGEFILE_SOURCE) +AC_DEFINE(_FILE_OFFSET_BITS,64) +AC_SYS_LARGEFILE + +# Do all testing using C++ +AC_LANG([C++]) + +# Checks for programs. +AC_PROG_CXX +AC_PROG_LIBTOOL +LT_INIT + +AC_ARG_WITH(xrootd-location, + [ --with-xrootd-location=installation of xrootd version to use], + [XROOTD_LOCATION=$withval], + [XROOTD_LOCATION=/opt/xrootd/]) + +AC_ARG_WITH(xrootd-incdir, + [ --with-xrootd-incdir=installation of xrootd include files], + [XROOTD_INCDIR=$withval], + [XROOTD_INCDIR=$XROOTD_LOCATION/include/xrootd/]) + +AC_ARG_WITH(xrootd-libdir, + [ --with-xrootd-libdir=installation of xrootd libraries], + [XROOTD_LIBDIR=$withval], + [XROOTD_LIBDIR=$XROOTD_LOCATION/lib]) + +AC_SUBST(XROOTD_INCDIR) +AC_SUBST(XROOTD_LIBDIR) + +if ! test -e "${XROOTD_INCDIR}/XrdOuc/XrdOucEnv.hh" +then + AC_MSG_ERROR([xrootd header files could not be found in inc directory ${XROOTD_INCDIR}]) +fi + +# Check for xrootd library +#if ! test -e "${XROOTD_LIBDIR}/libXrdOuc.so" +#then +# AC_MSG_ERROR([xrootd library could not be found in lib directory ${XROOTD_LIBDIR}]) +#fi + +SSLLINKLIB="-L${XROOTD_LIBDIR}/ -lXrdOuc" + +#################################################################### +# OpenSSL support + ACX_LIBOPTION(openssl,[ssl library],yes) + ssltests=ok + + if test x"$acx_openssl_incdir" != x; then + searchpath=$acx_openssl_incdir + else + searchpath="/usr/include" + fi + + ACX_LOCATEFILE([openssl/opensslv.h],[$searchpath], + [OPENSSL_INCDIR="-I$acx_founddir" + OPENSSL_VERSION_TEXT=`grep OPENSSL_VERSION_TEXT "$dir"/openssl/opensslv.h | grep OpenSSL` + AC_MSG_NOTICE([openssl header dir set to: $dir ]) ], + [AC_MSG_ERROR([Could not locate openssl/opensslv.h])] + ) + + # test openssl version + verstr=`expr x"$OPENSSL_VERSION_TEXT" : '.*\([[0-9]][[0-9]]*\.[[0-9]][[0-9]]*\.[[0-9]][[0-9]]*[[a-z]]*\)'` + AC_MSG_NOTICE([OpenSSL version : $verstr]) + + vermajor=`expr x"$verstr" : '.*\([[0-9]][[0-9]]*\).[[0-9]][[0-9]]*.[[0-9]][[0-9]]*'` + verminor=`expr x"$verstr" : '.*[[0-9]][[0-9]]*\.\([[0-9]][[0-9]]*\)\.[[0-9]][[0-9]]*'` + verrelease=`expr x"$verstr" : '.*[[0-9]][[0-9]]*\.[[0-9]][[0-9]]*\.\([[0-9]][[0-9]]*\)'` + vernum=`expr 10000 \* 0"$vermajor" + 100 \* 0"$verminor" + 0"$verrelease"` + if test 0"$vernum" -lt 906 ; then + AC_MSG_WARN([OpenSSL >= 0.9.6 required (found: $OPENSSL_VERSION_TEXT) + ssltests=failed]) + fi + + CPPFLAGS_BUP="$CPPFLAGS" + LDFLAGS_BUP=$LDFLAGS + + CPPFLAGS="$OPENSSL_INCDIR $CPPFLAGS" + AC_CHECK_HEADER([openssl/ssl.h], + [],[ssltests=failed] + ) + + + LDFLAGS="$OPENSSL_LIBDIR $LDFLAGS" + AC_CHECK_LIB([ssl],[SSL_library_init], + [:],[ssltests=failed],[-lcrypto] + ) + + if test x"$ssltests" != xok ; then + AC_MSG_ERROR([tests for openssl failed. Change configure options]) + else + AC_DEFINE(R__SSL) + # note: -DPERL5 is needed for resolving a clash between unistd.h and + # openssl/des.h regarding the crypt function on some older systems (e.g. RH7.3) + if test 0"$vernum" -lt 907 ; then + AC_DEFINE(R__SSL_096) + AC_SUBST(SSLCXXFLAGS,[-DPERL5]) + fi + + # note: -DR__SSL_GE_098 allows to deal with some differences in ANS1 macros + # introduced in version 0.9.8 + if test 0"$vernum" -ge 908 ; then + AC_DEFINE(R__SSL_GE_098) + fi + fi + + CPPFLAGS="$CPPFLAGS_BUP" + LDFLAGS=$LDFLAGS_BUP + +#################################################################### +# libxml2 support + xmltests=ok + ACX_LIBOPTION(xml2,[xml library],yes) + if test x"$acx_xml2_incdir" != x; then + searchpath=$acx_xml2_incdir + else + searchpath="/usr/include/libxml2" + fi + + ACX_LOCATEFILE([libxml/xmlmemory.h],[$searchpath], + [XML2_INCDIR="-I$acx_founddir" + AC_MSG_NOTICE([xml2 header dir set to: $dir ]) ], + [AC_MSG_ERROR([Could not locate libxml/xmlmemory.h])] + ) + + CPPFLAGS_BUP="$CPPFLAGS" + LDFLAGS_BUP=$LDFLAGS + + CPPFLAGS="$XML2_INCDIR $CPPFLAGS" + + AC_CHECK_HEADER([libxml/xmlmemory.h], + [],[ssltests=failed] + ) + + + LDFLAGS="$XML2_LIBDIR $LDFLAGS" + AC_CHECK_LIB([xml2],[xmlReaderForMemory], + [:],[xmltests=failed],[] + ) + + if test x"$xmltests" != xok ; then + AC_MSG_ERROR([tests for libxml2 failed. Change configure options]) + else + AC_MSG_NOTICE([using xml settings: $XML2_INCDIR $XML2_LIBDIR ]) + fi + + CPPFLAGS="$CPPFLAGS_BUP" + LDFLAGS=$LDFLAGS_BUP + +AC_SUBST(XML2_INCDIR) +AC_SUBST(XML2_LIBDIR) + +AC_SUBST(IS_XRDSECSSL) +AC_SUBST(SSLLINKLIB) +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS([sys/time.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_HEADER_STDBOOL +AC_C_CONST +AC_TYPE_MODE_T +AC_TYPE_SIZE_T +AC_HEADER_TIME + +# Checks for library functions. +AC_FUNC_STAT +AC_CHECK_FUNCS([gettimeofday mkdir strdup]) + +AC_CONFIG_FILES([Makefile + libsslGridSite/Makefile + xrootd-secssl.spec +] ) + +test -L XrdSecssl || ln -s ./ XrdSecssl +echo "==================================================" +echo "Configuring for ../lib$MARK64 library directories" +echo "==================================================" +AC_OUTPUT diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/getAutotools.sh b/net/xrootd/src/xrootd/src/XrdSecssl/getAutotools.sh new file mode 100755 index 0000000000000000000000000000000000000000..98a098fd938e214f53330fe1561d53e6a6e93353 --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdSecssl/getAutotools.sh @@ -0,0 +1,214 @@ +#!/bin/bash + +############################################################################ +# Little scriptlet to fetch and install an autotools development environment +# +# Author: Derek Feichtinger <derek.feichtinger@psi.ch> +# Initial Version: 2007-06-02 +# +# Version info: $Id$ +############################################################################ + +BUILDDIR=`pwd`/autotools-build +INSTALLDIR=`pwd`/install + +VAUTOCONF=2.60 +VAUTOMAKE=1.10 +VLIBTOOL=1.5.22 + +WGETSRC="http://ftp.gnu.org/gnu/" + +prompt=1 + +unset WGET + +usage() { +cat <<EOF +Name: getAutotools.sh - fetch and install an autotools development environment + +Synopsis: getAutotools.sh [options] + + Options: + -a autoconf version (default: $VAUTOCONF) + -m automake version (default: $VAUTOMAKE) + -l libtool version (default: $VLIBTOOL) + -b build directory (default: $BUILDDIR) + -i install directory (default: $INSTALLDIR) + -f do not prompt before installing + +Author: Derek Feichtinger <derek.feichtinger@psi.ch> +EOF +} + +testfor() { + p=`which $1` + status=$? + if test 0"$status" -ne 0; then + echo "Error: Could not find $1 in PATH" >&2 + echo "notfound" + return + fi + # following test needed on macos/darwin since "which" is + # horribly broken (If command is not found prints Error + # to stdout and returns a status of 0) + if test ! -e "$p"; then + echo "Error: Could not find $1 in PATH" >&2 + echo "notfound" + return + fi + + echo "$p" + echo "################################# $p" >&2 +} + +fetch_archive() { + product=`expr $1 : '\([a-zA-Z]*\)-'` + if test x"$1" = x; then + echo "Error: fetch_archives() called without argument" >&2 + exit 1 + fi + if test ! -e $1.tar; then + if test ! -e $1.tar.gz; then + test -z $WGET && WGET=`testfor wget` + if test x"$WGET" = xnotfound; then + echo "you need to manually get $WGETSRC/$product/$1.tar.gz " \ + "and place it in $BUILDDIR" >&2 + exit 1 + fi + $WGET $WGETSRC/$product/$1.tar.gz + if test ! -e $1.tar.gz; then + echo "Error: Failed to fetch tarball: $WGETSRC/$product/$1.tar.gz" >&2 + exit 1 + fi + fi + gunzip $1.tar.gz + status=$? + if test 0"$status" -ne 0; then + echo "Error: gunzip of $BUILDDIR/$1.tar.gz failed. Probably broken tarball." >&2 + echo "Remove the tarball and retry" >&2 + exit 1 + fi + fi +} + + +build_install() { + #product=`expr $1 : '\([^-]*\)'` + product=`expr $1 : '\([a-zA-Z]*\)-'` + if test x"$1" = x; then + echo "Error: build_install() called without argument" >&2 + exit 1 + fi + + echo "######################## BUILDING AND INSTALLING $1" + cd $BUILDDIR + tar xvf $1.tar + status=$? + if test 0"$status" -ne 0; then + echo "Error: Failed to extract $1.tar" >&2 + exit 1 + fi + cd $1 + ./configure --prefix=$INSTALLDIR + make install + status=$? + if test 0"$status" -ne 0; then + echo "Error: make install of $1 failed" >&2 + exit 1 + fi + if test ! -x $INSTALLDIR/bin/$product; then + echo "Error: Check for installed product failed: $INSTALLDIR/bin/$product" >&2 + exit 1 + fi +} + + + +TEMP=`getopt -o a:b:m:l:hi:f --long help -n 'getAutotools' -- "$@"` +if [ $? != 0 ] ; then usage ; echo "Terminating..." >&2 ; exit 1 ; fi +#echo "TEMP: $TEMP" +eval set -- "$TEMP" + +while true; do + case "$1" in + --help|-h) + usage + exit + ;; + -a) + VAUTOCONF="$2" + shift 2 + ;; + -m) + VAUTOMAKE="$2" + shift 2 + ;; + -l) + VLIBTOOL="$2" + shift 2 + ;; + -b) + BUILDDIR="$2" + shift 2 + ;; + -i) + INSTALLDIR="$2" + shift 2 + ;; + -f) + prompt=0 + shift + ;; + --) + shift; + break; + ;; + *) + echo "Internal error!" + exit 1 + ;; + esac +done + + + +AUTOCONF=autoconf-$VAUTOCONF +AUTOMAKE=automake-$VAUTOMAKE +LIBTOOL=libtool-$VLIBTOOL + +if test 0"$prompt" -eq 1; then + cat <<EOF +INSTALLATION SETTINGS: + + autoconf version : $VAUTOCONF + automake version : $VAUTOMAKE + libtool version : $VLIBTOOL + build directory : $BUILDDIR + install directory : $INSTALLDIR + +Is this ok? (Y/n) +EOF + read a + if test x"$a" != xy -a x"$a" != xY; then + usage + exit 0 + fi +fi + + +mkdir -p $BUILDDIR +cd $BUILDDIR +BUILDDIR=`pwd` + +export PATH=$INSTALLDIR/bin:$PATH +for n in $AUTOCONF $AUTOMAKE $LIBTOOL; do + fetch_archive $n +done +for n in $AUTOCONF $AUTOMAKE $LIBTOOL; do + build_install $n +done + +echo "############################################################" +echo "In order to use this autotools environment you need to" +echo "export PATH=$INSTALLDIR/bin:\$PATH" + diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/Makefile.am b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..412cab3c7284877763e0617b6ed5a1dd2272596f --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/Makefile.am @@ -0,0 +1,23 @@ + + +noinst_LTLIBRARIES = libsslGridSite.la +INCLUDES = ${OPENSSL_INCDIR} -I/usr/kerberos/include -I. ${XML2_INCDIR} + +libsslGridSite_la_CFLAGS = -Wall -Wno-deprecated-declarations -Wno-deprecated-declarations -DOPENSSL_NO_KRB5 -U_FORTIFY_SOURCE ${CFLAGS} + +libsslGridSite_la_SOURCES = \ + grst_err.c \ + grst_asn1.c \ + grst_x509.c \ + grst_gacl.c \ + grst_xacml.c \ + grst_http.c \ + grst_verifycallback.c \ + grst_verifycallback.h \ + gridsite.h + + + + + + diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/gridsite.h b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/gridsite.h new file mode 100644 index 0000000000000000000000000000000000000000..4606f93e1e2f189d15164789603b73e9b9471008 --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/gridsite.h @@ -0,0 +1,454 @@ +/* + Copyright (c) 2002-7, Andrew McNab, University of Manchester + All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, are permitted provided that the following + conditions are met: + + o Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + o Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/*---------------------------------------------------------------* + * For more about GridSite: http://www.gridsite.org/ * + *---------------------------------------------------------------*/ + +#ifndef GRST_VERSION +#define GRST_VERSION 010500 +#endif + +#ifndef GRST_NO_OPENSSL + +#ifndef HEADER_SSL_H +#include <openssl/ssl.h> +#endif + +#ifndef HEADER_CRYPTO_H +#include <openssl/crypto.h> +#endif +#endif + +#ifndef _TIME_H +#include <time.h> +#endif + +#ifndef _STDIO_H +#include <stdio.h> +#endif + +#ifndef FALSE +#define FALSE (0) +#endif +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +// Everything ok (= OpenSSL X509_V_OK) +#define GRST_RET_OK 0 + +// Failed for unspecified reason +#define GRST_RET_FAILED 1000 + +// Failed to find certificate in some cert store / directory +#define GRST_RET_CERT_NOT_FOUND 1001 + +// Bad signature +#define GRST_RET_BAD_SIGNATURE 1002 + +// No such file or directory +#define GRST_RET_NO_SUCH_FILE 1003 + + +// #define GRSTerrorLog(GRSTerrorLevel, GRSTerrorFmt, ...) if (GRSTerrorLogFunc != NULL) (GRSTerrorLogFunc)(__FILE__, __LINE__, GRSTerrorLevel, GRSTerrorFmt, __VA_ARGS__) + +#define GRSTerrorLog(GRSTerrorLevel, ...) if (GRSTerrorLogFunc != NULL) (GRSTerrorLogFunc)(__FILE__, __LINE__, GRSTerrorLevel, __VA_ARGS__) + +extern void (*GRSTerrorLogFunc)(char *, int, int, char *, ...); + +/* these levels are the same as Unix syslog() and Apache ap_log_error() */ + +#define GRST_LOG_EMERG 0 +#define GRST_LOG_ALERT 1 +#define GRST_LOG_CRIT 2 +#define GRST_LOG_ERR 3 +#define GRST_LOG_WARNING 4 +#define GRST_LOG_NOTICE 5 +#define GRST_LOG_INFO 6 +#define GRST_LOG_DEBUG 7 + +#define GRST_MAX_TIME_T INT32_MAX + +typedef struct { char *auri; + int delegation; + int nist_loa; + time_t notbefore; + time_t notafter; + void *next; } GRSTgaclCred; + +/* used by pre-AURI GRSTgaclCred structs */ +#ifndef SUNCC +__attribute__ ((deprecated)) +#endif +typedef struct { char *name; + char *value; + void *next; } GRSTgaclNamevalue; + +typedef int GRSTgaclAction; +typedef int GRSTgaclPerm; + +typedef struct { GRSTgaclCred *firstcred; + GRSTgaclPerm allowed; + GRSTgaclPerm denied; + void *next; } GRSTgaclEntry; + +typedef struct { GRSTgaclEntry *firstentry; } GRSTgaclAcl; + +typedef struct { GRSTgaclCred *firstcred; char *dnlists; } GRSTgaclUser; + +#define GRST_PERM_NONE 0 +#define GRST_PERM_READ 1 +#define GRST_PERM_EXEC 2 +#define GRST_PERM_LIST 4 +#define GRST_PERM_WRITE 8 +#define GRST_PERM_ADMIN 16 +#define GRST_PERM_ALL 31 + +/* DO NOT USE PermIsNone!! */ +#define GRSTgaclPermIsNone(perm) ((perm) == 0) + +#define GRSTgaclPermHasNone(perm) ((perm) == 0) +#define GRSTgaclPermHasRead(perm) (((perm) & GRST_PERM_READ ) != 0) +#define GRSTgaclPermHasExec(perm) (((perm) & GRST_PERM_EXEC ) != 0) +#define GRSTgaclPermHasList(perm) (((perm) & GRST_PERM_LIST ) != 0) +#define GRSTgaclPermHasWrite(perm) (((perm) & GRST_PERM_WRITE) != 0) +#define GRSTgaclPermHasAdmin(perm) (((perm) & GRST_PERM_ADMIN) != 0) + +#define GRST_ACTION_ALLOW 0 +#define GRST_ACTION_DENY 1 + +#define GRST_HIST_PREFIX ".grsthist" +#define GRST_ACL_FILE ".gacl" +#define GRST_DN_LISTS "/etc/grid-security/dn-lists" +#define GRST_RECURS_LIMIT 9 + +#define GRST_PROXYCERTINFO_OID "1.3.6.1.4.1.3536.1.222" +#define GRST_VOMS_OID "1.3.6.1.4.1.8005.100.100.5" +#define GRST_VOMS_DIR "/etc/grid-security/vomsdir" + +#define GRST_ASN1_MAXCOORDLEN 50 +#define GRST_ASN1_MAXTAGS 500 + +struct GRSTasn1TagList { char treecoords[GRST_ASN1_MAXCOORDLEN+1]; + int start; + int headerlength; + int length; + int tag; } ; + +typedef struct { int type; /* CA, user, proxy, VOMS, ... */ + int errors; /* unchecked, bad sig, bad time */ + char *issuer; /* Cert CA DN, EEC of PC, or VOMS DN */ + char *dn; /* Cert DN, or VOMS AC holder DN */ + char value[16384]; /* VOMS FQAN or NULL */ + time_t notbefore; + time_t notafter; + int delegation; /* relative to END of any chain */ + int serial; + char *ocsp; /* accessLocation field */ + void *raw; /* X509 or VOMS Extension object */ + void *next; } GRSTx509Cert; + +#define GRST_CERT_BAD_FORMAT 1 +#define GRST_CERT_BAD_CHAIN 2 +#define GRST_CERT_BAD_SIG 4 +#define GRST_CERT_BAD_TIME 8 +#define GRST_CERT_BAD_OCSP 16 + +#define GRST_CERT_TYPE_CA 1 +#define GRST_CERT_TYPE_EEC 2 +#define GRST_CERT_TYPE_PROXY 3 +#define GRST_CERT_TYPE_VOMS 4 + +/* a chain of certs, starting from the first CA */ +typedef struct { GRSTx509Cert *firstcert; } GRSTx509Chain; + +#ifndef GRST_NO_OPENSSL +int GRSTx509CertLoad(GRSTx509Cert *, X509 *); +int GRSTx509ChainLoadCheck(GRSTx509Chain **, STACK_OF(X509) *, X509 *, char *, char *); +#endif +int GRSTx509ChainFree(GRSTx509Chain *); + +#define GRST_HTTP_PORT 777 +#define GRST_HTTPS_PORT 488 +#define GRST_HTCP_PORT 777 +#define GRST_GSIFTP_PORT 2811 + +#define GRSThtcpNOPop 0 +#define GRSThtcpTSTop 1 + +typedef struct { unsigned char length_msb; + unsigned char length_lsb; + char text[1]; } GRSThtcpCountstr; + +#define GRSThtcpCountstrLen(string) (256*((string)->length_msb) + (string)->length_lsb) + +typedef struct { unsigned char total_length_msb; + unsigned char total_length_lsb; + unsigned char version_msb; + unsigned char version_lsb; + unsigned char data_length_msb; + unsigned char data_length_lsb; + unsigned int response : 4; + unsigned int opcode : 4; + unsigned int rr : 1; + unsigned int f1 : 1; + unsigned int reserved : 6; + unsigned int trans_id; /* must be 4 bytes */ + GRSThtcpCountstr *method; + GRSThtcpCountstr *uri; + GRSThtcpCountstr *version; + GRSThtcpCountstr *req_hdrs; + GRSThtcpCountstr *resp_hdrs; + GRSThtcpCountstr *entity_hdrs; + GRSThtcpCountstr *cache_hdrs; } GRSThtcpMessage; + +int GRSTgaclInit(void); + +#ifndef SUNCC +__attribute__ ((deprecated)) +#endif +GRSTgaclCred *GRSTgaclCredNew(char *); + +GRSTgaclCred *GRSTgaclCredCreate(char *, char *); + +#ifndef SUNCC +__attribute__ ((deprecated)) +#endif +int GRSTgaclCredAddValue(GRSTgaclCred *, char *, char *); + +#define GRSTgaclCredGetAuri(cred) ((cred)->auri) + +#define GRSTgaclCredSetNotBefore(cred, time) ((cred)->notbefore = (time)) +#define GRSTgaclCredGetNotBefore(cred) ((cred)->notbefore) + +#define GRSTgaclCredSetNotAfter(cred, time) ((cred)->notafter = (time)) +#define GRSTgaclCredGetNotAfter(cred) ((cred)->notafter) + +#define GRSTgaclCredSetDelegation(cred, level) ((cred)->delegation = (level)) +#define GRSTgaclCredGetDelegation(cred) ((cred)->delegation) + +#define GRSTgaclCredSetNistLoa(cred, level) ((cred)->nist_loa = (level)) +#define GRSTgaclCredGetNistLoa(cred) ((cred)->nist_loa) + +/* #define GACLfreeCred(x) GRSTgaclCredFree((x)) */ +int GRSTgaclCredFree(GRSTgaclCred *); + +/* #define GACLaddCred(x,y) GRSTgaclEntryAddCred((x),(y)) */ +int GRSTgaclEntryAddCred(GRSTgaclEntry *, GRSTgaclCred *); + +/* #define GACLdelCred(x,y) GRSTgaclEntryDelCred((x),(y)) */ +int GRSTgaclEntryDelCred(GRSTgaclEntry *, GRSTgaclCred *); + +/* #define GACLprintCred(x,y) GRSTgaclCredPrint((x),(y)) */ +int GRSTgaclCredCredPrint(GRSTgaclCred *, FILE *); + +int GRSTgaclCredCmpAuri(GRSTgaclCred *, GRSTgaclCred *); + +/* #define GACLnewEntry(x) GRSTgaclEntryNew((x)) */ +GRSTgaclEntry *GRSTgaclEntryNew(void); + +/* #define GACLfreeEntry(x) GRSTgaclEntryFree((x)) */ +int GRSTgaclEntryFree(GRSTgaclEntry *); + +/* #define GACLaddEntry(x,y) GRSTgaclAclAddEntry((x),(y)) */ +int GRSTgaclAclAddEntry(GRSTgaclAcl *, GRSTgaclEntry *); + +/* #define GACLprintEntry(x,y) GRSTgaclEntryPrint((x),(y)) */ +int GRSTgaclEntryPrint(GRSTgaclEntry *, FILE *); + + +/* #define GACLprintPerm(x,y) GRSTgaclPermPrint((x),(y)) */ +int GRSTgaclPermPrint(GRSTgaclPerm, FILE *); + +/* #define GACLallowPerm(x,y) GRSTgaclEntryAllowPerm((x),(y)) */ +int GRSTgaclEntryAllowPerm(GRSTgaclEntry *, GRSTgaclPerm); + +/* #define GACLunallowPerm(x,y) GRSTgaclEntryUnallowPerm((x),(y)) */ +int GRSTgaclEntryUnallowPerm(GRSTgaclEntry *, GRSTgaclPerm); + +/* #define GACLdenyPerm(x,y) GRSTgaclEntryDenyPerm((x),(y)) */ +int GRSTgaclEntryDenyPerm(GRSTgaclEntry *, GRSTgaclPerm); + +/* #define GACLundenyPerm(x,y) GRSTgaclEntryUndenyPerm((x),(y)) */ +int GRSTgaclEntryUndenyPerm(GRSTgaclEntry *, GRSTgaclPerm); + +/* #define GACLpermToChar(x) GRSTgaclPermToChar((x)) */ +char *GRSTgaclPermToChar(GRSTgaclPerm); + +/* #define GACLcharToPerm(x) GRSTgaclPermFromChar((x)) */ +GRSTgaclPerm GRSTgaclPermFromChar(char *); + +/* #define GACLnewAcl(x) GRSTgaclAclNew((x)) */ +GRSTgaclAcl *GRSTgaclAclNew(void); + +/* #define GACLfreeAcl(x) GRSTgaclAclFree((x)) */ +int GRSTgaclAclFree(GRSTgaclAcl *); + +/* #define GACLprintAcl(x,y) GRSTgaclAclPrint((x),(y)) */ +int GRSTgaclAclPrint(GRSTgaclAcl *, FILE *); + +/* #define GACLsaveAcl(x,y) GRSTgaclAclSave((y),(x)) */ +int GRSTgaclAclSave(GRSTgaclAcl *, char *); + +/* #define GACLloadAcl(x) GRSTgaclFileLoadAcl((x)) */ +GRSTgaclAcl *GRSTgaclAclLoadFile(char *); + +/* #define GACLfindAclForFile(x) GRSTgaclFileFindAclname((x)) */ +char *GRSTgaclFileFindAclname(char *); + +/* #define GACLloadAclForFile(x) GRSTgaclFileLoadAcl((x)) */ +GRSTgaclAcl *GRSTgaclAclLoadforFile(char *); + +/* #define GACLisAclFile(x) GRSTgaclFileIsAcl((x)) */ +int GRSTgaclFileIsAcl(char *); + + +/* #define GACLnewUser(x) GRSTgaclUserNew((x)) */ +GRSTgaclUser *GRSTgaclUserNew(GRSTgaclCred *); + +/* #define GACLfreeUser(x) GRSTgaclUserFree((x)) */ +int GRSTgaclUserFree(GRSTgaclUser *); + +/* #define GACLuserAddCred(x,y) GRSTgaclUserAddCred((x),(y)) */ +int GRSTgaclUserAddCred(GRSTgaclUser *, GRSTgaclCred *); + +/* #define GACLuserHasCred(x,y) GRSTgaclUserHasCred((x),(y)) */ +int GRSTgaclUserHasCred(GRSTgaclUser *, GRSTgaclCred *); + +#ifndef SUNCC +__attribute__ ((deprecated)) +#endif +int GRSTgaclUserSetDNlists(GRSTgaclUser *, char *); + +int GRSTgaclUserLoadDNlists(GRSTgaclUser *, char *); + +/* #define GACLuserFindCredType(x,y) GRSTgaclUserFindCredtype((x),(y)) */ +GRSTgaclCred *GRSTgaclUserFindCredtype(GRSTgaclUser *, char *); + +#ifndef SUNCC +__attribute__ ((deprecated)) +#endif +int GRSTgaclDNlistHasUser(char *, GRSTgaclUser *); + +int GRSTgaclUserHasAURI(GRSTgaclUser *, char *); + +/* #define GACLtestUserAcl(x,y) GRSTgaclAclTestUser((x),(y)) */ +GRSTgaclPerm GRSTgaclAclTestUser(GRSTgaclAcl *, GRSTgaclUser *); + +/* #define GACLtestExclAcl(x,y) GRSTgaclAclTestexclUser((x),(y)) */ +GRSTgaclPerm GRSTgaclAclTestexclUser(GRSTgaclAcl *, GRSTgaclUser *); + +char *GRSThttpUrlDecode(char *); + +/* #define GACLurlEncode(x) GRSThttpUrlEncode((x)) */ +char *GRSThttpUrlEncode(char *); + +/* #define GACLmildUrlEncode(x) GRSThttpMildUrlEncode((x)) */ +char *GRSThttpUrlMildencode(char *); + +int GRSTx509NameCmp(char *, char *); + +#ifndef GRST_NO_OPENSSL +int GRSTx509KnownCriticalExts(X509 *); + +int GRSTx509IsCA(X509 *); +int GRSTx509CheckChain(int *, X509_STORE_CTX *); +int GRSTx509VerifyCallback(int, X509_STORE_CTX *); + +#ifndef SUNCC +__attribute__ ((deprecated)) +#endif +int GRSTx509GetVomsCreds(int *, int, size_t, char *, X509 *, STACK_OF(X509) *, char *); + +#ifndef SUNCC +__attribute__ ((deprecated)) +#endif +GRSTgaclCred *GRSTx509CompactToCred(char *); + +#ifndef SUNCC +__attribute__ ((deprecated)) +#endif +int GRSTx509CompactCreds(int *, int, size_t, char *, STACK_OF(X509) *, char *, X509 *); +#endif + +char *GRSTx509CachedProxyFind(char *, char *, char *); +char *GRSTx509FindProxyFileName(void); +int GRSTx509MakeProxyCert(char **, FILE *, char *, char *, char *, int); +char *GRSTx509CachedProxyKeyFind(char *, char *, char *); +int GRSTx509ProxyDestroy(char *, char *, char *); +int GRSTx509ProxyGetTimes(char *, char *, char *, time_t *, time_t *); +int GRSTx509CreateProxyRequest(char **, char **, char *); +int GRSTx509MakeProxyRequest(char **, char *, char *, char *); + +char *GRSTx509MakeDelegationID(void); + +#ifndef GRST_NO_OPENSSL +int GRSTx509StringToChain(STACK_OF(X509) **, char *); +char *GRSTx509MakeProxyFileName(char *, STACK_OF(X509) *); +#endif + +int GRSTx509CacheProxy(char *, char *, char *, char *); + +#define GRST_HEADFILE "gridsitehead.txt" +#define GRST_FOOTFILE "gridsitefoot.txt" +#define GRST_ADMIN_FILE "gridsite-admin.cgi" + +typedef struct { char *text; + void *next; } GRSThttpCharsList; + +typedef struct { size_t size; + GRSThttpCharsList *first; + GRSThttpCharsList *last; } GRSThttpBody; + +void GRSThttpBodyInit(GRSThttpBody *); +void GRSThttpPrintf(GRSThttpBody *, char *, ...); +int GRSThttpCopy(GRSThttpBody *, char *); +void GRSThttpWriteOut(GRSThttpBody *); +int GRSThttpPrintHeaderFooter(GRSThttpBody *, char *, char *); +int GRSThttpPrintHeader(GRSThttpBody *, char *); +int GRSThttpPrintFooter(GRSThttpBody *, char *); +char *GRSThttpGetCGI(char *); + +time_t GRSTasn1TimeToTimeT(unsigned char *, size_t); +int GRSTasn1SearchTaglist(struct GRSTasn1TagList taglist[], int, char *); +#ifndef GRST_NO_OPENSSL +int GRSTasn1ParseDump(BIO *, unsigned char *, long, + struct GRSTasn1TagList taglist[], int, int *); +#endif +int GRSTasn1GetX509Name(char *, int, char *, char *, + struct GRSTasn1TagList taglist[], int); + +int GRSThtcpNOPrequestMake(char **, int *, unsigned int); +int GRSThtcpNOPresponseMake(char **, int *, unsigned int); +int GRSThtcpTSTrequestMake(char **, int *, unsigned int, char *, char *, char *); +int GRSThtcpTSTresponseMake(char **, int *, unsigned int, char *, char *, char *); +int GRSThtcpMessageParse(GRSThtcpMessage *, char *, int); diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_asn1.c b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_asn1.c new file mode 100644 index 0000000000000000000000000000000000000000..459ad0e59f999161f933b5b5de5db3b155efff44 --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_asn1.c @@ -0,0 +1,535 @@ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <stdio.h> +#include <string.h> + +#ifndef GRST_NO_OPENSSL +#include <openssl/x509_vfy.h> +#include <openssl/err.h> +#include <openssl/pem.h> + +#include <openssl/buffer.h> +#include <openssl/objects.h> +#include <openssl/asn1.h> +#endif + +#ifdef SUNCC +time_t timegm (struct tm *tm) { + time_t ret; + char *tz; + + tz = getenv("TZ"); + setenv("TZ", "", 1); + tzset(); + ret = mktime(tm); + if (tz) + setenv("TZ", tz, 1); + else + unsetenv("TZ"); + tzset(); + return ret; +} +#endif + +#include "gridsite.h" + +#ifdef R__SSL_GE_098 +#define SSLARG const unsigned char** +#else +#define SSLARG unsigned char** +#endif + +/// ASN1 time string (in a char *) to time_t +/** + * (Use ASN1_STRING_data() to convert ASN1_GENERALIZEDTIME to char * if + * necessary) + */ + +time_t GRSTasn1TimeToTimeT(unsigned char *asn1time, size_t len) +{ + char zone; + struct tm time_tm; + + if (len == 0) len = strlen((char*)asn1time); + + if ((len != 13) && (len != 15)) return 0; /* dont understand */ + + if ((len == 13) && + ((sscanf((char*)asn1time, "%02d%02d%02d%02d%02d%02d%c", + &(time_tm.tm_year), + &(time_tm.tm_mon), + &(time_tm.tm_mday), + &(time_tm.tm_hour), + &(time_tm.tm_min), + &(time_tm.tm_sec), + &zone) != 7) || (zone != 'Z'))) return 0; /* dont understand */ + + if ((len == 15) && + ((sscanf((char*)asn1time, "20%02d%02d%02d%02d%02d%02d%c", + &(time_tm.tm_year), + &(time_tm.tm_mon), + &(time_tm.tm_mday), + &(time_tm.tm_hour), + &(time_tm.tm_min), + &(time_tm.tm_sec), + &zone) != 7) || (zone != 'Z'))) return 0; /* dont understand */ + + /* time format fixups */ + + if (time_tm.tm_year < 90) time_tm.tm_year += 100; + --(time_tm.tm_mon); + + return timegm(&time_tm); +} + +/* this function is taken from OpenSSL without modification */ + +static int asn1_print_info(BIO *bp, int tag, int xclass, int constructed, + int indent) + { + static const char fmt[]="%-18s"; + static const char fmt2[]="%2d %-15s"; + char str[128]; + const char *p,*p2=NULL; + + if (constructed & V_ASN1_CONSTRUCTED) + p="cons: "; + else + p="prim: "; + if (BIO_write(bp,p,6) < 6) goto err; +#if OPENSSL_VERSION_NUMBER >= 0x0090701fL + BIO_indent(bp,indent,128); +#endif + + p=str; + if ((xclass & V_ASN1_PRIVATE) == V_ASN1_PRIVATE) + sprintf(str,"priv [ %d ] ",tag); + else if ((xclass & V_ASN1_CONTEXT_SPECIFIC) == V_ASN1_CONTEXT_SPECIFIC) + sprintf(str,"cont [ %d ]",tag); + else if ((xclass & V_ASN1_APPLICATION) == V_ASN1_APPLICATION) + sprintf(str,"appl [ %d ]",tag); + else p = ASN1_tag2str(tag); + + if (p2 != NULL) + { + if (BIO_printf(bp,fmt2,tag,p2) <= 0) goto err; + } + else + { + if (BIO_printf(bp,fmt,p) <= 0) goto err; + } + return(1); +err: + return(0); + } + +static void GRSTasn1AddToTaglist(struct GRSTasn1TagList taglist[], + int maxtag, int *lasttag, + char *treecoords, int start, int headerlength, + int length, int tag) +{ + if ((strlen(treecoords) > GRST_ASN1_MAXCOORDLEN) || + (*lasttag + 1 > maxtag)) return; + + ++(*lasttag); + + strncpy(taglist[*lasttag].treecoords, treecoords, GRST_ASN1_MAXCOORDLEN+1); + taglist[*lasttag].start = start; + taglist[*lasttag].headerlength = headerlength; + taglist[*lasttag].length = length; + taglist[*lasttag].tag = tag; +} + +int GRSTasn1SearchTaglist(struct GRSTasn1TagList taglist[], + int lasttag, char *treecoords) +{ + int i; + + for (i=0; i <= lasttag; ++i) + { + if (strcmp(treecoords, taglist[i].treecoords) == 0) return i; + } + + return -1; +} + +static int GRSTasn1PrintPrintable(BIO *bp, char *str, int length) +{ + int ret = 0; + char *dup, *p; + + dup = (char*)malloc(length); + strncpy(dup,str,length); + + for (p=dup; *p != '\0'; ++p) if ((*p < ' ') || (*p > '~')) *p = '.'; + + if (bp != NULL) ret = BIO_write(bp, dup, strlen(dup)); + + free(dup); + + return ret; +} + +static int GRSTasn1Parse2(BIO *bp, unsigned char **pp, long length, int offset, + int depth, int indent, int dump, char *treecoords, + struct GRSTasn1TagList taglist[], int maxtag, int *lasttag) + { + int sibling = 0; + char sibtreecoords[512]; + + unsigned char *p,*ep,*tot,*op,*opp; + long len; + int tag,xclass,ret=0; + int nl,hl,j,r; + ASN1_OBJECT *o=NULL; + ASN1_OCTET_STRING *os=NULL; + int dump_indent; + + + dump_indent = 6; /* Because we know BIO_dump_indent() */ + p= *pp; + tot=p+length; + op=p-1; + while ((p < tot) && (op < p)) + { + op=p; + j=ASN1_get_object((SSLARG)&p,&len,&tag,&xclass,length); + + if (j & 0x80) + { + if ((bp != NULL) && + (BIO_write(bp,"Error in encoding\n",18) <= 0)) + goto end; + ret=0; + goto end; + } + hl=(p-op); + length-=hl; + + ++sibling; + sprintf(sibtreecoords, "%s-%d", treecoords, sibling); + + GRSTasn1AddToTaglist(taglist, maxtag, lasttag, sibtreecoords, + (int)offset+(int)(op - *pp), + (int) hl, len, tag); + + if (bp != NULL) + { + BIO_printf(bp, " %s %ld %d %ld %d ", sibtreecoords, + (long)offset+(long)(op - *pp), hl, len, tag); + + GRSTasn1PrintPrintable(bp, (char*)p, +// &((*pp)[(long)offset+(long)(op - *pp)+hl]), + (len > 30) ? 30 : len); + + BIO_printf(bp, "\n"); + } + + + /* if j == 0x21 it is a constructed indefinite length object */ + if ((bp != NULL) && + (BIO_printf(bp,"%5ld:",(long)offset+(long)(op- *pp)) + <= 0)) goto end; + + if (j != (V_ASN1_CONSTRUCTED | 1)) + { + if ((bp != NULL) && + (BIO_printf(bp,"d=%-2d hl=%ld l=%4ld ", + depth,(long)hl,len) <= 0)) + goto end; + } + else + { + if ((bp != NULL) && + (BIO_printf(bp,"d=%-2d hl=%ld l=inf ", + depth,(long)hl) <= 0)) + goto end; + } + if ((bp != NULL) && + !asn1_print_info(bp,tag,xclass,j,(indent)?depth:0)) + goto end; + if (j & V_ASN1_CONSTRUCTED) + { + ep=p+len; + if ((bp != NULL) && + (BIO_write(bp,"\n",1) <= 0)) goto end; + if (len > length) + { + if (bp != NULL) BIO_printf(bp, + "length is greater than %ld\n",length); + ret=0; + goto end; + } + if ((j == 0x21) && (len == 0)) + { + for (;;) + { + r=GRSTasn1Parse2(bp,&p,(long)(tot-p), + offset+(p - *pp),depth+1, + indent,dump,sibtreecoords, + taglist, maxtag, lasttag); + if (r == 0) { ret=0; goto end; } + if ((r == 2) || (p >= tot)) break; + } + } + else + while (p < ep) + { + r=GRSTasn1Parse2(bp,&p,(long)len, + offset+(p - *pp),depth+1, + indent,dump,sibtreecoords, + taglist, maxtag, lasttag); + if (r == 0) { ret=0; goto end; } + } + } + else if (xclass != 0) + { + p+=len; + if ((bp != NULL) && + (BIO_write(bp,"\n",1) <= 0)) goto end; + } + else + { + nl=0; + if ( (tag == V_ASN1_PRINTABLESTRING) || + (tag == V_ASN1_T61STRING) || + (tag == V_ASN1_IA5STRING) || + (tag == V_ASN1_VISIBLESTRING) || + (tag == V_ASN1_UTCTIME) || + (tag == V_ASN1_GENERALIZEDTIME)) + { + if ((bp != NULL) && + (BIO_write(bp,":",1) <= 0)) goto end; + if ((len > 0) && (bp != NULL) && + BIO_write(bp,(char *)p,(int)len) + != (int)len) + goto end; + } + else if (tag == V_ASN1_OBJECT) + { + opp=op; + if (d2i_ASN1_OBJECT(&o,(SSLARG)&opp,len+hl) != NULL) + { + if (bp != NULL) + { + if (BIO_write(bp,":",1) <= 0) goto end; + i2a_ASN1_OBJECT(bp,o); + } + } + else + { + if ((bp != NULL) && + (BIO_write(bp,":BAD OBJECT",11) <= 0)) + goto end; + } + } + else if (tag == V_ASN1_BOOLEAN) + { + int ii; + + opp=op; + ii=d2i_ASN1_BOOLEAN(NULL,(SSLARG)&opp,len+hl); + if (ii < 0) + { + if ((bp != NULL) && + (BIO_write(bp,"Bad boolean\n",12))) + goto end; + } + if (bp != NULL) BIO_printf(bp,":%d",ii); + } + else if (tag == V_ASN1_BMPSTRING) + { + /* do the BMP thang */ + } + else if (tag == V_ASN1_OCTET_STRING) + { + opp=op; + os=d2i_ASN1_OCTET_STRING(NULL,(SSLARG) &opp,len+hl); + if (os != NULL) + { + opp=os->data; + + if (os->length > 0) + { + if ((bp != NULL) && + (BIO_write(bp,":",1) <= 0)) + goto end; + if ((bp != NULL) && + (GRSTasn1PrintPrintable(bp, + (char*)opp, + os->length) <= 0)) + goto end; + } + + M_ASN1_OCTET_STRING_free(os); + os=NULL; + } + } + else if (tag == V_ASN1_INTEGER) + { + ASN1_INTEGER *bs; + int i; + + opp=op; + bs=d2i_ASN1_INTEGER(NULL,(SSLARG)&opp,len+hl); + if (bs != NULL) + { + if ((bp != NULL) && + (BIO_write(bp,":",1) <= 0)) goto end; + if (bs->type == V_ASN1_NEG_INTEGER) + if ((bp != NULL) && + (BIO_write(bp,"-",1) <= 0)) + goto end; + for (i=0; i<bs->length; i++) + { + if ((bp != NULL) && + (BIO_printf(bp,"%02X", + bs->data[i]) <= 0)) + goto end; + } + if (bs->length == 0) + { + if ((bp != NULL) && + (BIO_write(bp,"00",2) <= 0)) + goto end; + } + } + else + { + if ((bp != NULL) && + (BIO_write(bp,"BAD INTEGER",11) <= 0)) + goto end; + } + M_ASN1_INTEGER_free(bs); + } + else if (tag == V_ASN1_ENUMERATED) + { + ASN1_ENUMERATED *bs; + int i; + + opp=op; + bs=d2i_ASN1_ENUMERATED(NULL,(SSLARG) &opp,len+hl); + if (bs != NULL) + { + if ((bp != NULL) && + (BIO_write(bp,":",1) <= 0)) goto end; + if (bs->type == V_ASN1_NEG_ENUMERATED) + if ((bp != NULL) && + (BIO_write(bp,"-",1) <= 0)) + goto end; + for (i=0; i<bs->length; i++) + { + if ((bp != NULL) && + (BIO_printf(bp,"%02X", + bs->data[i]) <= 0)) + goto end; + } + if (bs->length == 0) + { + if ((bp != NULL) && + (BIO_write(bp,"00",2) <= 0)) + goto end; + } + } + else + { + if ((bp != NULL) && + (BIO_write(bp,"BAD ENUMERATED",11) <= 0)) + goto end; + } + M_ASN1_ENUMERATED_free(bs); + } + else if (len > 0 && dump) + { + if (!nl) + { + if ((bp != NULL) && + (BIO_write(bp,"\n",1) <= 0)) + goto end; + } + if ((bp != NULL) && + (BIO_dump_indent(bp,(char *)p, + ((dump == -1 || dump > len)?len:dump), + dump_indent) <= 0)) + goto end; + nl=1; + } + + if (!nl) + { + if ((bp != NULL) && + (BIO_write(bp,"\n",1) <= 0)) goto end; + } + p+=len; + if ((tag == V_ASN1_EOC) && (xclass == 0)) + { + ret=2; /* End of sequence */ + goto end; + } + } + + length-=len; + } + ret=1; +end: + if (o != NULL) ASN1_OBJECT_free(o); + if (os != NULL) M_ASN1_OCTET_STRING_free(os); + *pp=p; + return(ret); + } + +int GRSTasn1ParseDump(BIO *bp, unsigned char *pp, long len, + struct GRSTasn1TagList taglist[], + int maxtag, int *lasttag) + { + return(GRSTasn1Parse2(bp,&pp,len,0,0,0,0,"", + taglist, maxtag, lasttag)); + } + +int GRSTasn1GetX509Name(char *x509name, int maxlength, char *coords, + char *asn1string, + struct GRSTasn1TagList taglist[], int lasttag) +{ + int i, iobj, istr, n, len = 0; + ASN1_OBJECT *obj = NULL; + unsigned char coordstmp[81], *q; + const unsigned char *shortname; + + for (i=1; ; ++i) + { + snprintf((char*)coordstmp, sizeof(coordstmp), coords, i, 1); + iobj = GRSTasn1SearchTaglist(taglist, lasttag, (char*)coordstmp); + if (iobj < 0) break; + + snprintf((char*)coordstmp, sizeof(coordstmp), coords, i, 2); + istr = GRSTasn1SearchTaglist(taglist, lasttag, (char*)coordstmp); + if (istr < 0) break; + + q = (unsigned char*) &asn1string[taglist[iobj].start]; + d2i_ASN1_OBJECT(&obj, (SSLARG) &q, taglist[iobj].length + + taglist[iobj].headerlength); + + n = OBJ_obj2nid(obj); +// free obj now? +// if (obj) free (obj); + shortname = (const unsigned char*) OBJ_nid2sn(n); + + if (len + 2 + strlen((char*)shortname) + taglist[istr].length >= maxlength) + { + x509name[0] = '\0'; + return GRST_RET_FAILED; + } + + sprintf(&x509name[len], "/%s=%.*s", shortname, + taglist[istr].length, + &asn1string[taglist[istr].start+taglist[istr].headerlength]); + len += 2 + strlen((char*)shortname) + taglist[istr].length; + } + + x509name[len] = '\0'; + + return (x509name[0] != '\0') ? GRST_RET_OK : GRST_RET_FAILED; +} diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_err.c b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_err.c new file mode 100644 index 0000000000000000000000000000000000000000..14adf13d0137edf15b31db946f700fe0cf53852a --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_err.c @@ -0,0 +1,43 @@ +/* + Copyright (c) 2002-6, Andrew McNab, University of Manchester + All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, are permitted provided that the following + conditions are met: + + o Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + o Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + --------------------------------------------------------------- + For more information about GridSite: http://www.gridsite.org/ + --------------------------------------------------------------- +*/ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include "gridsite.h" + +void (*GRSTerrorLogFunc)(char *, int, int, char *, ...) = NULL; + diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_gacl.c b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_gacl.c new file mode 100644 index 0000000000000000000000000000000000000000..2cf876c99ecc95d59c6a49e3d7ff8138e1284b4b --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_gacl.c @@ -0,0 +1,1419 @@ +/* + Copyright (c) 2002-6, Andrew McNab, University of Manchester + All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, are permitted provided that the following + conditions are met: + + o Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + o Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/*---------------------------------------------------------------* + * For more information about GridSite: http://www.gridsite.org/ * + *---------------------------------------------------------------*/ + + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <unistd.h> +#include <string.h> +#include <dirent.h> +#include <fcntl.h> +#include <ctype.h> +#include <strings.h> + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#endif +#include <fnmatch.h> + +#include <libxml/xmlmemory.h> +#include <libxml/tree.h> +#include <libxml/parser.h> + +#include "gridsite.h" + +#ifdef SUNCC +/* SUN does not know strsep .... */ + +static char* strsep(char** str, const char* delims) +{ + char* token; + if (*str==NULL) { + /* No more tokens */ + return NULL; + } + + token=*str; + while (**str!='\0') { + if (strchr(delims,**str)!=NULL) { + **str='\0'; + (*str)++; + return token; + } + (*str)++; + } + /* There is no other token */ + *str=NULL; + return token; +} + +#endif + +/* * + * Global variables, shared by all GACL functions but private to libgacl * + * */ + +char *grst_perm_syms[] = { "none", + "read", + "exec", + "list", + "write", + "admin", + NULL }; + +GRSTgaclPerm grst_perm_vals[] = { GRST_PERM_NONE, + GRST_PERM_READ, + GRST_PERM_EXEC, + GRST_PERM_LIST, + GRST_PERM_WRITE, + GRST_PERM_ADMIN, + -1 }; + +int GRSTgaclInit(void) +{ + xmlInitParser(); + + LIBXML_TEST_VERSION + + xmlKeepBlanksDefault(0); + + return 1; +} + +/* declare these two private functions at the start */ + +GRSTgaclAcl *GRSTgaclAclParse(xmlDocPtr, xmlNodePtr, GRSTgaclAcl *); +GRSTgaclAcl *GRSTxacmlAclParse(xmlDocPtr, xmlNodePtr, GRSTgaclAcl *); + +/* * + * Functions to manipulate GRSTgaclCred structures * + * */ + +GRSTgaclCred *GRSTgaclCredCreate(char *auri_prefix, char *auri_suffix) +/* + GRSTgaclCredCreate - allocate a new GRSTgaclCred structure, and return + it's pointer or NULL on (malloc) error. +*/ +{ + int i; + char auri[16384]; + GRSTgaclCred *newcred; + + if ((auri_prefix != NULL) && (auri_suffix == NULL)) + sprintf(auri,"%s",auri_prefix); + else if ((auri_prefix == NULL) && (auri_suffix != NULL)) + sprintf(auri,"%s",auri_suffix); + else if ((auri_prefix != NULL) && (auri_suffix != NULL)) + sprintf(auri, "%s%s", auri_prefix, auri_suffix); + else return NULL; + + for (i=0; (auri[i] != '\0') && isspace(auri[i]); ++i) ; /* leading space */ + + for (i=strlen(auri) - 1; (i >= 0) && isspace(auri[i]); --i) + auri[i]='\0'; /* trailing space */ + + newcred = malloc(sizeof(GRSTgaclCred)); + if (newcred == NULL) + { + return NULL; + } + + newcred->auri = auri; + newcred->delegation = 0; + newcred->nist_loa = 0; + newcred->notbefore = 0; + newcred->notafter = 0; + newcred->next = NULL; + + return newcred; +} + +GRSTgaclCred *GRSTgaclCredNew(char *type) +/* + GRSTgaclCredNew - allocate a new GRSTgaclCred structure, and return + it's pointer or NULL on (malloc) error. +*/ +{ + if (type == NULL) return NULL; + + if ((strcmp(type, "person" ) == 0) || + (strcmp(type, "voms" ) == 0) || + (strcmp(type, "dn-list") == 0) || + (strcmp(type, "dns" ) == 0) || + (strcmp(type, "level" ) == 0)) return GRSTgaclCredCreate("", NULL); + + if (strcmp(type, "any-user") == 0) + return GRSTgaclCredCreate("gacl:", "any-user"); + + if (strcmp(type, "auth-user") == 0) + return GRSTgaclCredCreate("gacl:", "auth-user"); + + return NULL; +} + +int GRSTgaclCredAddValue(GRSTgaclCred *cred, char *name, char *rawvalue) +/* + GRSTgaclCredAddValue - add a name/value pair to a GRSTgaclCred +*/ +{ + int i; + char *value, *encoded_value; + + if ((cred == NULL) || (cred->auri == NULL)) return 0; + free(cred->auri); + cred->auri = NULL; + + /* no leading or trailing space in value */ + + value = rawvalue; + while ((*value != '\0') && isspace(*value)) ++value; + + value = strdup(value); + for (i=strlen(value) - 1; (i >= 0) && isspace(value[i]); --i) value[i]='\0'; + + encoded_value = GRSThttpUrlMildencode(value); + + if (strcmp(name, "dn") == 0) + { + sprintf(cred->auri, "dn:%s", encoded_value); + free(value); + free(encoded_value); + return 1; + } + else if (strcmp(name, "fqan") == 0) + { + sprintf(cred->auri, "fqan:%s", encoded_value); + free(value); + free(encoded_value); + return 1; + } + else if (strcmp(name, "url") == 0) + { + sprintf(cred->auri, "%s", encoded_value); + free(value); + free(encoded_value); + return 1; + } + else if (strcmp(name, "hostname") == 0) + { + sprintf(cred->auri, "dns:%s", encoded_value); + free(value); + free(encoded_value); + return 1; + } + else if (strcmp(name, "nist-loa") == 0) + { + sprintf(cred->auri, "nist-loa:%s", encoded_value); + free(value); + free(encoded_value); + return 1; + } + + free(value); + free(encoded_value); + return 0; +} + +int GRSTgaclCredFree(GRSTgaclCred *cred) +/* + GRSTgaclCredFree - free memory structures of a GRSTgaclCred, + returning 1 always! +*/ +{ + if (cred == NULL) return 1; + + if (cred->auri != NULL) free(cred->auri); + free(cred); + + return 1; +} + +static int GRSTgaclCredsFree(GRSTgaclCred *firstcred) +/* + GRSTgaclCredsFree - free a cred and all the creds in its *next chain +*/ +{ + if (firstcred == NULL) return 0; + + if (firstcred->next != NULL) GRSTgaclCredsFree(firstcred->next); + + return GRSTgaclCredFree(firstcred); +} + +static int GRSTgaclCredInsert(GRSTgaclCred *firstcred, GRSTgaclCred *newcred) +/* + GRSTgaclCredInsert - insert a cred in the *next chain of firstcred + + FOR THE MOMENT THIS JUST APPENDS! +*/ +{ + if (firstcred == NULL) return 0; + + if (firstcred->next == NULL) + { + firstcred->next = newcred; + return 1; + } + + return GRSTgaclCredInsert(firstcred->next, newcred); +} + +int GRSTgaclEntryAddCred(GRSTgaclEntry *entry, GRSTgaclCred *cred) +/* + GRSTaddCred - add a new credential to an existing entry, returning 1 + on success or 0 on error +*/ +{ + if (entry == NULL) return 0; + + if (entry->firstcred == NULL) + { + entry->firstcred = cred; + return 1; + } + else return GRSTgaclCredInsert(entry->firstcred, cred); +} + +static int GRSTgaclCredRemoveCred(GRSTgaclCred *firstcred, GRSTgaclCred *oldcred) +/* + (Private) + + GRSTgaclCredRemoveCred - remove a cred in the *next chain of firstcred + and relink the chain +*/ +{ + if (firstcred == NULL) return 0; + + return 1; +} + +int GRSTgaclEntryDelCred(GRSTgaclEntry *entry, GRSTgaclCred *cred) +/* + GRSTgaclEntryDelCred - remove a new cred from an entry, returning 1 + on success (or absense) or 0 on error. +*/ +{ + if (entry == NULL) return 0; + + return GRSTgaclCredRemoveCred(entry->firstcred, cred); +} + +int GRSTgaclCredPrint(GRSTgaclCred *cred, FILE *fp) +/* + GRSTgaclCredPrint - print a credential and the AURI value it contains +*/ +{ + char *q; + + if ((cred->auri != NULL) && (cred->auri[0] != '\0')) + { + fprintf(fp, "<cred>\n<auri>"); + + for (q=cred->auri; *q != '\0'; ++q) + if (*q == '<') fputs("<", fp); + else if (*q == '>') fputs(">", fp); + else if (*q == '&') fputs("&" , fp); + else if (*q == '\'') fputs("'", fp); + else if (*q == '"') fputs(""", fp); + else fputc(*q, fp); + + fprintf(fp, "</auri>\n"); + + if (cred->nist_loa > 0) + fprintf(fp, "<nist-loa>%d</nist-loa>\n", cred->nist_loa); + + if (cred->delegation > 0) + fprintf(fp, "<delegation>%d</delegation>\n", cred->delegation); + + fprintf(fp, "</cred>\n"); + + return 1; + } + + return 0; +} + +int GRSTgaclCredCmpAuri(GRSTgaclCred *cred1, GRSTgaclCred *cred2) +/* + GRSTgaclCredCmp - compare two credentials for exact match in AURI values + (this means a string match, not just any-user=DN etc) +*/ +{ + if ((cred1 == NULL) && (cred2 == NULL)) return 0; + + if (cred1 == NULL) return -1; + + if (cred2 == NULL) return 1; + + if ((cred1->auri == NULL) && (cred2->auri == NULL)) return 0; + + if (cred1->auri == NULL) return -1; + + if (cred2->auri == NULL) return 1; + + return strcmp(cred1->auri, cred2->auri); +} + +/* * + * Functions to manipulate GRSTgaclEntry structures * + * */ + +GRSTgaclEntry *GRSTgaclEntryNew(void) +/* + GRSTgaclEntryNew - allocate space for a new entry, returning its pointer + or NULL on failure. +*/ +{ + GRSTgaclEntry *newentry; + + newentry = (GRSTgaclEntry *) malloc(sizeof(GRSTgaclEntry)); + if (newentry == NULL) return NULL; + + newentry->firstcred = NULL; + newentry->allowed = 0; + newentry->denied = 0; + newentry->next = NULL; + + return newentry; +} + +int GRSTgaclEntryFree(GRSTgaclEntry *entry) +/* + GRSTgaclEntryFree - free up space used by an entry (always returns 1) +*/ +{ + if (entry == NULL) return 1; + + GRSTgaclCredsFree(entry->firstcred); + + free(entry); + + return 1; +} + +static int GRSTgaclEntriesFree(GRSTgaclEntry *entry) +/* + GRSTgaclEntriesFree - free up entry and all entries linked to in its *next + chain +*/ +{ + if (entry == NULL) return 0; + + if (entry->next != NULL) GRSTgaclEntriesFree(entry->next); + + return GRSTgaclEntryFree(entry); +} + +static int GRSTgaclEntryInsert(GRSTgaclEntry *firstentry, GRSTgaclEntry *newentry) +/* + GRSTgaclEntryInsert - insert an entry in the *next chain of firstentry + + FOR THE MOMENT THIS JUST APPENDS +*/ +{ + if (firstentry == NULL) return 0; + + if (firstentry->next == NULL) + { + firstentry->next = newentry; + return 1; + } + + return GRSTgaclEntryInsert(firstentry->next, newentry); +} + +int GRSTgaclAclAddEntry(GRSTgaclAcl *acl, GRSTgaclEntry *entry) +/* + GRSTgaclAclAddEntry - add a new entry to an existing acl, returning 1 + on success or 0 on error +*/ +{ + if (acl == NULL) return 0; + + if (acl->firstentry == NULL) + { + acl->firstentry = entry; + return 1; + } + else return GRSTgaclEntryInsert(acl->firstentry, entry); +} + +int GRSTgaclEntryPrint(GRSTgaclEntry *entry, FILE *fp) +{ + GRSTgaclCred *cred; + GRSTgaclPerm i; + + fputs("<entry>\n", fp); + + for (cred = entry->firstcred; cred != NULL; cred = cred->next) + GRSTgaclCredPrint(cred, fp); + + if (entry->allowed) + { + fputs("<allow>", fp); + + for (i=GRST_PERM_READ; i <= GRST_PERM_ADMIN; ++i) + if ((entry->allowed) & i) GRSTgaclPermPrint(i, fp); + + fputs("</allow>\n", fp); + } + + + if (entry->denied) + { + fputs("<deny>", fp); + + for (i=GRST_PERM_READ; i <= GRST_PERM_ADMIN; ++i) + if (entry->denied & i) GRSTgaclPermPrint(i, fp); + + fputs("</deny>\n", fp); + } + + fputs("</entry>\n", fp); + + return 1; +} + +/* * + * Functions to manipulate GRSTgaclPerm items * + * */ + +int GRSTgaclPermPrint(GRSTgaclPerm perm, FILE *fp) +{ + GRSTgaclPerm i; + + for (i=GRST_PERM_READ; grst_perm_syms[i] != NULL; ++i) + if (perm == grst_perm_vals[i]) + { + fprintf(fp, "<%s/>", grst_perm_syms[i]); + return 1; + } + + return 0; +} + +int GRSTgaclEntryAllowPerm(GRSTgaclEntry *entry, GRSTgaclPerm perm) +{ + entry->allowed = entry->allowed | perm; + + return 1; +} + +int GRSTgaclEntryUnallowPerm(GRSTgaclEntry *entry, GRSTgaclPerm perm) +{ + entry->allowed = entry->allowed & ~perm; + + return 1; +} + +int GRSTgaclEntryDenyPerm(GRSTgaclEntry *entry, GRSTgaclPerm perm) +{ + entry->denied = entry->denied | perm; + + return 1; +} + +int GRSTgaclEntryUndenyPerm(GRSTgaclEntry *entry, GRSTgaclPerm perm) +{ + entry->denied = entry->denied & ~perm; + + return 1; +} + +char *GRSTgaclPermToChar(GRSTgaclPerm perm) +/* + GRSTgaclPermToChar - return char * or NULL corresponding to most significant + set bit of perm. +*/ +{ + char *p = NULL; + GRSTgaclPerm i; + + for (i=0; grst_perm_syms[i] != NULL; ++i) + if (perm & grst_perm_vals[i]) p = grst_perm_syms[i]; + + return p; +} + +GRSTgaclPerm GRSTgaclPermFromChar(char *s) +/* + GRSTgaclPermToChar - return access perm corresponding to symbol s[] +*/ +{ + GRSTgaclPerm i; + + for (i=0; grst_perm_syms[i] != NULL; ++i) + if (strcasecmp(grst_perm_syms[i], s) == 0) return grst_perm_vals[i]; + + return -1; +} + +/* * + * Functions to manipulate GRSTgaclAcl structures * + * */ + +GRSTgaclAcl *GRSTgaclAclNew(void) +/* + GRSTgaclAclNew - allocate a new acl and return its pointer (or NULL + on failure.) +*/ +{ + GRSTgaclAcl *newacl; + + newacl = (GRSTgaclAcl *) malloc(sizeof(GRSTgaclAcl)); + if (newacl == NULL) return NULL; + + newacl->firstentry = NULL; + + return newacl; +} + +int GRSTgaclAclFree(GRSTgaclAcl *acl) +/* + GRSTgaclAclFree - free up space used by *acl. Always returns 1. +*/ +{ + if (acl == NULL) return 1; + + GRSTgaclEntriesFree(acl->firstentry); + + return 1; +} + +int GRSTgaclAclPrint(GRSTgaclAcl *acl, FILE *fp) +{ + GRSTgaclEntry *entry; + + fputs("<gacl version=\"0.9.0\">\n", fp); + + for (entry = acl->firstentry; entry != NULL; entry = entry->next) + GRSTgaclEntryPrint(entry, fp); + + fputs("</gacl>\n", fp); + + return 1; +} + +int GRSTgaclAclSave(GRSTgaclAcl *acl, char *filename) +{ + int ret; + FILE *fp; + + fp = fopen(filename, "w"); + if (fp == NULL) return 0; + + fputs("<?xml version=\"1.0\"?>\n", fp); + + ret = GRSTgaclAclPrint(acl, fp); + + fclose(fp); + + return ret; +} + +/* * + * Functions for loading and parsing XML using libxml * + * */ + +// need to check these for libxml memory leaks? - what needs to be freed? + +static GRSTgaclCred *GRSTgaclCredParse(xmlNodePtr cur) +/* + GRSTgaclCredParse - parse a credential stored in the libxml structure cur, + returning it as a pointer or NULL on error. +*/ +{ + xmlNodePtr cur2; + GRSTgaclCred *cred = NULL; + + /* generic AURI creds first */ + + if (strcmp((char *) cur->name, "cred") == 0) + { + for (cur2 = cur->xmlChildrenNode; cur2 != NULL; cur2=cur2->next) + { + if (!xmlIsBlankNode(cur2) && + (strcmp((char *) cur2->name, "auri") == 0)) + { + if (cred != NULL) /* multiple AURI */ + { + GRSTgaclCredFree(cred); + cred = NULL; + return NULL; + } + + cred = GRSTgaclCredCreate((char *) xmlNodeGetContent(cur2),NULL); + } + } + + if (cred == NULL) return NULL; + + for (cur2 = cur->xmlChildrenNode; cur2 != NULL; cur2=cur2->next) + { + if (xmlIsBlankNode(cur2)) continue; + + if (strcmp((char *) cur2->name, "nist-loa") == 0) + { + cred->nist_loa = atoi((char *) xmlNodeGetContent(cur2)); + } + else if (strcmp((char *) cur2->name, "delegation") == 0) + { + cred->delegation = atoi((char *) xmlNodeGetContent(cur2)); + } + } + + return cred; + } + + /* backwards compatibility */ + + cred = GRSTgaclCredNew((char *) cur->name); + + for (cur2 = cur->xmlChildrenNode; cur2 != NULL; cur2=cur2->next) + { + if (!xmlIsBlankNode(cur2)) + GRSTgaclCredAddValue(cred, (char *) cur2->name, + (char *) xmlNodeGetContent(cur2)); + } + + return cred; +} + +static GRSTgaclEntry *GRSTgaclEntryParse(xmlNodePtr cur) +/* + GRSTgaclEntryParse - parse an entry stored in the libxml structure cur, + returning it as a pointer or NULL on error. +*/ +{ + int i; + xmlNodePtr cur2; + GRSTgaclEntry *entry; + GRSTgaclCred *cred; + + if (xmlStrcmp(cur->name, (const xmlChar *) "entry") != 0) return NULL; + + cur = cur->xmlChildrenNode; + + entry = GRSTgaclEntryNew(); + + while (cur != NULL) + { + if (xmlIsBlankNode(cur)) + { + cur=cur->next; + continue; + } + else if (xmlStrcmp(cur->name, (const xmlChar *) "allow") == 0) + { + for (cur2 = cur->xmlChildrenNode; cur2 != NULL; cur2=cur2->next) + if (!xmlIsBlankNode(cur2)) + { + for (i=0; grst_perm_syms[i] != NULL; ++i) + if (xmlStrcmp(cur2->name, + (const xmlChar *) grst_perm_syms[i]) == 0) + GRSTgaclEntryAllowPerm(entry, grst_perm_vals[i]); + } + } + else if (xmlStrcmp(cur->name, (const xmlChar *) "deny") == 0) + { + for (cur2 = cur->xmlChildrenNode; cur2 != NULL; cur2=cur2->next) + if (!xmlIsBlankNode(cur2)) + { + for (i=0; grst_perm_syms[i] != NULL; ++i) + if (xmlStrcmp(cur2->name, + (const xmlChar *) grst_perm_syms[i]) == 0) + GRSTgaclEntryDenyPerm(entry, grst_perm_vals[i]); + } + } + else if ((cred = GRSTgaclCredParse(cur)) != NULL) + { + if (!GRSTgaclEntryAddCred(entry, cred)) + { + GRSTgaclCredFree(cred); + GRSTgaclEntryFree(entry); + return NULL; + } + } + else /* I cannot parse this - give up rather than get it wrong */ + { + GRSTgaclEntryFree(entry); + return NULL; + } + + cur=cur->next; + } + + return entry; +} + +GRSTgaclAcl *GRSTgaclAclLoadFile(char *filename) +{ + xmlDocPtr doc; + xmlNodePtr cur; + GRSTgaclAcl *acl=NULL; + + GRSTerrorLog(GRST_LOG_DEBUG, "GRSTgaclAclLoadFile() starting"); + + if (filename == NULL) + { + GRSTerrorLog(GRST_LOG_DEBUG, "GRSTgaclAclLoadFile() cannot open a NULL filename"); + return NULL; + } + + doc = xmlParseFile(filename); + if (doc == NULL) + { + GRSTerrorLog(GRST_LOG_DEBUG, "GRSTgaclAclLoadFile failed to open ACL file %s", filename); + return NULL; + } + + cur = xmlDocGetRootElement(doc); + if (cur == NULL) + { + xmlFreeDoc(doc); + GRSTerrorLog(GRST_LOG_DEBUG, "GRSTgaclAclLoadFile failed to parse root of ACL file %s", filename); + return NULL; + } + + if (!xmlStrcmp(cur->name, (const xmlChar *) "Policy")) + { + GRSTerrorLog(GRST_LOG_DEBUG, "GRSTgaclAclLoadFile parsing XACML"); + acl=GRSTxacmlAclParse(doc, cur, acl); + } + else if (!xmlStrcmp(cur->name, (const xmlChar *) "gacl")) + { + GRSTerrorLog(GRST_LOG_DEBUG, "GRSTgaclAclLoadFile parsing GACL"); + acl=GRSTgaclAclParse(doc, cur, acl); + } + else /* ACL format not recognised */ + { + xmlFreeDoc(doc); + return NULL; + } + + xmlFreeDoc(doc); + return acl; +} + +GRSTgaclAcl *GRSTgaclAclParse(xmlDocPtr doc, xmlNodePtr cur, GRSTgaclAcl *acl) +{ + GRSTgaclEntry *entry; + + cur = cur->xmlChildrenNode; + + acl = GRSTgaclAclNew(); + + while (cur != NULL) + { + if (!xmlIsBlankNode(cur)) + { + entry = GRSTgaclEntryParse(cur); + if (entry == NULL) + { + GRSTgaclAclFree(acl); + + return NULL; + } + + GRSTgaclAclAddEntry(acl, entry); + } + + cur=cur->next; + } + + return acl; +} +int GRSTgaclFileIsAcl(char *pathandfile) +/* Return 1 if filename in *pathandfile starts GRST_ACL_FILE + Return 0 otherwise. */ +{ + char *filename; + + filename = rindex(pathandfile, '/'); + if (filename == NULL) filename = pathandfile; + else filename++; + + return (strncmp((const char*)filename, GRST_ACL_FILE, sizeof(GRST_ACL_FILE) - 1) == 0); +} + +char *GRSTgaclFileFindAclname(char *pathandfile) +/* Return malloc()ed ACL filename that governs the given file or directory + (for directories, the ACL file is in the directory itself), or NULL if none + can be found. */ +{ + int len; + char *path, *file, *p; + struct stat statbuf; + + len = strlen(pathandfile); + if (len == 0) return NULL; + + path = malloc(len + sizeof(GRST_ACL_FILE) + 2); + strcpy(path, pathandfile); + + if ((stat(path, &statbuf) == 0) && + S_ISDIR(statbuf.st_mode) && + (path[len-1] != '/')) + { + strcat(path, "/"); + ++len; + } + + if (path[len-1] != '/') + { + p = rindex(pathandfile, '/'); + if (p != NULL) + { + file = &p[1]; + p = rindex(path, '/'); + sprintf(p, "/%s:%s", GRST_ACL_FILE, file); + + if (stat(path, &statbuf) == 0) return path; + + *p = '\0'; /* otherwise strip off any filename */ + } + } + + while (path[0] != '\0') + { + strcat(path, "/"); + strcat(path, GRST_ACL_FILE); + + if (stat(path, &statbuf) == 0) return path; + + p = rindex(path, '/'); + *p = '\0'; /* strip off the / we added for ACL */ + + p = rindex(path, '/'); + if (p == NULL) break; /* must start without / and we there now ??? */ + + *p = '\0'; /* strip off another layer of / */ + } + + free(path); + return NULL; +} + +GRSTgaclAcl *GRSTgaclAclLoadforFile(char *pathandfile) +/* Return ACL that governs the given file or directory (for directories, + the ACL file is in the directory itself.) */ +{ + char *path; + GRSTgaclAcl *acl; + + path = GRSTgaclFileFindAclname(pathandfile); + + if (path != NULL) + { + acl = GRSTgaclAclLoadFile(path); + free(path); + return acl; + } + + return NULL; +} + +/* * + * Functions to create and query GACLuser * + * */ + +GRSTgaclUser *GRSTgaclUserNew(GRSTgaclCred *cred) +{ + GRSTgaclUser *user; + + if (cred == NULL) return NULL; + + user = malloc(sizeof(GRSTgaclUser)); + + if (user != NULL) + { + user->firstcred = cred; + user->dnlists = NULL; + } + + return user; +} + +int GRSTgaclUserFree(GRSTgaclUser *user) +{ + if (user == NULL) return 1; + + if (user->firstcred != NULL) GRSTgaclCredsFree(user->firstcred); + + if (user->dnlists != NULL) free(user->dnlists); + + free(user); + + return 1; +} + +int GRSTgaclUserAddCred(GRSTgaclUser *user, GRSTgaclCred *cred) +{ + GRSTgaclCred *crediter; + + if ((user == NULL) || (cred == NULL)) return 0; + + if (user->firstcred == NULL) + { + user->firstcred = cred; + cred->next = NULL; /* so cannot be used to add whole lists */ + return 1; + } + + crediter = user->firstcred; + + while (crediter->next != NULL) crediter = crediter->next; + + crediter->next = cred; + cred->next = NULL; /* so cannot be used to add whole lists */ + + return 1; +} + +int GRSTgaclUserHasCred(GRSTgaclUser *user, GRSTgaclCred *cred) +/* test if the user has the given credential */ +{ + int nist_loa = 999; + GRSTgaclCred *crediter; + + if ((cred == NULL) || (cred->auri == NULL)) return 0; + + if (strcmp(cred->auri, "gacl:any-user") == 0) return 1; + + if ((user == NULL) || (user->firstcred == NULL)) return 0; + + if (strncmp((const char*)cred->auri, "dns:", 4) == 0) + { + for (crediter=user->firstcred; + crediter != NULL; + crediter = crediter->next) + if ((crediter->auri != NULL) && + (strncmp((const char*)crediter->auri, "dns:", 4) == 0)) +#ifdef SUNCC + return (fnmatch(cred->auri, crediter->auri, 0) == 0); +#else + return (fnmatch(cred->auri, crediter->auri, FNM_CASEFOLD) == 0); +#endif + + return 0; + } + + if (strcmp(cred->auri, "gacl:auth-user") == 0) + { + for (crediter=user->firstcred; + crediter != NULL; + crediter = crediter->next) + if ((crediter->auri != NULL) && + (strncmp((const char*)crediter->auri, "dn:", 3) == 0)) return 1; + + return 0; + } + + if (sscanf(cred->auri, "nist-loa:%d", &nist_loa) == 1) + { + for (crediter=user->firstcred; + crediter != NULL; + crediter = crediter->next) + if ((crediter->auri != NULL) && + (strncmp((const char*)crediter->auri, "dn:", 3) == 0) && + (crediter->nist_loa >= nist_loa)) return 1; + + return 0; + } + +/* +// can remove this once we preload DN Lists etc as AURIs? + if ((strncmp((const char*)cred->auri, "http:", 5) == 0) || + (strncmp((const char*)cred->auri, "https:", 6) == 0)) + { + return GRSTgaclDNlistHasUser(cred->auri, user); + } +*/ + /* generic AURI = AURI test */ + + for (crediter=user->firstcred; crediter != NULL; crediter = crediter->next) + if ((crediter->auri != NULL) && + (strcmp(crediter->auri, cred->auri) == 0)) return 1; + + return 0; +} + +GRSTgaclCred *GRSTgaclUserFindCredtype(GRSTgaclUser *user, char *type) +/* find the first credential of a given type for this user */ +{ + GRSTgaclCred *cred; + + if (user == NULL) return NULL; + + cred = user->firstcred; + + while (cred != NULL) + { + if ((strcmp(type, "person") == 0) && + (strncmp((const char*)cred->auri, "dn:", 3) == 0)) return cred; + + if ((strcmp(type, "voms") == 0) && + (strncmp((const char*)cred->auri, "fqan:", 5) == 0)) return cred; + + if ((strcmp(type, "dns") == 0) && + (strncmp((const char*)cred->auri, "dns:", 4) == 0)) return cred; + + if ((strcmp(type, "dn-list") == 0) && + ((strncmp((const char*)cred->auri, "http:", 5) == 0) || + (strncmp((const char*)cred->auri, "https:", 6) == 0))) return cred; + + cred = cred->next; + } + + return NULL; +} + +int GRSTgaclUserSetDNlists(GRSTgaclUser *user, char *dnlists) +{ + if (user->dnlists != NULL) free(user->dnlists); + + if (dnlists == NULL) user->dnlists = NULL; + else user->dnlists = strdup(dnlists); + + return GRSTgaclUserLoadDNlists(user, dnlists); +} + +static void recurse4dnlists(GRSTgaclUser *user, char *dir, + int recurse_level, GRSTgaclCred *dn_cred) +/* try to find file[] in dir[]. try subdirs if not found. + return full path to first found version or NULL on failure */ +{ + int fd, linestart, i; + char *mapped, *s; + char fullfilename[16384]; + size_t dn_len; + struct stat statbuf; + DIR *dirDIR; + struct dirent *file_ent; + GRSTgaclCred *cred; + + if (recurse_level >= GRST_RECURS_LIMIT) return; + + dn_len = strlen(dn_cred->auri) - 3; + + /* search this directory */ + + dirDIR = opendir(dir); + + if (dirDIR == NULL) return; + + while ((file_ent = readdir(dirDIR)) != NULL) + { + if (file_ent->d_name[0] == '.') continue; + + sprintf(fullfilename, "%s/%s", dir, file_ent->d_name); + + GRSTerrorLog(GRST_LOG_DEBUG, "recurse4dnlists opens %s", fullfilename); + + if ((fd = open(fullfilename, O_RDONLY)) < 0) + ; + else if (fstat(fd, &statbuf) != 0) + ; + else if (S_ISDIR(statbuf.st_mode)) + recurse4dnlists(user, fullfilename, recurse_level + 1, dn_cred); + else if ((S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) && + ((mapped = mmap(NULL, statbuf.st_size, + PROT_READ, MAP_PRIVATE, fd, 0)) != MAP_FAILED)) + { + linestart = 0; + + while (linestart + dn_len <= statbuf.st_size) + { + GRSTerrorLog(GRST_LOG_DEBUG, "recurse4dnlists at %ld in %s", + linestart, fullfilename); + + for (i=0; + (linestart + i < statbuf.st_size) && (i < dn_len); + ++i) + if (mapped[linestart + i] != dn_cred->auri[3+i]) break; + + GRSTerrorLog(GRST_LOG_DEBUG, "recurse4dnlists at %d %d %d %d", + linestart, i, dn_len, statbuf.st_size); + + if ((i == dn_len) && + ((linestart+i == statbuf.st_size) || + (mapped[linestart+i] == '\n') || + (mapped[linestart+i] == '\r'))) /* matched */ + { + s = GRSThttpUrlDecode(file_ent->d_name); + cred = GRSTgaclCredCreate(s, NULL); + GRSTerrorLog(GRST_LOG_DEBUG, + "recurse4dnlists adds %s", s); + free(s); + + GRSTgaclCredSetNotBefore(cred, dn_cred->notbefore); + GRSTgaclCredSetNotAfter(cred, dn_cred->notafter); + GRSTgaclCredSetDelegation(cred, dn_cred->delegation); + GRSTgaclCredSetNistLoa(cred, dn_cred->nist_loa); + + GRSTgaclUserAddCred(user, cred); + break; + } + + linestart += i; + + while ((linestart < statbuf.st_size) && + (mapped[linestart] != '\n') && + (mapped[linestart] != '\r')) ++linestart; + + while ((linestart < statbuf.st_size) && + ((mapped[linestart] == '\n') || + (mapped[linestart] == '\r'))) ++linestart; + } + + munmap(mapped, statbuf.st_size); + } + + if (fd < 0) close(fd); + } + + closedir(dirDIR); +} + +int GRSTgaclUserLoadDNlists(GRSTgaclUser *user, char *dnlists) +/* + Examine DN Lists for attributes belonging to this user and + add them to *user as additional credentials. +*/ +{ + char *dn_lists_dirs, *dn_list_ptr, *dirname; + GRSTgaclCred *dn_cred; + + /* check for DN Lists */ + + if (dnlists == NULL) dnlists = getenv("GRST_DN_LISTS"); + + if (dnlists == NULL) dnlists = GRST_DN_LISTS; + + if (*dnlists == '\0') return 1; /* Didn't ask for anything: that's ok */ + + /* find this user's (first) DN */ + + if (user == NULL) return 1; /* No user! */ + + for (dn_cred = user->firstcred; dn_cred != NULL; dn_cred = dn_cred->next) + { + if (strncmp((const char*)dn_cred->auri, "dn:", 3) == 0) break; + } + + if (dn_cred == NULL) return 1; /* User has no DN! */ + + /* look through DN List files */ + + dn_lists_dirs = strdup(dnlists); /* we need to keep this for free() later! */ + dn_list_ptr = dn_lists_dirs; /* copy, for naughty function strsep() */ + + while ((dirname = strsep(&dn_list_ptr, ":")) != NULL) + { + recurse4dnlists(user, dirname, 0, dn_cred); + } + + free(dn_lists_dirs); + return 1; +} + +/* * + * Functions to test for access perm of an individual * + * */ + +#if 0 +static char *recurse4file(char *dir, char *file, int recurse_level) +/* try to find file[] in dir[]. try subdirs if not found. + return full path to first found version or NULL on failure */ +{ + char fullfilename[16384], fulldirname[16384]; + struct stat statbuf; + DIR *dirDIR; + struct dirent *file_ent; + + /* try to find in current directory */ + + sprintf(fullfilename, "%s/%s", dir, file); + if (stat(fullfilename, &statbuf) == 0) return fullfilename; + + /* maybe search in subdirectories */ + + if (recurse_level >= GRST_RECURS_LIMIT) return NULL; + + dirDIR = opendir(dir); + + if (dirDIR == NULL) return NULL; + + while ((file_ent = readdir(dirDIR)) != NULL) + { + if (file_ent->d_name[0] == '.') continue; + + sprintf(fulldirname, "%s/%s", dir, file_ent->d_name); + + if ((stat(fulldirname, &statbuf) == 0) && + S_ISDIR(statbuf.st_mode) && + ((fullfilename = recurse4file(fulldirname, file, + recurse_level + 1)) != NULL)) + { + closedir(dirDIR); + return fullfilename; + } + + } + + closedir(dirDIR); + + return NULL; +} +#endif + +int GRSTgaclDNlistHasUser(char *listurl, GRSTgaclUser *user) +{ + return GRSTgaclUserHasAURI(user, listurl); +} + +int GRSTgaclUserHasAURI(GRSTgaclUser *user, char *auri) +{ + GRSTgaclCred *cred; + + if ((auri == NULL) || (user == NULL)) return 0; + + for (cred = user->firstcred; cred != NULL; cred = cred->next) + { + if (strcmp(auri, cred->auri) == 0) return 1; + } + + return 0; +} + +GRSTgaclPerm GRSTgaclAclTestUser(GRSTgaclAcl *acl, GRSTgaclUser *user) +/* + GACLgaclAclTestUser - return bit fields depending on access perms user has + for given acl. All zero for no access. If *user is + NULL, matching to "any-user" will still work. +*/ +{ + int flag, onlyanyuser; + GRSTgaclPerm allowperms = 0, denyperms = 0, allowed; + GRSTgaclEntry *entry; + GRSTgaclCred *cred; + + if (acl == NULL) return 0; + + for (entry = acl->firstentry; entry != NULL; entry = entry->next) + { + flag = 1; /* begin by assuming this entry applies to us */ + onlyanyuser = 1; /* begin by assuming just <any-user/> */ + + /* now go through creds, checking they all do apply to us */ + + for (cred = entry->firstcred; cred != NULL; cred = cred->next) + if (!GRSTgaclUserHasCred(user, cred)) flag = 0; + else if (strcmp(cred->auri, "gacl:any-user") != 0) onlyanyuser = 0; + + if (!flag) continue; /* flag false if a subtest failed */ + + /* does apply to us, so we remember this entry's perms */ + + /* we dont allow Write or Admin on the basis of any-user alone */ + + allowed = entry->allowed; + + if (onlyanyuser) + allowed = entry->allowed & ~GRST_PERM_WRITE & ~GRST_PERM_ADMIN; + else allowed = entry->allowed; + + allowperms = allowperms | allowed; + denyperms = denyperms | entry->denied; + } + + return (allowperms & (~ denyperms)); + /* for each perm type, any deny we saw kills any allow */ +} + +GRSTgaclPerm GRSTgaclAclTestexclUser(GRSTgaclAcl *acl, GRSTgaclUser *user) +/* + GRSTgaclAclTestexclUser - + return bit fields depending on ALLOW perms OTHER users + have for given acl. All zero if they have no access. + (used for testing if a user has exclusive access) +*/ +{ + int flag; + GRSTgaclPerm perm = 0; + GRSTgaclEntry *entry; + GRSTgaclCred *cred; + + if (acl == NULL) return 0; + + for (entry = acl->firstentry; entry != NULL; entry = entry->next) + { + flag = 0; /* flag will be set if cred implies other users */ + + for (cred = entry->firstcred; cred != NULL; cred = cred->next) + { + if (strncmp((const char*)cred->auri, "dn:", 3) != 0) + /* if we ever add support for other person-specific credentials, + they must also be recognised here */ + { + flag = 1; + break; + } + + if (!GRSTgaclUserHasCred(user, cred)) + /* if user doesnt have this person credential, assume + it refers to a different individual */ + { + flag = 1; + break; + } + } + + if (flag) perm = perm | entry->allowed; + } + + return perm; +} + +/* + Wrapper functions for gridsite-gacl.h support of legacy API +*/ + +GRSTgaclEntry *GACLparseEntry(xmlNodePtr cur) +{ + return GRSTgaclEntryParse(cur); +} diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_http.c b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_http.c new file mode 100644 index 0000000000000000000000000000000000000000..34a9577003a506e7b1d59c9492b21f37e016980a --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_http.c @@ -0,0 +1,477 @@ +/* + Copyright (c) 2002-3, Andrew McNab, University of Manchester + All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, are permitted provided that the following + conditions are met: + + o Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + o Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VERSION +#define VERSION "x.x.x" +#endif + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <stdio.h> + +#include <time.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <dirent.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <ctype.h> + +#include "gridsite.h" + +void GRSThttpBodyInit(GRSThttpBody *thisbody) +{ + thisbody->size = 0; /* simple, but we don't expose internals to callers */ +} + +void GRSThttpPrintf(GRSThttpBody *thisbody, char *fmt, ...) +/* append printf() style format and arguments to *thisbody. */ + +{ + char p[16384]; + size_t size; + va_list args; + + va_start(args, fmt); + size = vsprintf(p, fmt, args); + va_end(args); + + if (size > 0) + { + if (thisbody->size == 0) /* need to initialise */ + { + thisbody->first = (GRSThttpCharsList *)malloc(sizeof(GRSThttpCharsList)); + thisbody->first->text = p; + thisbody->first->next = NULL; + + thisbody->last = thisbody->first; + thisbody->size = size; + } + else + { + thisbody->last->next = (GRSThttpCharsList *) + malloc(sizeof(GRSThttpCharsList)); + ((GRSThttpCharsList *) thisbody->last->next)->text = p; + ((GRSThttpCharsList *) thisbody->last->next)->next = NULL; + + thisbody->last = thisbody->last->next; + thisbody->size = thisbody->size + size; + } + } +} + +int GRSThttpCopy(GRSThttpBody *thisbody, char *file) +/* + copy a whole file, named file[], into the body output buffer, returning + 1 if file was found and copied ok, or 0 otherwise. +*/ +{ + int fd, len; + char *p; + struct stat statbuf; + + fd = open(file, O_RDONLY); + + if (fd == -1) return 0; + + if (fstat(fd, &statbuf) != 0) + { + close(fd); + return 0; + } + + p = malloc(statbuf.st_size + 1); + + if (p == NULL) + { + close(fd); + return 0; + } + + len = read(fd, p, statbuf.st_size); + p[len] = '\0'; + + close(fd); + + if (thisbody->size == 0) /* need to initialise */ + { + thisbody->first = (GRSThttpCharsList *) malloc(sizeof(GRSThttpCharsList)); + thisbody->first->text = p; + thisbody->first->next = NULL; + + thisbody->last = thisbody->first; + thisbody->size = len; + } + else + { + thisbody->last->next=(GRSThttpCharsList *)malloc(sizeof(GRSThttpCharsList)); + ((GRSThttpCharsList *) thisbody->last->next)->text = p; + ((GRSThttpCharsList *) thisbody->last->next)->next = NULL; + + thisbody->last = thisbody->last->next; + thisbody->size = thisbody->size + len; + } + + return 1; +} + +void GRSThttpWriteOut(GRSThttpBody *thisbody) +/* output Content-Length header, blank line then whole of the body to + standard output */ +{ + GRSThttpCharsList *p; + + printf("Content-Length: %d\n\n", (int)thisbody->size); + + p = thisbody->first; + + while (p != NULL) + { + fputs(p->text, stdout); + + p = p->next; + } +} + +int GRSThttpPrintHeaderFooter(GRSThttpBody *bp, char *file, char *headfootname) +/* + try to print Header or Footer appropriate for absolute path file[], + returning 1 rather than 0 if found. +*/ +{ + int found = 0; + char *pathfile, *p; + struct stat statbuf; + + pathfile = malloc(strlen(file) + strlen(headfootname) + 2); + strcpy(pathfile, file); + + if ((pathfile[strlen(pathfile) - 1] != '/') && + (stat(pathfile, &statbuf) == 0) && + S_ISDIR(statbuf.st_mode)) strcat(pathfile, "/"); + + for (;;) + { + p = rindex(pathfile, '/'); + if (p == NULL) break; + p[1] = '\0'; + strcat(p, headfootname); + + if (stat(pathfile, &statbuf) == 0) + { + found = GRSThttpCopy(bp, pathfile); + break; + } + + p[0] = '\0'; + } + + free(pathfile); + return found; +} + +int GRSThttpPrintHeader(GRSThttpBody *bp, char *file) +{ + char *headname; + + headname = getenv("REDIRECT_GRST_HEAD_FILE"); + if (headname == NULL) headname = getenv("GRST_HEAD_FILE"); + if (headname == NULL) headname = GRST_HEADFILE; + + if (headname[0] == '/') /* absolute location */ + { + return GRSThttpCopy(bp, headname); + } + + return GRSThttpPrintHeaderFooter(bp, file, headname); +} + +int GRSThttpPrintFooter(GRSThttpBody *bp, char *file) +{ + char *footname; + + footname = getenv("REDIRECT_GRST_FOOT_FILE"); + if (footname == NULL) footname = getenv("GRST_FOOT_FILE"); + if (footname == NULL) footname = GRST_FOOTFILE; + + if (footname[0] == '/') /* absolute location */ + { + return GRSThttpCopy(bp, footname); + } + + return GRSThttpPrintHeaderFooter(bp, file, footname); +} + +char *GRSThttpGetCGI(char *name) +/* + Return a malloc()ed copy of CGI form parameter identified by name[], + either received by QUERY_STRING (via GET) or on stdin (via POST). + Caller must free() the returned string itself. If name[] is not found, + an empty NUL-terminated malloc()ed string is returned. name[] has any + URL-encoding reversed. +*/ +{ + char *p, *namepattern, *valuestart, *returnvalue, *querystring; + int c, i, j, n, contentlength = 0; + static char *cgiposted = NULL; + + if (cgiposted == NULL) /* have to initialise cgiposted */ + { + p = getenv("CONTENT_LENGTH"); + if (p != NULL) sscanf(p, "%d", &contentlength); + + querystring = getenv("REDIRECT_QUERY_STRING"); + if (querystring == NULL) querystring = getenv("QUERY_STRING"); + + if (querystring == NULL) cgiposted = malloc(contentlength + 3); + else cgiposted = malloc(contentlength + strlen(querystring) + 4); + + cgiposted[0] = '&'; + + for (i = 1; i <= contentlength; ++i) + { + c = getchar(); + if (c == EOF) break; + cgiposted[i] = c; + } + + cgiposted[i] = '&'; + cgiposted[i+1] = '\0'; + + if (querystring != NULL) + { + strcat(cgiposted, querystring); + strcat(cgiposted, "&"); + } + } + + namepattern = malloc(strlen(name) + 3); + sprintf(namepattern, "&%s=", name); + + p = strstr(cgiposted, namepattern); + free(namepattern); + if (p == NULL) return strdup(""); + + valuestart = &p[strlen(name) + 2]; + + for (n=0; valuestart[n] != '&'; ++n) ; + + returnvalue = malloc(n + 1); + + j=0; + + for (i=0; i < n; ++i) + { + if ((i < n - 2) && (valuestart[i] == '%')) /* url encoded as %HH */ + { + returnvalue[j] = 0; + + if (isdigit(valuestart[i+1])) + returnvalue[j] += 16 * (valuestart[i+1] - '0'); + else if (isalpha(valuestart[i+1])) + returnvalue[j] += 16 * (10 + tolower(valuestart[i+1]) - 'a'); + + if (isdigit(valuestart[i+2])) + returnvalue[j] += valuestart[i+2] - '0'; + else if (isalpha(valuestart[i+2])) + returnvalue[j] += 10 + tolower(valuestart[i+2]) - 'a'; + + i = i + 2; + } + else if (valuestart[i] == '+') returnvalue[j] = ' '; + else returnvalue[j] = valuestart[i]; + + if (returnvalue[j] == '\r') continue; /* CR/LF -> LF */ + ++j; + } + + returnvalue[j] = '\0'; + + return returnvalue; +} + +/* * + * Utility functions * + * */ + +char *GRSThttpUrlDecode(char *in) +{ + int i, j, n; + char *out; + + n = strlen(in); + out = malloc(n + 1); + + j=0; + + for (i=0; i < n; ++i) + { + if ((i < n - 2) && (in[i] == '%')) /* url encoded as %HH */ + { + out[j] = 0; + + if (isdigit(in[i+1])) + out[j] += 16 * (in[i+1] - '0'); + else if (isalpha(in[i+1])) + out[j] += 16 * (10 + tolower(in[i+1]) - 'a'); + + if (isdigit(in[i+2])) + out[j] += in[i+2] - '0'; + else if (isalpha(in[i+2])) + out[j] += 10 + tolower(in[i+2]) - 'a'; + + i = i + 2; + } + else if (in[i] == '+') out[j] = ' '; + else out[j] = in[i]; + + ++j; + } + + out[j] = '\0'; + + return out; +} + +char *GRSThttpUrlEncode(char *in) +/* Return a pointer to a malloc'd string holding a URL-encoded (RFC 1738) + version of *in. Only A-Z a-z 0-9 . _ - are passed through unmodified. + (DN's processed by GRSThttpUrlEncode can be used as valid Unix filenames, + assuming they do not exceed restrictions on filename length.) */ +{ + char *out, *p, *q; + + out = malloc(3*strlen(in) + 1); + + p = in; + q = out; + + while (*p != '\0') + { + if (isalnum(*p) || (*p == '.') || (*p == '_') || (*p == '-')) + { + *q = *p; + ++q; + } + else + { + sprintf(q, "%%%2X", *p); + q = &q[3]; + } + + ++p; + } + + *q = '\0'; + return out; +} + +char *GRSThttpUrlMildencode(char *in) +/* Return a pointer to a malloc'd string holding a partially URL-encoded + version of *in. "Partially" means that A-Z a-z 0-9 . = - _ @ and / + are passed through unmodified. (DN's processed by GRSThttpUrlMildencode() + can be used as valid Unix paths+filenames if you are prepared to + create or simulate the resulting /X=xyz directories.) */ +{ + char *out, *p, *q; + + out = malloc(3*strlen(in) + 1); + + p = in; + q = out; + + while (*p != '\0') + { + if (isalnum(*p) || (*p == '.') || (*p == '=') || (*p == '-') + || (*p == '/') || (*p == '@') || (*p == '_')) + { + *q = *p; + ++q; + } + else if (*p == ' ') + { + *q = '+'; + ++q; + } + else + { + sprintf(q, "%%%2X", *p); + q = &q[3]; + } + + ++p; + } + + *q = '\0'; + return out; +} + +/// Return a one-time passcode string, for use with GridHTTP +/** + * Returns + * + * String is timestamp+SHA1_HASH(timestamp+":"+method+":"+URL) + * Timestamps and hashes are in lowercase hexadecimal. Timestamps are + * seconds since 00:00:00 on January 1, 1970 UTC. + */ + +/* +char *GRSThttpMakeOneTimePasscode(time_t timestamp, char *method, char *url) +{ + int len, i; + char stringtohash[16384], hashedstring[EVP_MAX_MD_SIZE], *returnstring; + const EVP_MD *m; + EVP_MD_CTX ctx; + + m = EVP_sha1(); + if (m == NULL) return NULL; + + sprintf(stringtohash, "%08x:%s:%s", timestamp, method, url); + + EVP_DigestInit(&ctx, m); + EVP_DigestUpdate(&ctx, stringtohash, strlen(stringtohash)); + EVP_DigestFinal(&ctx, hashedstring, &len); + + returnstring = malloc(9 + len * 2); + + sprintf(returnstring, "%08x", timestamp); + + for (i=0; + + return returnstring; +} +*/ diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_verifycallback.c b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_verifycallback.c new file mode 100644 index 0000000000000000000000000000000000000000..adc367a3f6d34bb7c2443c556881cae35a9386ad --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_verifycallback.c @@ -0,0 +1,631 @@ +/* This code is extracted from mod_gridsite removing references to apache methods */ + +/* + Copyright (c) 2003-7, Andrew McNab, Shiv Kaushal, Joseph Dada, + and Yibiao Li, University of Manchester. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, are permitted provided that the following + conditions are met: + + o Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + o Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + + This program includes code from dav_parse_range() from Apache mod_dav.c, + and associated code contributed by David O Callaghan + + Copyright 2000-2005 The Apache Software Foundation or its licensors, as + applicable. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include "grst_verifycallback.h" +#include "gridsite.h" +#include <openssl/x509v3.h> +#include <string.h> + +X509_STORE* grst_store=NULL; +int grst_verify=0; +int grst_depth=0; +char* grst_cadir = "/etc/grid-certificates/certificates"; +char* grst_vomsdir = "/etc/grid-certificates/vomsdir"; + +// APACHE mod_ssl functions +int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx); +int ssl_callback_SSLVerify_CRL(int ok, X509_STORE_CTX *ctx); + +// GRIDSITE functions +int GRST_X509_check_issued_wrapper(X509_STORE_CTX *ctx, X509 *x, X509 *issuer) +/* We change the default callback to use our wrapper and discard errors + due to GSI proxy chains (ie where users certs act as CAs) */ +{ + int ret; + ret = X509_check_issued(issuer, x); + if (ret == X509_V_OK) + return 1; + + /* Non self-signed certs without signing are ok if they passed + the other checks inside X509_check_issued. Is this enough? */ + if ((ret == X509_V_ERR_KEYUSAGE_NO_CERTSIGN) && + (X509_NAME_cmp(X509_get_subject_name(issuer), + X509_get_subject_name(x)) != 0)) return 1; + + /* If we haven't asked for issuer errors don't set ctx */ +#if OPENSSL_VERSION_NUMBER < 0x00908000 + if (!(ctx->flags & X509_V_FLAG_CB_ISSUER_CHECK)) return 0; +#else + if (!(ctx->param->flags & X509_V_FLAG_CB_ISSUER_CHECK)) return 0; +#endif + + ctx->error = ret; + ctx->current_cert = x; + ctx->current_issuer = issuer; + return ctx->verify_cb(0, ctx); +} + +/* Later OpenSSL versions add a second pointer ... */ +int GRST_verify_cert_wrapper(X509_STORE_CTX *ctx, void *p) + +/* Earlier ones have a single argument ... */ +// int GRST_verify_cert_wrapper(X509_STORE_CTX *ctx) + +/* Before 0.9.7 we cannot change the check_issued callback directly in + the X509_STORE, so we must insert it in another callback that gets + called early enough */ +{ + ctx->check_issued = GRST_X509_check_issued_wrapper; + return X509_verify_cert(ctx); +} + +int GRST_callback_SSLVerify_wrapper(int ok, X509_STORE_CTX *ctx) +{ + SSL *ssl = (SSL *) X509_STORE_CTX_get_app_data(ctx); + int errnum = X509_STORE_CTX_get_error(ctx); + int errdepth = X509_STORE_CTX_get_error_depth(ctx); + int returned_ok; + STACK_OF(X509) *certstack; + GRSTx509Chain *grst_chain; + GRSTx509Chain *grst_old_chain; + + /* + * GSI Proxy user-cert-as-CA handling: + * we skip Invalid CA errors at this stage, since we will check this + * again at errdepth=0 for the full chain using GRSTx509ChainLoadCheck + */ + if (errnum == X509_V_ERR_INVALID_CA) + { + GRSTerrorLog(GRST_LOG_DEBUG,"Skip invalid CA error, since we will check again at errdepth=0"); + ok = TRUE; + errnum = X509_V_OK; + X509_STORE_CTX_set_error(ctx, errnum); + } + + /* + * New style GSI Proxy handling, with critical ProxyCertInfo + * extension: we use GRSTx509KnownCriticalExts() to check this + */ +#ifndef X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION +#define X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION 34 +#endif + if (errnum == X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION) + { + if (GRSTx509KnownCriticalExts(X509_STORE_CTX_get_current_cert(ctx)) + == GRST_RET_OK) + { + GRSTerrorLog(GRST_LOG_DEBUG,"GRSTx509KnownCriticalExts() accepts previously unhandled Critical Extension(GSI Proxy?)"); + ok = TRUE; + errnum = X509_V_OK; + X509_STORE_CTX_set_error(ctx, errnum); + } + } + + if (errnum == X509_V_ERR_INVALID_PURPOSE ) { + GRSTerrorLog(GRST_LOG_DEBUG,"GRSTx509 invalid purpose error ignored "); + ok = TRUE; + errnum = X509_V_OK; + X509_STORE_CTX_set_error(ctx, errnum); + } + + returned_ok = ssl_callback_SSLVerify(ok, ctx); + + /* in case ssl_callback_SSLVerify changed it */ + errnum = X509_STORE_CTX_get_error(ctx); + + if ((errdepth == 0) && (errnum == X509_V_OK)) + /* + * We've now got the last certificate - the identity being used for + * this connection. At this point we check the whole chain for valid + * CAs or, failing that, GSI-proxy validity using GRSTx509CheckChain. + */ + { + certstack = (STACK_OF(X509) *) X509_STORE_CTX_get_chain(ctx); + + errnum = GRSTx509ChainLoadCheck(&grst_chain, certstack, NULL, + grst_cadir, + grst_vomsdir); + + if (errnum != X509_V_OK) + { + GRSTerrorLog(GRST_LOG_ERR,"Invalid certificate chain reported by GRSTx509CheckChain() %s\n", X509_verify_cert_error_string(errnum)); + ok = FALSE; + } + else + { + GRSTerrorLog(GRST_LOG_DEBUG,"Valid certificate chain reported by GRSTx509ChainLoadCheck()\n"); + } + + // we don't free it but rather put it into the SSL context application data + //GRSTx509ChainFree(grst_chain); + + //GRSTx509ChainFree(grst_chain); + + if ((grst_old_chain = SSL_get_app_data(ssl))) { + GRSTx509ChainFree(grst_old_chain); + } + + SSL_set_app_data(ssl,grst_chain); + } + + return returned_ok; +} + +/* + Save result validity info from chain into connection notes, + and write out in an SSL session creds file. +*/ + +void GRST_print_ssl_creds(void *in_chain) +{ + int i= 0; + int lowest_voms_delegation = 65535; + GRSTx509Chain *grst_chain = (GRSTx509Chain*) in_chain; + GRSTx509Cert *grst_cert = NULL; + + /* check if already done */ + + for (grst_cert = grst_chain->firstcert; + grst_cert != NULL; grst_cert = grst_cert->next) + { + if (grst_cert->type == GRST_CERT_TYPE_VOMS) + { + /* want to record the delegation level + of the last proxy with VOMS attributes */ + GRSTerrorLog(GRST_LOG_DEBUG,"Recording VOMS delegation %d\n",grst_cert->delegation); + lowest_voms_delegation = grst_cert->delegation; + } + else if ((grst_cert->type == GRST_CERT_TYPE_EEC) || + (grst_cert->type == GRST_CERT_TYPE_PROXY)) + { + GRSTerrorLog(GRST_LOG_INFO,"(%d) dn: %s\n",i,grst_cert->dn); + GRSTerrorLog(GRST_LOG_INFO,"notbefore=%ld notafter=%ld delegation=%d nist-loa=%d\n",grst_cert->notbefore,grst_cert->notafter,grst_cert->delegation); + ++i; + } + } + + for (grst_cert = grst_chain->firstcert; + grst_cert != NULL; grst_cert = grst_cert->next) + { + if ((grst_cert->type == GRST_CERT_TYPE_VOMS) && + (grst_cert->delegation == lowest_voms_delegation)) + { + /* only export attributes from the last proxy to contain them */ + GRSTerrorLog(GRST_LOG_INFO,"fqan:%s\n",grst_cert->value); + GRSTerrorLog(GRST_LOG_INFO,"notbefore=%ld notafter=%ld delegation=%d nist-loa=%d\n",grst_cert->notbefore,grst_cert->notafter,grst_cert->delegation); + ++i; + } + } +} + + +char* GRST_get_voms_roles_and_free(void* in_chain) +{ + int i, lowest_voms_delegation = 65535; + GRSTx509Chain *grst_chain = (GRSTx509Chain*) in_chain; + GRSTx509Cert *grst_cert = NULL; + + char* voms_roles = (char*) malloc(16384); + voms_roles[0] =0; + + if (!voms_roles) { + return NULL; + } + + /* check if already done */ + + for (grst_cert = grst_chain->firstcert; + grst_cert != NULL; grst_cert = grst_cert->next) + { + if (grst_cert->type == GRST_CERT_TYPE_VOMS) + { + /* want to record the delegation level + of the last proxy with VOMS attributes */ + lowest_voms_delegation = grst_cert->delegation; + } + else if ((grst_cert->type == GRST_CERT_TYPE_EEC) || + (grst_cert->type == GRST_CERT_TYPE_PROXY)) + { + ++i; + } + } + + for (grst_cert = grst_chain->firstcert; + grst_cert != NULL; grst_cert = grst_cert->next) + { + if ((grst_cert->type == GRST_CERT_TYPE_VOMS) && + (grst_cert->delegation == lowest_voms_delegation)) + { + /* only export attributes from the last proxy to contain them */ + GRSTerrorLog(GRST_LOG_DEBUG,"fqan:%s\n",grst_cert->value); + + // filter the faulty roles out + // if (((strstr(grst_cert->value,"Role=NULL")))) { + // //|| (strstr(grst_cert->value,"Capability=NULL"))))) { + // filter out - don't concatenate + // } else { + + // don't filter, but leave it to the application to interpret the FQANs + strcat(voms_roles,grst_cert->value); + strcat(voms_roles,":"); + // } + GRSTerrorLog(GRST_LOG_DEBUG,"notbefore=%ld notafter=%ld delegation=%d nist-loa=%d\n",grst_cert->notbefore,grst_cert->notafter,grst_cert->delegation); + ++i; + } + } + + if (strlen(voms_roles)) { + // remove last : + voms_roles[strlen(voms_roles)-1] = 0; + } + + + GRSTx509ChainFree(grst_chain); + return voms_roles; +} + + + +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_engine_kernel.c +** The SSL engine kernel +*/ + +/* + * This OpenSSL callback function is called when OpenSSL + * does client authentication and verifies the certificate chain. + */ +int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx) +{ + X509 *xs; + int errnum; + int errdepth; + char *cp; + char *cp2; + int depth; + + /* + * Get context back through OpenSSL context + */ + SSL *ssl = (SSL *) X509_STORE_CTX_get_app_data(ctx); + + /* + * Get verify ingredients + */ + xs = X509_STORE_CTX_get_current_cert(ctx); + errnum = X509_STORE_CTX_get_error(ctx); + errdepth = X509_STORE_CTX_get_error_depth(ctx); + + /* + * Log verification information + */ + cp = X509_NAME_oneline(X509_get_subject_name(xs), NULL, 0); + cp2 = X509_NAME_oneline(X509_get_issuer_name(xs), NULL, 0); + + GRSTerrorLog(GRST_LOG_DEBUG, + "Certificate Verification: depth: %d, subject: %s, issuer: %s\n", + errdepth, cp != NULL ? cp : "-unknown-", + cp2 != NULL ? cp2 : "-unknown"); + + + if (cp) + OPENSSL_free(cp); + if (cp2) + OPENSSL_free(cp2); + + /* + * Check for optionally acceptable non-verifiable issuer situation + */ + + if ( ( errnum == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT + || errnum == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN + || errnum == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY +#if SSL_LIBRARY_VERSION >= 0x00905000 + || errnum == X509_V_ERR_CERT_UNTRUSTED +#endif + || errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE ) + && grst_verify == GRST_VERIFY_OPTIONAL_NO_CA ) { + GRSTerrorLog(GRST_LOG_ERR, + "Certificate Verification: Verifiable Issuer is configured as " + "optional, therefore we're accepting the certificate\n"); + SSL_set_verify_result(ssl, X509_V_OK); + ok = TRUE; + } + + /* + * Additionally perform CRL-based revocation checks + */ + + if (ok) { + ok = ssl_callback_SSLVerify_CRL(ok, ctx); + if (!ok) + errnum = X509_STORE_CTX_get_error(ctx); + } + + /* + * If we already know it's not ok, log the real reason + */ + if (!ok) { + GRSTerrorLog(GRST_LOG_ERR,"Certificate Verification: Error (%d): %s\n", + errnum, X509_verify_cert_error_string(errnum)); + } + + /* + * Finally check the depth of the certificate verification + */ + + depth = grst_depth; + + if (errdepth > depth) { + GRSTerrorLog(GRST_LOG_ERR, + "Certificate Verification: Certificate Chain too long " + "(chain has %d certificates, but maximum allowed are only %d)\n", + errdepth, depth); + ok = FALSE; + } + + /* + * And finally signal OpenSSL the (perhaps changed) state + */ + return (ok); +} + +int ssl_callback_SSLVerify_CRL( + int ok, X509_STORE_CTX *ctx) +{ + X509_OBJECT obj; + X509_NAME *subject; + X509_NAME *issuer; + X509 *xs; + X509_CRL *crl; + X509_REVOKED *revoked; + EVP_PKEY *pubkey; + long serial; + int i, n, rc; + char *cp; + ASN1_TIME *t; + + /* + * Determine certificate ingredients in advance + */ + + GRSTerrorLog(GRST_LOG_DEBUG,"Checking certificate revocation lists\n"); + + + xs = X509_STORE_CTX_get_current_cert(ctx); + subject = X509_get_subject_name(xs); + issuer = X509_get_issuer_name(xs); + + + /* + * OpenSSL provides the general mechanism to deal with CRLs but does not + * use them automatically when verifying certificates, so we do it + * explicitly here. We will check the CRL for the currently checked + * certificate, if there is such a CRL in the store. + * + * We come through this procedure for each certificate in the certificate + * chain, starting with the root-CA's certificate. At each step we've to + * both verify the signature on the CRL (to make sure it's a valid CRL) + * and it's revocation list (to make sure the current certificate isn't + * revoked). But because to check the signature on the CRL we need the + * public key of the issuing CA certificate (which was already processed + * one round before), we've a little problem. But we can both solve it and + * at the same time optimize the processing by using the following + * verification scheme (idea and code snippets borrowed from the GLOBUS + * project): + * + * 1. We'll check the signature of a CRL in each step when we find a CRL + * through the _subject_ name of the current certificate. This CRL + * itself will be needed the first time in the next round, of course. + * But we do the signature processing one round before this where the + * public key of the issuing CA certificate (which was already processed + * one round before), we've a little problem. But we can both solve it and + * at the same time optimize the processing by using the following + * verification scheme (idea and code snippets borrowed from the GLOBUS + * project): + * + * 1. We'll check the signature of a CRL in each step when we find a CRL + * through the _subject_ name of the current certificate. This CRL + * itself will be needed the first time in the next round, of course. + * But we do the signature processing one round before this where the + * public key of the CA is available. + * + * 2. We'll check the revocation list of a CRL in each step when + * we find a CRL through the _issuer_ name of the current certificate. + * This CRLs signature was then already verified one round before. + * + * This verification scheme allows a CA to revoke its own certificate as + * well, of course. + + */ + + if (!grst_store) { + return 1; + } + + + /* + * Try to retrieve a CRL corresponding to the _subject_ of + * the current certificate in order to verify it's integrity. + */ + memset((char *)&obj, 0, sizeof(obj)); + + rc = SSL_X509_STORE_lookup(grst_store,X509_LU_CRL, subject, &obj); + + crl = obj.data.crl; + if (rc > 0 && crl != NULL) { + GRSTerrorLog(GRST_LOG_DEBUG,"CRL lookup ..."); + /* + * Verify the signature on this CRL + */ + pubkey = X509_get_pubkey(xs); + if (X509_CRL_verify(crl, pubkey) <= 0) { + GRSTerrorLog(GRST_LOG_ERR,"Invalid signature on CRL\n"); + X509_STORE_CTX_set_error(ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE); + X509_OBJECT_free_contents(&obj); + if (pubkey != NULL) + EVP_PKEY_free(pubkey); + return 0; + } + + if (pubkey != NULL) + EVP_PKEY_free(pubkey); + + /* + * Check date of CRL to make sure it's not expired + */ + if ((t = X509_CRL_get_nextUpdate(crl)) == NULL) { + GRSTerrorLog(GRST_LOG_ERR,"Found CRL has invalid enxtUpdate field\n"); + X509_STORE_CTX_set_error(ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD); + X509_OBJECT_free_contents(&obj); + return 0; + } + if (X509_cmp_current_time(t) < 0) { + GRSTerrorLog(GRST_LOG_ERR,"Found CRL is expired - " + "revoking all certificates until you get updated CRL\n"); + X509_STORE_CTX_set_error(ctx, X509_V_ERR_CRL_HAS_EXPIRED); + X509_OBJECT_free_contents(&obj); + return 0; + } + X509_OBJECT_free_contents(&obj); + } + + /* + * Try to retrieve a CRL corresponding to the _issuer_ of + * the current certificate in order to check for revocation. + */ + + memset((char *)&obj, 0, sizeof(obj)); + rc = SSL_X509_STORE_lookup(grst_store,X509_LU_CRL, issuer, &obj); + crl = obj.data.crl; + if (rc > 0 && crl != NULL) { + /* + * Check if the current certificate is revoked by this CRL + */ +#if SSL_LIBRARY_VERSION < 0x00904000 + n = sk_num(X509_CRL_get_REVOKED(crl)); +#else + n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl)); +#endif + for (i = 0; i < n; i++) { +#if SSL_LIBRARY_VERSION < 0x00904000 + revoked = (X509_REVOKED *)sk_value(X509_CRL_get_REVOKED(crl), i); +#else + revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i); +#endif + if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(xs)) == 0) { + serial = ASN1_INTEGER_get(revoked->serialNumber); + cp = X509_NAME_oneline(issuer, NULL, 0); + GRSTerrorLog(GRST_LOG_ERR, + "Certificate with serial %ld (0x%lX) " + "revoked per CRL from issuer %s\n", + serial, serial, cp); + OPENSSL_free(cp); + + X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REVOKED); + X509_OBJECT_free_contents(&obj); + return 0; + } + } + X509_OBJECT_free_contents(&obj); + } + return 1; +} + +/* _________________________________________________________________ +** +** Certificate Revocation List (CRL) Storage +** _________________________________________________________________ +*/ + +X509_STORE *SSL_X509_STORE_create(char *cpFile, char *cpPath) +{ + X509_STORE *pStore; + X509_LOOKUP *pLookup; + + if (cpFile == NULL && cpPath == NULL) + return NULL; + if ((pStore = X509_STORE_new()) == NULL) + return NULL; + if (cpFile != NULL) { + if ((pLookup = X509_STORE_add_lookup(pStore, X509_LOOKUP_file())) == NULL) { + X509_STORE_free(pStore); + return NULL; + } + X509_LOOKUP_load_file(pLookup, cpFile, X509_FILETYPE_PEM); + } + if (cpPath != NULL) { + if ((pLookup = X509_STORE_add_lookup(pStore, X509_LOOKUP_hash_dir())) == NULL) { + X509_STORE_free(pStore); + return NULL; + } + X509_LOOKUP_add_dir(pLookup, cpPath, X509_FILETYPE_PEM); + } + return pStore; +} + +int SSL_X509_STORE_lookup(X509_STORE *pStore, int nType, + X509_NAME *pName, X509_OBJECT *pObj) +{ + X509_STORE_CTX pStoreCtx; + int rc; + + X509_STORE_CTX_init(&pStoreCtx, pStore, NULL, NULL); + rc = X509_STORE_get_by_subject(&pStoreCtx, nType, pName, pObj); + X509_STORE_CTX_cleanup(&pStoreCtx); + return rc; +} diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_verifycallback.h b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_verifycallback.h new file mode 100644 index 0000000000000000000000000000000000000000..2be12e4ec90757b358df2fa02cba539da98700f6 --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_verifycallback.h @@ -0,0 +1,35 @@ +#ifndef GRST_VERIFYCALLBACK__H +#define GRST_VERIFYCALLBACK__H + +#include <openssl/ssl.h> + +extern X509_STORE* grst_store; + +#define GRST_VERIFY_OPTIONAL_NO_CA 1 + +extern int grst_verify; // relax the verify setting if you want to GRST_VERIFY_OPTIONAL_NO_CA +extern int grst_depth; + +extern char* grst_vomsdir; +extern char* grst_cadir; + +#ifdef __cplusplus +extern "C" +{ +#endif + int GRST_X509_set_log_fd(FILE* fd); + int GRST_verify_cert_wrapper(X509_STORE_CTX *ctx, void *p); + int GRST_X509_check_issued_wrapper(X509_STORE_CTX *ctx, X509 *x, X509 *issuer); + int GRST_callback_SSLVerify_wrapper(int ok, X509_STORE_CTX *ctx); + void GRST_print_ssl_creds(void *grst_chain); + char* GRST_get_voms_roles_and_free(void *grst_chain); + + X509_STORE *SSL_X509_STORE_create(char *cpFile, char *cpPath); + int SSL_X509_STORE_lookup(X509_STORE *pStore, int nType, + X509_NAME *pName, X509_OBJECT *pObj); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_x509.c b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_x509.c new file mode 100644 index 0000000000000000000000000000000000000000..c7d72cb120d025a866e63815b560bfeaf5658315 --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_x509.c @@ -0,0 +1,2149 @@ +/* + Copyright (c) 2002-7, Andrew McNab, University of Manchester + All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, are permitted provided that the following + conditions are met: + + o Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + o Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + --------------------------------------------------------------- + For more information about GridSite: http://www.gridsite.org/ + --------------------------------------------------------------- +*/ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdarg.h> +#include <time.h> +#include <stdarg.h> +#include <dirent.h> +#include <string.h> +#include <pwd.h> +#include <errno.h> +#include <getopt.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#ifndef GRST_NO_OPENSSL +#include <openssl/x509.h> +#include <openssl/x509v3.h> +#include <openssl/err.h> +#include <openssl/pem.h> + +#include <openssl/rsa.h> +#include <openssl/pem.h> +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/bio.h> +#include <openssl/des.h> +#include <openssl/rand.h> +#endif + +#include "gridsite.h" + +#define GRST_KEYSIZE 512 +#define GRST_PROXYCACHE "/../proxycache/" +#define GRST_MAX_CHAIN_LEN 9 +#define GRST_BACKDATE_SECONDS 300 + +/// Compare X509 Distinguished Name strings +int GRSTx509NameCmp(char *a, char *b) +/// +/// This function attempts to do with string representations what +/// would ideally be done with OIDs/values. In particular, we equate +/// "/Email=" == "/emailAddress=" to deal with this important change +/// between OpenSSL 0.9.6 and 0.9.7. +/// Other than that, it is currently the same as ordinary strcasecmp(3) +/// (for consistency with EDG/LCG/EGEE gridmapdir case insensitivity.) +{ + int ret; + char *aa, *bb, *p; + + if ((a == NULL) || (b == NULL)) return 1; /* NULL never matches */ + + aa = strdup(a); + while ((p = strstr(aa, "/emailAddress=")) != NULL) + { + memmove(&p[6], &p[13], strlen(&p[13]) + 1); + p[1] = 'E'; + } + + bb = strdup(b); + while ((p = strstr(bb, "/emailAddress=")) != NULL) + { + memmove(&p[6], &p[13], strlen(&p[13]) + 1); + p[1] = 'E'; + } + + ret = strcasecmp(aa, bb); + + free(aa); + free(bb); + + return ret; +} + + +/// Check critical extensions +int GRSTx509KnownCriticalExts(X509 *cert) +/// +/// Returning GRST_RET_OK if all of extensions are known to us or +/// OpenSSL; GRST_REF_FAILED otherwise. +/// +/// Since this function relies on functionality (X509_supported_extension) +/// introduced in 0.9.7, then we do nothing and report an error +/// (GRST_RET_FAILED) if one of the associated defines +/// (X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION) is absent. +{ + int i; + char s[80]; + X509_EXTENSION *ex; + +#ifdef X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION + for (i = 0; i < X509_get_ext_count(cert); ++i) + { + ex = X509_get_ext(cert, i); + + if (X509_EXTENSION_get_critical(ex) && + !X509_supported_extension(ex)) + { + OBJ_obj2txt(s, sizeof(s), X509_EXTENSION_get_object(ex), 1); + + if (strcmp(s, GRST_PROXYCERTINFO_OID) != 0) return GRST_RET_FAILED; + } + } + + return GRST_RET_OK; +#else + return GRST_RET_FAILED; +#endif +} + +/// Check if certificate can be used as a CA to sign standard X509 certs +int GRSTx509IsCA(X509 *cert) +/// +/// Return GRST_RET_OK if true; GRST_RET_FAILED if not. +{ + int purpose_id; + + purpose_id = X509_PURPOSE_get_by_sname("sslclient"); + + /* final argument to X509_check_purpose() is whether to check for CAness */ + if (X509_check_purpose(cert, purpose_id + X509_PURPOSE_MIN, 1)) + return GRST_RET_OK; + else return GRST_RET_FAILED; +} + +int GRSTx509ChainFree(GRSTx509Chain *chain) +{ + GRSTx509Cert *grst_cert, *next_grst_cert; + + if (chain == NULL) return GRST_RET_OK; + + next_grst_cert = chain->firstcert; + + while (next_grst_cert != NULL) + { + grst_cert = next_grst_cert; + + if (grst_cert->issuer != NULL) free(grst_cert->issuer); + if (grst_cert->dn != NULL) free(grst_cert->dn); + if (grst_cert->ocsp != NULL) free(grst_cert->ocsp); + + next_grst_cert = grst_cert->next; + free(grst_cert); + } + + free(chain); + + return GRST_RET_OK; +} + +/// Check a specific signature against a specific (VOMS) cert +static int GRSTx509VerifySig(time_t *time1_time, time_t *time2_time, + unsigned char *txt, int txt_len, + unsigned char *sig, int sig_len, + X509 *cert) +/// +/// Returns GRST_RET_OK if signature is ok, other values if not. +{ + int ret; + EVP_PKEY *prvkey; + EVP_MD_CTX ctx; + time_t voms_service_time1, voms_service_time2; + + prvkey = X509_extract_key(cert); + if (prvkey == NULL) return GRST_RET_FAILED; + + OpenSSL_add_all_digests(); +#if OPENSSL_VERSION_NUMBER >= 0x0090701fL + EVP_MD_CTX_init(&ctx); + EVP_VerifyInit_ex(&ctx, EVP_md5(), NULL); +#else + EVP_VerifyInit(&ctx, EVP_md5()); +#endif + + EVP_VerifyUpdate(&ctx, txt, txt_len); + + ret = EVP_VerifyFinal(&ctx, sig, sig_len, prvkey); + +#if OPENSSL_VERSION_NUMBER >= 0x0090701fL + EVP_MD_CTX_cleanup(&ctx); +#endif + EVP_PKEY_free(prvkey); + + if (ret != 1) return GRST_RET_FAILED; + + voms_service_time1 = + GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(cert)),0); + if (voms_service_time1 > *time1_time) + *time1_time = voms_service_time1; + + voms_service_time2 = + GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(cert)),0); + if (voms_service_time2 < *time1_time) + *time2_time = voms_service_time2; + + return GRST_RET_OK ; /* verified */ +} + +/// Check the signature of the VOMS attributes +static int GRSTx509VerifyVomsSig(time_t *time1_time, time_t *time2_time, + unsigned char *asn1string, + struct GRSTasn1TagList taglist[], + int lasttag, + char *vomsdir, int acnumber) +/// +/// Returns GRST_RET_OK if signature is ok, other values if not. +{ +#define GRST_ASN1_COORDS_VOMS_DN "-1-1-%d-1-3-1-1-1-%%d-1-%%d" +#define GRST_ASN1_COORDS_VOMS_INFO "-1-1-%d-1" +#define GRST_ASN1_COORDS_VOMS_SIG "-1-1-%d-3" + int isig, iinfo; + char certpath[16384], certpath2[16384], acvomsdn[200], dn_coords[200], + info_coords[200], sig_coords[200]; + + DIR *vomsDIR, *vomsDIR2; + struct dirent *vomsdirent, *vomsdirent2; + X509 *cert; + + FILE *fp; + struct stat statbuf; + + if ((vomsdir == NULL) || (vomsdir[0] == '\0')) return GRST_RET_FAILED; + + snprintf(dn_coords, sizeof(dn_coords), + GRST_ASN1_COORDS_VOMS_DN, acnumber); + + if (GRSTasn1GetX509Name(acvomsdn, sizeof(acvomsdn), dn_coords, + (char*)asn1string, taglist, lasttag) != GRST_RET_OK) return GRST_RET_FAILED; + + snprintf(info_coords, sizeof(info_coords), + GRST_ASN1_COORDS_VOMS_INFO, acnumber); + iinfo = GRSTasn1SearchTaglist(taglist, lasttag, info_coords); + + snprintf(sig_coords, sizeof(sig_coords), + GRST_ASN1_COORDS_VOMS_SIG, acnumber); + isig = GRSTasn1SearchTaglist(taglist, lasttag, sig_coords); + + if ((iinfo < 0) || (isig < 0)) return GRST_RET_FAILED; + + vomsDIR = opendir(vomsdir); + if (vomsDIR == NULL) return GRST_RET_FAILED; + + while ((vomsdirent = readdir(vomsDIR)) != NULL) + { + if (vomsdirent->d_name[0] == '.') continue; + + sprintf(certpath, "%s/%s", vomsdir, vomsdirent->d_name); + stat(certpath, &statbuf); + + if (S_ISDIR(statbuf.st_mode)) + { + vomsDIR2 = opendir(certpath); + GRSTerrorLog(GRST_LOG_DEBUG, + "Descend VOMS subdirectory %s", certpath); + + if (vomsDIR2 == NULL) continue; + + + while ((vomsdirent2 = readdir(vomsDIR2)) != NULL) + { + if (vomsdirent2->d_name[0] == '.') continue; + + sprintf(certpath2, "%s/%s/%s", + vomsdir, vomsdirent->d_name, vomsdirent2->d_name); + + fp = fopen(certpath2, "r"); + GRSTerrorLog(GRST_LOG_DEBUG, + "Examine VOMS cert %s", certpath2); + if (fp == NULL) continue; + + cert = PEM_read_X509(fp, NULL, NULL, NULL); + fclose(fp); + if (cert == NULL) continue; + + if (GRSTx509VerifySig(time1_time, time2_time, + &asn1string[taglist[iinfo].start], + taglist[iinfo].length+taglist[iinfo].headerlength, + &asn1string[taglist[isig].start+ + taglist[isig].headerlength+1], + taglist[isig].length - 1, + cert) == GRST_RET_OK) + { + GRSTerrorLog(GRST_LOG_DEBUG, " VOMS cert signature match"); + X509_free(cert); + closedir(vomsDIR2); + closedir(vomsDIR); + return GRST_RET_OK ; /* verified */ + } + + X509_free(cert); + } + + closedir(vomsDIR2); + } + else + { + fp = fopen(certpath, "r"); + GRSTerrorLog(GRST_LOG_DEBUG, "Examine VOMS cert %s", certpath); + if (fp == NULL) continue; + + cert = PEM_read_X509(fp, NULL, NULL, NULL); + fclose(fp); + if (cert == NULL) continue; + + if (GRSTx509VerifySig(time1_time, time2_time, + &asn1string[taglist[iinfo].start], + taglist[iinfo].length+taglist[iinfo].headerlength, + &asn1string[taglist[isig].start+ + taglist[isig].headerlength+1], + taglist[isig].length - 1, + cert) == GRST_RET_OK) + { + X509_free(cert); + closedir(vomsDIR); + return GRST_RET_OK ; /* verified */ + } + + X509_free(cert); + } + } + + closedir(vomsDIR); + return GRST_RET_FAILED; +} + +/// Get the VOMS attributes in the given extension +static int GRSTx509ChainVomsAdd(GRSTx509Cert **grst_cert, + time_t time1_time, time_t time2_time, + X509_EXTENSION *ex, + char *ucuserdn, char *vomsdir) +/// +/// Add any VOMS credentials found into the chain. Always returns GRST_RET_OK +/// - even for invalid credentials, which are flagged in errors field +{ +#define MAXTAG 500 +#define GRST_ASN1_COORDS_FQAN "-1-1-%d-1-7-1-2-1-2-%d" +#define GRST_ASN1_COORDS_USER_DN "-1-1-%d-1-2-1-1-1-1-%%d-1-%%d" +#define GRST_ASN1_COORDS_VOMS_DN "-1-1-%d-1-3-1-1-1-%%d-1-%%d" +#define GRST_ASN1_COORDS_TIME1 "-1-1-%d-1-6-1" +#define GRST_ASN1_COORDS_TIME2 "-1-1-%d-1-6-2" + ASN1_OCTET_STRING *asn1data; + char *asn1string, acuserdn[200], acvomsdn[200], + dn_coords[200], fqan_coords[200], time1_coords[200], + time2_coords[200]; + long asn1length; + int lasttag=-1, itag, i, acnumber = 1, chain_errors = 0; + struct GRSTasn1TagList taglist[MAXTAG+1]; + time_t actime1 = 0, actime2 = 0, time_now; + + asn1data = X509_EXTENSION_get_data(ex); + asn1string = (char*)ASN1_STRING_data(asn1data); + asn1length = ASN1_STRING_length(asn1data); + + GRSTasn1ParseDump(NULL, (unsigned char*) asn1string, asn1length, taglist, MAXTAG, &lasttag); + + for (acnumber = 1; ; ++acnumber) /* go through ACs one by one */ + { + chain_errors = 0; + + snprintf(dn_coords, sizeof(dn_coords), GRST_ASN1_COORDS_USER_DN, acnumber); + if (GRSTasn1GetX509Name(acuserdn, sizeof(acuserdn), dn_coords, + asn1string, taglist, lasttag) != GRST_RET_OK) + break; + + snprintf(dn_coords, sizeof(dn_coords), GRST_ASN1_COORDS_VOMS_DN, acnumber); + if (GRSTasn1GetX509Name(acvomsdn, sizeof(acvomsdn), dn_coords, + asn1string, taglist, lasttag) != GRST_RET_OK) + break; + + if (GRSTx509NameCmp(ucuserdn, acuserdn) != 0) + chain_errors |= GRST_CERT_BAD_CHAIN; + + if (GRSTx509VerifyVomsSig(&time1_time, &time2_time, + (unsigned char*)asn1string, taglist, lasttag, vomsdir, acnumber) + != GRST_RET_OK) + chain_errors |= GRST_CERT_BAD_SIG; + + snprintf(time1_coords, sizeof(time1_coords), GRST_ASN1_COORDS_TIME1, acnumber); + itag = GRSTasn1SearchTaglist(taglist, lasttag, time1_coords); + + if (itag > -1) actime1 = GRSTasn1TimeToTimeT((unsigned char*) + &asn1string[taglist[itag].start+ + taglist[itag].headerlength], + taglist[itag].length); + else actime1 = 0; + + snprintf(time2_coords, sizeof(time2_coords), GRST_ASN1_COORDS_TIME2, acnumber); + itag = GRSTasn1SearchTaglist(taglist, lasttag, time2_coords); + + if (itag > -1) actime2 = GRSTasn1TimeToTimeT((unsigned char*) + &asn1string[taglist[itag].start+ + taglist[itag].headerlength], + taglist[itag].length); + else actime2 = 0; + + if (actime1 > time1_time) time1_time = actime1; + if (actime2 < time2_time) time2_time = actime2; + + time(&time_now); + if ((time1_time > time_now + 300) || (time2_time < time_now)) + chain_errors |= GRST_CERT_BAD_TIME; + + for (i=1; ; ++i) /* now go through FQANs */ + { + snprintf(fqan_coords, sizeof(fqan_coords), GRST_ASN1_COORDS_FQAN, acnumber, i); + itag = GRSTasn1SearchTaglist(taglist, lasttag, fqan_coords); + + if (itag > -1) + { + (*grst_cert)->next = malloc(sizeof(GRSTx509Cert)); + *grst_cert = (*grst_cert)->next; + bzero(*grst_cert, sizeof(GRSTx509Cert)); + + (*grst_cert)->notbefore = time1_time; + (*grst_cert)->notafter = time2_time; + sprintf(((*grst_cert)->value), "%.*s", + taglist[itag].length, + &asn1string[taglist[itag].start+ + taglist[itag].headerlength]); + + (*grst_cert)->errors = chain_errors; /* ie may be invalid */ + (*grst_cert)->type = GRST_CERT_TYPE_VOMS; + (*grst_cert)->issuer = strdup(acvomsdn); + (*grst_cert)->dn = strdup(acuserdn); + } + else break; + } + } + + return GRST_RET_OK; +} + +/// Check certificate chain for GSI proxy acceptability. +int GRSTx509ChainLoadCheck(GRSTx509Chain **chain, + STACK_OF(X509) *certstack, X509 *lastcert, + char *capath, char *vomsdir) +/// +/// Returns GRST_RET_OK if valid; OpenSSL X509 errors otherwise. +/// +/// The GridSite version handles old and new style Globus proxies, and +/// proxies derived from user certificates issued with "X509v3 Basic +/// Constraints: CA:FALSE" (eg UK e-Science CA) +/// +/// TODO: we do not yet check ProxyCertInfo and ProxyCertPolicy extensions +/// (although via GRSTx509KnownCriticalExts() we can accept them.) +{ + X509 *cert; /* Points to the current cert in the loop */ + X509 *cacert = NULL; /* The CA root cert */ + int depth = 0; /* Depth of cert chain */ + int chain_errors = 0; /* records previous errors */ + int first_non_ca; /* number of the EEC issued to user by CA */ + char *ucuserdn = NULL; /* DN of EEC issued to user by CA */ + size_t len,len2; /* Lengths of issuer and cert DN */ + int IsCA; /* Holds whether cert is allowed to sign */ + int prevIsCA; /* Holds whether previous cert in chain is + allowed to sign */ + int prevIsLimited; /* previous cert was proxy and limited */ + int i,j,ret; /* Iteration/temp variables */ + char *proxy_part_DN; /* Pointer to end part of current-cert-in-chain + maybe eg "/CN=proxy" */ + char s[80]; + char cacertpath[16384]; + unsigned long subjecthash = 0; /* hash of the name of first cert */ + unsigned long issuerhash = 0; /* hash of issuer name of first cert */ + FILE *fp; + X509_EXTENSION *ex; + time_t now; + GRSTx509Cert *grst_cert, *new_grst_cert; + grst_cert = NULL; + new_grst_cert = NULL; + GRSTerrorLog(GRST_LOG_DEBUG, "GRSTx509ChainLoadCheck() starts"); + + time(&now); + + first_non_ca = 0; /* set to something predictable if things fail */ + + /* Set necessary preliminary values */ + IsCA = TRUE; /* =prevIsCA - start from a CA */ + prevIsLimited = 0; + + /* Get the client cert chain */ + if (certstack != NULL) + depth = sk_X509_num(certstack); /* How deep is that chain? */ + + if ((depth == 0) && (lastcert == NULL)) + { + *chain = NULL; + return GRST_RET_FAILED; + } + + cert = sk_X509_value(certstack, depth - 1); + subjecthash = X509_NAME_hash(X509_get_subject_name(cert)); + issuerhash = X509_NAME_hash(X509_get_issuer_name(cert)); + sprintf(cacertpath, "%s/%.8x.0", capath, (int)issuerhash); + + GRSTerrorLog(GRST_LOG_DEBUG, "Look for CA root file %s", cacertpath); + + fp = fopen(cacertpath, "r"); + + if (fp == NULL) chain_errors |= GRST_CERT_BAD_CHAIN; + else + { + cacert = PEM_read_X509(fp, NULL, NULL, NULL); + fclose(fp); + if (cacert != NULL) { + GRSTerrorLog(GRST_LOG_DEBUG, "Loaded CA root cert from file"); + } else { + GRSTerrorLog(GRST_LOG_DEBUG, "Failed to load CA root cert file"); + } + } + + // *chain = (GRSTx509Chain*) malloc(sizeof(GRSTx509Chain)); + *chain = malloc(sizeof(GRSTx509Chain)); + bzero(*chain, sizeof(GRSTx509Chain)); + + /* Check the client chain */ + for (i = depth - ((subjecthash == issuerhash) ? 1 : 0); + i >= ((lastcert == NULL) ? 0 : -1); + --i) + /* loop through client-presented chain starting at CA end */ + { + GRSTerrorLog(GRST_LOG_DEBUG, "Process cert at depth %d in chain", i); + + prevIsCA=IsCA; + + new_grst_cert = malloc(sizeof(GRSTx509Cert)); + bzero(new_grst_cert, sizeof(GRSTx509Cert)); + new_grst_cert->errors = chain_errors; + + if ((*chain)->firstcert == NULL) + { + GRSTerrorLog(GRST_LOG_DEBUG, "Initialise chain"); + (*chain)->firstcert = new_grst_cert; + } + else grst_cert->next = new_grst_cert; + + grst_cert = new_grst_cert; + + /* Choose X509 certificate and point to it with 'cert' */ + if (i < 0) cert = lastcert; + else if (i == depth) + cert = cacert; /* the self-signed CA from the store*/ + else if ((i == depth - 1) && (subjecthash == issuerhash)) + cert = cacert; /* ie claims to be a copy of a self-signed CA */ + else cert = sk_X509_value(certstack, i); + + if (cert != NULL) + { + if ((i == depth - 1) && (subjecthash != issuerhash)) + { + /* if first cert does not claim to be a self-signed copy + of a CA root cert in the store, we check the signature */ + + if (cacert == NULL) + { + chain_errors |= GRST_CERT_BAD_CHAIN; + ret = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT; + } + else + { + ret = X509_check_issued(cacert, cert); + + GRSTerrorLog(GRST_LOG_DEBUG, + "Cert sig check %d returns %d", i, ret); + + if (ret != X509_V_OK) + new_grst_cert->errors |= GRST_CERT_BAD_SIG; + } + } + else if ((i == depth - 2) && (subjecthash == issuerhash)) + { + /* first cert claimed to be a self-signed copy of a CA root + cert in the store, we check the signature of the second + cert, using OUR copy of the CA cert DIRECT from the store */ + + if (cacert == NULL) + { + chain_errors |= GRST_CERT_BAD_CHAIN; + ret = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT; + } + else + { + ret = X509_check_issued(cacert, cert); + + GRSTerrorLog(GRST_LOG_DEBUG, + "Cert sig check %d returns %d", i, ret); + + if (ret != X509_V_OK) + new_grst_cert->errors |= GRST_CERT_BAD_SIG; + } + } + else if (i < depth - 1) + { + /* otherwise a normal part of the chain: note that if the + first cert claims to be a self-signed copy of a CA root + cert in the store, we never use it for sig checking */ + + ret = X509_check_issued(sk_X509_value(certstack, i + 1), cert); + + GRSTerrorLog(GRST_LOG_DEBUG, + "Cert sig check %d returns %d", i, ret); + + if ((ret != X509_V_OK) && + (ret != X509_V_ERR_KEYUSAGE_NO_CERTSIGN)) + new_grst_cert->errors |= GRST_CERT_BAD_SIG; + + /* NO_CERTSIGN can still be ok due to Proxy Certificates */ + } + + new_grst_cert->serial = (int) ASN1_INTEGER_get( + X509_get_serialNumber(cert)); + new_grst_cert->notbefore = GRSTasn1TimeToTimeT( + ASN1_STRING_data(X509_get_notBefore(cert)), 0); + new_grst_cert->notafter = GRSTasn1TimeToTimeT( + ASN1_STRING_data(X509_get_notAfter(cert)), 0); + + /* we check times and record if invalid */ + + if (now < new_grst_cert->notbefore) + new_grst_cert->errors |= GRST_CERT_BAD_TIME; + + if (now > new_grst_cert->notafter) + new_grst_cert->errors |= GRST_CERT_BAD_TIME; + + new_grst_cert->dn = X509_NAME_oneline(X509_get_subject_name(cert),NULL,0); + new_grst_cert->issuer = X509_NAME_oneline(X509_get_issuer_name(cert),NULL,0); + len = strlen(new_grst_cert->dn); + len2 = strlen(new_grst_cert->issuer); + + /* always treat a first cert from the CA files as a + CA: this is really for lousy CAs that dont create + proper v3 root certificates */ + + if (i == depth) + IsCA = TRUE; + else + IsCA = (GRSTx509IsCA(cert) == GRST_RET_OK); + + /* If any forebear certificate is not allowed to sign we must + assume all decendents are proxies and cannot sign either */ + if (prevIsCA) + { + if (IsCA) + { + new_grst_cert->type = GRST_CERT_TYPE_CA; + } + else + { + new_grst_cert->type = GRST_CERT_TYPE_EEC; + first_non_ca = i; + ucuserdn = new_grst_cert->dn; + new_grst_cert->delegation + = (lastcert == NULL) ? i : i + 1; + } + } + else + { + new_grst_cert->type = GRST_CERT_TYPE_PROXY; + + IsCA = FALSE; + /* Force proxy check next iteration. Important because I can + sign any CA I create! */ + + new_grst_cert->delegation = (lastcert == NULL) ? i : i + 1; + } + + if (!prevIsCA) + { + /* issuer didn't have CA status, so this is (at best) a proxy: + check for bad proxy extension*/ + + if (prevIsLimited) /* we reject proxies of limited proxies! */ + { + new_grst_cert->errors |= GRST_CERT_BAD_CHAIN; + chain_errors |= GRST_CERT_BAD_CHAIN; + } + + /* User not allowed to sign shortened DN */ + if (len2 > len) + { + new_grst_cert->errors |= GRST_CERT_BAD_CHAIN; + chain_errors |= GRST_CERT_BAD_CHAIN; + } + + /* Proxy subject must begin with issuer. */ + if (strncmp((const char*)new_grst_cert->dn, new_grst_cert->issuer, len2) != 0) + { + new_grst_cert->errors |= GRST_CERT_BAD_CHAIN; + chain_errors |= GRST_CERT_BAD_CHAIN; + } + + /* Set pointer to end of base DN in cert_DN */ + proxy_part_DN = &(new_grst_cert->dn[len2]); + + /* First attempt at support for Old and New style GSI + proxies: /CN=anything is ok for now */ + if (strncmp((const char*)proxy_part_DN, "/CN=", 4) != 0) + { + new_grst_cert->errors |= GRST_CERT_BAD_CHAIN; + chain_errors |= GRST_CERT_BAD_CHAIN; + } + + if (strncmp((const char*)proxy_part_DN, "/CN=limited proxy", 17) == 0) + prevIsLimited = 1; /* ready for next cert ... */ + + for (j=0; j < X509_get_ext_count(cert); ++j) + { + ex = X509_get_ext(cert, j); + OBJ_obj2txt(s,sizeof(s),X509_EXTENSION_get_object(ex),1); + + if (strcmp(s, GRST_VOMS_OID) == 0) /* a VOMS extension */ + { + GRSTx509ChainVomsAdd(&grst_cert, + new_grst_cert->notbefore, + new_grst_cert->notafter, + ex, + ucuserdn, + vomsdir); + grst_cert->delegation = (lastcert == NULL) ? i : i+1; + } + } + + } + } + + + } /* end of for loop */ + + if (cacert != NULL) X509_free(cacert); + + return GRST_RET_OK; +} + +/// Check certificate chain for GSI proxy acceptability. +int GRSTx509CheckChain(int *first_non_ca, X509_STORE_CTX *ctx) +/// +/// Returns X509_V_OK/GRST_RET_OK if valid; OpenSSL X509 errors otherwise. +/// +/// Inspired by GSIcheck written by Mike Jones, SVE, Manchester Computing, +/// The University of Manchester. +/// +/// The GridSite version handles old and new style Globus proxies, and +/// proxies derived from user certificates issued with "X509v3 Basic +/// Constraints: CA:FALSE" (eg UK e-Science CA) +/// +/// We do not check chain links between certs here: this is done by +/// GRST_check_issued/X509_check_issued in mod_ssl's ssl_engine_init.c +/// +/// TODO: we do not yet check ProxyCertInfo and ProxyCertPolicy extensions +/// (although via GRSTx509KnownCriticalExts() we can accept them.) +{ + STACK_OF(X509) *certstack; /* Points to the client's cert chain */ + X509 *cert; /* Points to the client's cert */ + int depth; /* Depth of cert chain */ + size_t len,len2; /* Lengths of issuer and cert DN */ + int IsCA; /* Holds whether cert is allowed to sign */ + int prevIsCA=TRUE; /* Holds whether previous cert in chain is + allowed to sign */ + int prevIsLimited; /* previous cert was proxy and limited */ + int i; /* Iteration variables */ + char *cert_DN; /* Pointer to current-certificate-in-chain's + DN */ + char *issuer_DN; /* Pointer to + issuer-of-current-cert-in-chain's DN */ + char *proxy_part_DN; /* Pointer to end part of current-cert-in-chain + maybe eg "/CN=proxy" */ + time_t now; + + time(&now); + + *first_non_ca = 0; /* set to something predictable if things fail */ + + /* Check for context */ + if (!ctx) return X509_V_ERR_INVALID_CA; + /* Can't GSI-verify if there is no context. Here and throughout this + function we report all errors as X509_V_ERR_INVALID_CA. */ + + /* Set necessary preliminary values */ + IsCA = TRUE; /* =prevIsCA - start from a CA */ + prevIsLimited = 0; + + /* Get the client cert chain */ + certstack = X509_STORE_CTX_get_chain(ctx); /* Get the client's chain */ + depth = sk_X509_num(certstack); /* How deep is that chain? */ + + /* Check the client chain */ + for (i=depth-1; i >= 0; --i) + /* loop through client-presented chain starting at CA end */ + { + prevIsCA=IsCA; + + /* Check for X509 certificate and point to it with 'cert' */ + if ( (cert = sk_X509_value(certstack, i)) ) + { + /* we check times and reject immediately if invalid */ + + if (now < + GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(cert)),0)) + return X509_V_ERR_INVALID_CA; + + if (now > + GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(cert)),0)) + return X509_V_ERR_INVALID_CA; + + /* If any forebear certificate is not allowed to sign we must + assume all decendents are proxies and cannot sign either */ + if (prevIsCA) + { + /* always treat the first cert (from the CA files) as a CA */ + if (i == depth-1) IsCA = TRUE; + /* check if this cert is valid CA for signing certs */ + else IsCA = (GRSTx509IsCA(cert) == GRST_RET_OK); + + if (!IsCA) *first_non_ca = i; + } + else + { + IsCA = FALSE; + /* Force proxy check next iteration. Important because I can + sign any CA I create! */ + } + + cert_DN = X509_NAME_oneline(X509_get_subject_name(cert),NULL,0); + issuer_DN = X509_NAME_oneline(X509_get_issuer_name(cert),NULL,0); + len = strlen(cert_DN); + len2 = strlen(issuer_DN); + + /* issuer didn't have CA status, so this is (at best) a proxy: + check for bad proxy extension*/ + + if (!prevIsCA) + { + if (prevIsLimited) /* we reject proxies of limited proxies! */ + return X509_V_ERR_INVALID_CA; + + /* User not allowed to sign shortened DN */ + if (len2 > len) return X509_V_ERR_INVALID_CA; + + /* Proxy subject must begin with issuer. */ + if (strncmp((const char*)cert_DN, issuer_DN, len2) != 0) + return X509_V_ERR_INVALID_CA; + + /* Set pointer to end of base DN in cert_DN */ + proxy_part_DN = &cert_DN[len2]; + + /* First attempt at support for Old and New style GSI + proxies: /CN=anything is ok for now */ + if (strncmp((const char*)proxy_part_DN, "/CN=", 4) != 0) + return X509_V_ERR_INVALID_CA; + + if ((strncmp((const char*)proxy_part_DN, "/CN=limited proxy", 17) == 0) && + (i > 0)) prevIsLimited = 1; /* ready for next cert ... */ + } + } + } + + /* Check cert whose private key is being used by client. If previous in + chain is not allowed to be a CA then need to check this final cert for + valid proxy-icity too */ + if (!prevIsCA) + { + if (prevIsLimited) return X509_V_ERR_INVALID_CA; + /* we do not accept proxies signed by limited proxies */ + + if ( (cert = sk_X509_value(certstack, 0)) ) + { + /* Load DN & length of DN and either its issuer or the + first-bad-issuer-in-chain */ + cert_DN = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0); + issuer_DN = X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0); + len = strlen(cert_DN); + len2 = strlen(issuer_DN); + + /* issuer didn't have CA status, check for bad proxy extension */ + + if (len2 > len) return X509_V_ERR_INVALID_CA; + /* User not allowed to sign shortened DN */ + + if (strncmp((const char*)cert_DN, issuer_DN, len2) != 0) + return X509_V_ERR_INVALID_CA; + /* Proxy subject must begin with issuer. */ + + proxy_part_DN = &cert_DN[len2]; + /* Set pointer to end of DN base in cert_DN */ + + /* Remander of subject must be either "/CN=proxy" or + "/CN=limited proxy" (or /CN=XYZ for New style GSI) */ + + /* First attempt at support for Old and New style GSI + proxies: /CN=anything is ok for now. */ + if (strncmp((const char*)proxy_part_DN, "/CN=", 4) != 0) + return X509_V_ERR_INVALID_CA; + } + } + + return X509_V_OK; /* this is also GRST_RET_OK, of course - by choice */ +} + +/// Example VerifyCallback routine +int GRSTx509VerifyCallback (int ok, X509_STORE_CTX *ctx) +{ + int errnum = X509_STORE_CTX_get_error(ctx); + int errdepth = X509_STORE_CTX_get_error_depth(ctx); + int first_non_ca; + +#ifndef X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION +#define X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION 34 +#endif + + if (errnum == X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION) + { + if (GRSTx509KnownCriticalExts(X509_STORE_CTX_get_current_cert(ctx)) + == GRST_RET_OK) + { + ok = TRUE; + errnum = X509_V_OK; + X509_STORE_CTX_set_error(ctx, errnum); + } + } + else if ((errdepth == 0) && + (errnum == X509_V_OK) && + (GRSTx509CheckChain(&first_non_ca, ctx) != X509_V_OK)) ok = FALSE; + + + return ok; + +// check this + +// if (ok) return GRST_RET_OK; +// else return GRST_RET_FAILED; +} + +/// Get the VOMS attributes in the given extension +int GRSTx509ParseVomsExt(int *lastcred, int maxcreds, size_t credlen, + char *creds, time_t time1_time, time_t time2_time, + X509_EXTENSION *ex, char *ucuserdn, char *vomsdir) +/// +/// Puts any VOMS credentials found into the Compact Creds string array +/// starting at *creds. Always returns GRST_RET_OK - even for invalid +/// credentials, which are just ignored. +{ +#define MAXTAG 500 +#define GRST_ASN1_COORDS_FQAN "-1-1-%d-1-7-1-2-1-2-%d" +#define GRST_ASN1_COORDS_USER_DN "-1-1-%d-1-2-1-1-1-1-%%d-1-%%d" +#define GRST_ASN1_COORDS_TIME1 "-1-1-%d-1-6-1" +#define GRST_ASN1_COORDS_TIME2 "-1-1-%d-1-6-2" + ASN1_OCTET_STRING *asn1data; + unsigned char *asn1string; + char acuserdn[200], + dn_coords[200], fqan_coords[200], time1_coords[200], + time2_coords[200]; + long asn1length; + int lasttag=-1, itag, i, acnumber = 1; + struct GRSTasn1TagList taglist[MAXTAG+1]; + time_t actime1, actime2, time_now; + + asn1data = X509_EXTENSION_get_data(ex); + asn1string = (unsigned char*) (ASN1_STRING_data((asn1data))); + asn1length = (long)ASN1_STRING_length(asn1data); + + GRSTasn1ParseDump(NULL, asn1string, asn1length, taglist, MAXTAG, &lasttag); + + for (acnumber = 1; ; ++acnumber) /* go through ACs one by one */ + { + snprintf(dn_coords, sizeof(dn_coords), GRST_ASN1_COORDS_USER_DN, acnumber); + if (GRSTasn1GetX509Name(acuserdn, sizeof(acuserdn), dn_coords, + (char*)asn1string, taglist, lasttag) != GRST_RET_OK) break; + + if (GRSTx509NameCmp(ucuserdn, acuserdn) != 0) continue; + + if (GRSTx509VerifyVomsSig(&time1_time, &time2_time, + asn1string, taglist, lasttag, vomsdir, acnumber) + != GRST_RET_OK) continue; + + snprintf(time1_coords, sizeof(time1_coords), GRST_ASN1_COORDS_TIME1, acnumber); + itag = GRSTasn1SearchTaglist(taglist, lasttag, time1_coords); + actime1 = GRSTasn1TimeToTimeT(&asn1string[taglist[itag].start+ + taglist[itag].headerlength], + taglist[itag].length); + if (actime1 > time1_time) time1_time = actime1; + + snprintf(time2_coords, sizeof(time2_coords), GRST_ASN1_COORDS_TIME2, acnumber); + itag = GRSTasn1SearchTaglist(taglist, lasttag, time2_coords); + actime2 = GRSTasn1TimeToTimeT(&asn1string[taglist[itag].start+ + taglist[itag].headerlength], + taglist[itag].length); + if (actime2 < time2_time) time2_time = actime2; + + time(&time_now); + if ((time1_time > time_now + 300) || (time2_time < time_now)) + continue; /* expiration isnt invalidity ...? */ + + for (i=1; ; ++i) + { + snprintf(fqan_coords, sizeof(fqan_coords), GRST_ASN1_COORDS_FQAN, acnumber, i); + itag = GRSTasn1SearchTaglist(taglist, lasttag, fqan_coords); + + if (itag > -1) + { + if (*lastcred < maxcreds - 1) + { + ++(*lastcred); + snprintf(&creds[*lastcred * (credlen + 1)], credlen+1, + "VOMS %010lu %010lu 0 %.*s", + time1_time, time2_time, + taglist[itag].length, + &asn1string[taglist[itag].start+ + taglist[itag].headerlength]); + } + } + else break; + } + } + + return GRST_RET_OK; +} + +/// Get the VOMS attributes in the extensions to the given cert stack +int GRSTx509GetVomsCreds(int *lastcred, int maxcreds, size_t credlen, + char *creds, X509 *usercert, STACK_OF(X509) *certstack, + char *vomsdir) +/// +/// Puts any VOMS credentials found into the Compact Creds string array +/// starting at *creds. Always returns GRST_RET_OK. +{ + int i, j; + char s[80]; + unsigned char *ucuser; + X509_EXTENSION *ex; + X509 *cert; + time_t time1_time = 0, time2_time = 0, uctime1_time, uctime2_time; + + uctime1_time = + GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(usercert)),0); + uctime2_time = + GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(usercert)),0); + ucuser = (unsigned char*) + X509_NAME_oneline(X509_get_subject_name(usercert), NULL, 0); + + for (j=sk_X509_num(certstack)-1; j >= 0; --j) + { + cert = sk_X509_value(certstack, j); + + time1_time = + GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(cert)),0); + uctime1_time = (time1_time > uctime1_time) ? time1_time:uctime1_time; + + time2_time = + GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(cert)),0); + uctime2_time = (time2_time < uctime2_time) ? time2_time:uctime2_time; + + for (i=0; i < X509_get_ext_count(cert); ++i) + { + ex = X509_get_ext(cert, i); + OBJ_obj2txt(s, sizeof(s), X509_EXTENSION_get_object(ex), 1); + + if (strcmp(s, GRST_VOMS_OID) == 0) /* a VOMS extension */ + { + GRSTx509ParseVomsExt(lastcred, maxcreds, credlen, creds, + uctime1_time, uctime2_time, + ex, (char*)ucuser, vomsdir); + } + } + } + + return GRST_RET_OK; +} + +/// Turn a Compact Cred line into a GRSTgaclCred object +GRSTgaclCred *GRSTx509CompactToCred(char *grst_cred) +/// +/// Returns pointer to created GRSTgaclCred or NULL or failure. +{ + int delegation; + char *p, *encoded; + time_t now, notbefore, notafter; + GRSTgaclCred *cred = NULL; + + time(&now); + + if (grst_cred == NULL) return NULL; /* just in case */ + + if (strncmp((const char*)grst_cred, "X509USER ", 9) == 0) + { + if ((sscanf(grst_cred, "X509USER %lu %lu %d", + ¬before, ¬after, &delegation) == 3) + && (now >= notbefore) + && (now <= notafter) + && (p = index(grst_cred, ' ')) + && (p = index(++p, ' ')) + && (p = index(++p, ' ')) + && (p = index(++p, ' '))) + { + encoded = GRSThttpUrlMildencode(&p[1]); + cred = GRSTgaclCredCreate("dn:", encoded); + free(encoded); + GRSTgaclCredSetDelegation(cred, delegation); + } + + return cred; + } + + if (strncmp((const char*)grst_cred, "VOMS ", 5) == 0) + { + if ((sscanf(grst_cred, "VOMS %lu %lu %d", + ¬before, ¬after, &delegation) == 3) + && (now >= notbefore) + && (now <= notafter) + && (p = index(grst_cred, ' ')) + && (p = index(++p, ' ')) + && (p = index(++p, ' ')) + && (p = index(++p, ' '))) + { + /* include /VO/group/subgroup/Role=role/Capability=cap */ + + if (p[1] != '/') return NULL; /* must begin with / */ + + encoded = GRSThttpUrlMildencode(&p[1]); + cred = GRSTgaclCredCreate("fqan:", encoded); + free(encoded); + GRSTgaclCredSetDelegation(cred, delegation); + } + + return cred; + } + + return NULL; /* dont recognise this credential type */ +} + +/// Get the credentials in an X509 cert/GSI proxy, including any VOMS +int GRSTx509CompactCreds(int *lastcred, int maxcreds, size_t credlen, + char *creds, STACK_OF(X509) *certstack, char *vomsdir, + X509 *peercert) +/// +/// Credentials are placed in Compact Creds string array at *creds. +/// +/// Function returns GRST_RET_OK on success, or GRST_RET_FAILED if +/// some inconsistency found in certificate. +{ + int i, delegation = 0; + char credtemp[credlen+1]; + X509 *cert, *usercert = NULL, *gsiproxycert = NULL; + + *lastcred = -1; + + for (i = sk_X509_num(certstack) - 1; i >= 0; --i) + { + cert = sk_X509_value(certstack, i); + + if (usercert != NULL) + { /* found a (GSI proxy) cert after the user cert */ + gsiproxycert = cert; + ++delegation; + } + + if ((usercert == NULL) && + (i < sk_X509_num(certstack) - 1) && + (GRSTx509IsCA(cert) != GRST_RET_OK)) usercert = cert; + /* found the 1st non-CA cert */ + } + + if (peercert != NULL) + { + if (usercert != NULL) /* found a (GSI proxy) cert after user cert */ + { + gsiproxycert = peercert; + ++delegation; + } + + if ((usercert == NULL) && + (GRSTx509IsCA(peercert) != GRST_RET_OK)) usercert = peercert; + /* found the 1st non-CA cert */ + } + + if ((usercert == NULL) /* if no usercert ("EEC"), we're not interested */ + || + (snprintf(credtemp, credlen+1, "X509USER %010lu %010lu %d %s", + GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(usercert)),0), + GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(usercert)),0), + delegation, + X509_NAME_oneline(X509_get_subject_name(usercert), NULL, 0)) >= credlen+1) + || + (*lastcred >= maxcreds-1)) + { + *lastcred = -1; /* just in case the caller looks at it */ + return GRST_RET_FAILED; /* tell caller that things didn't work out */ + } + + ++(*lastcred); + strcpy(&creds[*lastcred * (credlen + 1)], credtemp); + + if ((gsiproxycert != NULL) + && + (snprintf(credtemp, credlen+1, "GSIPROXY %010lu %010lu %d %s", + GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(gsiproxycert)),0), + GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(gsiproxycert)),0), + delegation, + X509_NAME_oneline(X509_get_subject_name(gsiproxycert), NULL, 0)) < credlen+1) + && + (*lastcred < maxcreds-1)) + { + ++(*lastcred); + strcpy(&creds[*lastcred * (credlen + 1)], credtemp); + GRSTx509GetVomsCreds(lastcred, maxcreds, credlen, creds, + usercert, certstack, vomsdir); + } + + return GRST_RET_OK; +} + +/// Find proxy file name of the current user +char *GRSTx509FindProxyFileName(void) +/// +/// Return a string with the proxy file name or NULL if not present. +/// This function does not check if the proxy has expired. +{ + char *p; + + p = getenv("X509_USER_PROXY"); + + if (p != NULL) return strdup(p); + + p = malloc(sizeof("/tmp/x509up_uXYYYXXXYYY")); + + sprintf(p, "/tmp/x509up_u%d", getuid()); + + return p; +} + +static void mpcerror(FILE *debugfp, char *msg) +{ + if (debugfp != NULL) + { + fputs(msg, debugfp); + ERR_print_errors_fp(debugfp); + } +} + +/// Make a GSI Proxy chain from a request, certificate and private key +int GRSTx509MakeProxyCert(char **proxychain, FILE *debugfp, + char *reqtxt, char *cert, char *key, int minutes) +/// +/// The proxy chain is returned in *proxychain. If debugfp is non-NULL, +/// errors are output to that file pointer. The proxy will expired in +/// the given number of minutes starting from the current time. +{ + char *ptr, *certchain; + int i, ncerts; + long serial = 1234, ptrlen; + EVP_PKEY *pkey, *CApkey; + const EVP_MD *digest; + X509 *certs[GRST_MAX_CHAIN_LEN]; + X509_REQ *req; + X509_NAME *name, *CAsubject, *newsubject; + X509_NAME_ENTRY *ent; + FILE *fp; + BIO *reqmem, *certmem; + time_t notAfter; + + /* read in the request */ + reqmem = BIO_new(BIO_s_mem()); + BIO_puts(reqmem, reqtxt); + + if (!(req = PEM_read_bio_X509_REQ(reqmem, NULL, NULL, NULL))) + { + mpcerror(debugfp, + "GRSTx509MakeProxyCert(): error reading request from BIO memory\n"); + BIO_free(reqmem); + return GRST_RET_FAILED; + } + + BIO_free(reqmem); + + /* verify signature on the request */ + if (!(pkey = X509_REQ_get_pubkey(req))) + { + mpcerror(debugfp, + "GRSTx509MakeProxyCert(): error getting public key from request\n"); + + X509_REQ_free(req); + return GRST_RET_FAILED; + } + + if (X509_REQ_verify(req, pkey) != 1) + { + mpcerror(debugfp, + "GRSTx509MakeProxyCert(): error verifying signature on certificate\n"); + + X509_REQ_free(req); + return GRST_RET_FAILED; + } + + /* read in the signing certificate */ + if (!(fp = fopen(cert, "r"))) + { + mpcerror(debugfp, + "GRSTx509MakeProxyCert(): error opening signing certificate file\n"); + + X509_REQ_free(req); + return GRST_RET_FAILED; + } + + for (ncerts = 1; ncerts < GRST_MAX_CHAIN_LEN; ++ncerts) + if ((certs[ncerts] = PEM_read_X509(fp, NULL, NULL, NULL)) == NULL) break; + + if (ncerts == 1) /* zeroth cert with be new proxy cert */ + { + mpcerror(debugfp, + "GRSTx509MakeProxyCert(): error reading signing certificate file\n"); + + X509_REQ_free(req); + return GRST_RET_FAILED; + } + + fclose(fp); + + CAsubject = X509_get_subject_name(certs[1]); + + /* read in the CA private key */ + if (!(fp = fopen(key, "r"))) + { + mpcerror(debugfp, + "GRSTx509MakeProxyCert(): error reading signing private key file\n"); + + X509_REQ_free(req); + return GRST_RET_FAILED; + } + + if (!(CApkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL))) + { + mpcerror(debugfp, + "GRSTx509MakeProxyCert(): error reading signing private key in file\n"); + + X509_REQ_free(req); + return GRST_RET_FAILED; + } + + fclose(fp); + + /* get subject name */ + if (!(name = X509_REQ_get_subject_name(req))) + { + mpcerror(debugfp, + "GRSTx509MakeProxyCert(): error getting subject name from request\n"); + + X509_REQ_free(req); + return GRST_RET_FAILED; + } + + /* create new certificate */ + if (!(certs[0] = X509_new())) + { + mpcerror(debugfp, + "GRSTx509MakeProxyCert(): error creating X509 object\n"); + + X509_REQ_free(req); + return GRST_RET_FAILED; + } + + /* set version number for the certificate (X509v3) and the serial number + need 3 = v4 for GSI proxy?? */ + if (X509_set_version(certs[0], 3L) != 1) + { + mpcerror(debugfp, + "GRSTx509MakeProxyCert(): error setting certificate version\n"); + + X509_REQ_free(req); + return GRST_RET_FAILED; + } + + ASN1_INTEGER_set(X509_get_serialNumber(certs[0]), serial++); + + if (!(name = X509_get_subject_name(certs[1]))) + { + mpcerror(debugfp, + "GRSTx509MakeProxyCert(): error getting subject name from CA certificate\n"); + + X509_REQ_free(req); + return GRST_RET_FAILED; + } + + if (X509_set_issuer_name(certs[0], name) != 1) + { + mpcerror(debugfp, + "GRSTx509MakeProxyCert(): error setting issuer name of certificate\n"); + + X509_REQ_free(req); + return GRST_RET_FAILED; + } + + /* set issuer and subject name of the cert from the req and the CA */ + ent = X509_NAME_ENTRY_create_by_NID(NULL, OBJ_txt2nid("commonName"), + MBSTRING_ASC, (unsigned char*)"proxy", -1); + + newsubject = X509_NAME_dup(CAsubject); + + X509_NAME_add_entry(newsubject, ent, -1, 0); + + if (X509_set_subject_name(certs[0], newsubject) != 1) + { + mpcerror(debugfp, + "GRSTx509MakeProxyCert(): error setting subject name of certificate\n"); + + X509_REQ_free(req); + return GRST_RET_FAILED; + } + + X509_NAME_free(newsubject); + X509_NAME_ENTRY_free(ent); + + /* set public key in the certificate */ + if (X509_set_pubkey(certs[0], pkey) != 1) + { + mpcerror(debugfp, + "GRSTx509MakeProxyCert(): error setting public key of the certificate\n"); + + X509_REQ_free(req); + return GRST_RET_FAILED; + } + + /* set duration for the certificate */ + if (!(X509_gmtime_adj(X509_get_notBefore(certs[0]), -GRST_BACKDATE_SECONDS))) + { + mpcerror(debugfp, + "GRSTx509MakeProxyCert(): error setting beginning time of the certificate\n"); + + X509_REQ_free(req); + return GRST_RET_FAILED; + } + + if (!(X509_gmtime_adj(X509_get_notAfter(certs[0]), 60 * minutes))) + { + mpcerror(debugfp, + "GRSTx509MakeProxyCert(): error setting ending time of the certificate\n"); + + X509_REQ_free(req); + return GRST_RET_FAILED; + } + + /* go through chain making sure this proxy is not longer lived */ + + notAfter = + GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(certs[0])), 0); + + for (i=1; i < ncerts; ++i) + if (notAfter > + GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(certs[i])), + 0)) + { + notAfter = + GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(certs[i])), + 0); + + ASN1_UTCTIME_set(X509_get_notAfter(certs[0]), notAfter); + } + + /* sign the certificate with the signing private key */ + if (EVP_PKEY_type(CApkey->type) == EVP_PKEY_RSA) + digest = EVP_md5(); + else + { + mpcerror(debugfp, + "GRSTx509MakeProxyCert(): error checking signing private key for a valid digest\n"); + + X509_REQ_free(req); + return GRST_RET_FAILED; + } + + if (!(X509_sign(certs[0], CApkey, digest))) + { + mpcerror(debugfp, + "GRSTx509MakeProxyCert(): error signing certificate\n"); + + X509_REQ_free(req); + return GRST_RET_FAILED; + } + + /* store the completed certificate chain */ + + certchain = strdup(""); + + for (i=0; i < ncerts; ++i) + { + certmem = BIO_new(BIO_s_mem()); + + if (PEM_write_bio_X509(certmem, certs[i]) != 1) + { + mpcerror(debugfp, + "GRSTx509MakeProxyCert(): error writing certificate to memory BIO\n"); + + X509_REQ_free(req); + return GRST_RET_FAILED; + } + + ptrlen = BIO_get_mem_data(certmem, &ptr); + + certchain = realloc(certchain, strlen(certchain) + ptrlen + 1); + + strncat(certchain, ptr, ptrlen); + + BIO_free(certmem); + X509_free(certs[i]); + } + + EVP_PKEY_free(pkey); + EVP_PKEY_free(CApkey); + X509_REQ_free(req); + + *proxychain = certchain; + return GRST_RET_OK; +} + +/// Find a proxy file in the proxy cache +char *GRSTx509CachedProxyFind(char *proxydir, char *delegation_id, + char *user_dn) +/// +/// Returns the full path and file name of proxy file associated +/// with given delegation ID and user DN. +/// +/// Return a pointer to a malloc'd string with the full path of the +/// proxy file corresponding to the given delegation_id, or NULL +/// if not found. +{ + char *user_dn_enc; + char* proxyfile = (char*)malloc(16384); + struct stat statbuf; + + user_dn_enc = GRSThttpUrlEncode(user_dn); + + sprintf(proxyfile, "%s/%s/%s/userproxy.pem", + proxydir, user_dn_enc, delegation_id); + + free(user_dn_enc); + + if ((stat(proxyfile, &statbuf) != 0) || !S_ISREG(statbuf.st_mode)) + { + free(proxyfile); + return NULL; + } + + return proxyfile; +} + +/// Find a temporary proxy private key file in the proxy cache +char *GRSTx509CachedProxyKeyFind(char *proxydir, char *delegation_id, + char *user_dn) +/// +/// Returns the full path and file name of the private key file associated +/// with given delegation ID and user DN. +/// +/// Return a pointer to a malloc'd string with the full path of the +/// private proxy key corresponding to the given delegation_id, or NULL +/// if not found. +{ + char *user_dn_enc; + char* prvkeyfile = (char*) malloc(16384); + struct stat statbuf; + + user_dn_enc = GRSThttpUrlEncode(user_dn); + + sprintf(prvkeyfile, "%s/cache/%s/%s/userkey.pem", + proxydir, user_dn_enc, delegation_id); + + free(user_dn_enc); + + if ((stat(prvkeyfile, &statbuf) != 0) || !S_ISREG(statbuf.st_mode)) + { + free(prvkeyfile); + return NULL; + } + + return prvkeyfile; +} + +static void mkdir_printf(mode_t mode, char *fmt, ...) +{ + int ret; + char path[16384]; + va_list ap; + + va_start(ap, fmt); + vsprintf(path, fmt, ap); + va_end(ap); + + ret = mkdir(path, mode); +} + +/// Create a X.509 request for a GSI proxy and its private key +int GRSTx509CreateProxyRequest(char **reqtxt, char **keytxt, char *ocspurl) +/// +/// Returns GRST_RET_OK on success, non-zero otherwise. Request string +/// and private key are PEM encoded strings +{ + char *ptr; + size_t ptrlen; + RSA *keypair; + X509_NAME *subject; + X509_NAME_ENTRY *ent; + EVP_PKEY *pkey; + X509_REQ *certreq; + BIO *reqmem, *keymem; + const EVP_MD *digest; + + /* create key pair and put it in a PEM string */ + + if ((keypair = RSA_generate_key(GRST_KEYSIZE, 65537, NULL, NULL)) == NULL) + return 1; + + keymem = BIO_new(BIO_s_mem()); + if (!PEM_write_bio_RSAPrivateKey(keymem, keypair, NULL, NULL, 0, NULL, NULL)) + { + BIO_free(keymem); + return 3; + } + + ptrlen = BIO_get_mem_data(keymem, &ptr); + + *keytxt = malloc(ptrlen + 1); + memcpy(*keytxt, ptr, ptrlen); + (*keytxt)[ptrlen] = '\0'; + + BIO_free(keymem); + + /* now create the certificate request */ + + certreq = X509_REQ_new(); + + OpenSSL_add_all_algorithms(); + + pkey = EVP_PKEY_new(); + EVP_PKEY_assign_RSA(pkey, keypair); + + X509_REQ_set_pubkey(certreq, pkey); + + subject = X509_NAME_new(); + ent = X509_NAME_ENTRY_create_by_NID(NULL, OBJ_txt2nid("organizationName"), + MBSTRING_ASC, (unsigned char*)"Dummy", -1); + X509_NAME_add_entry (subject, ent, -1, 0); + X509_REQ_set_subject_name (certreq, subject); + + digest = EVP_md5(); + X509_REQ_sign(certreq, pkey, digest); + + reqmem = BIO_new(BIO_s_mem()); + PEM_write_bio_X509_REQ(reqmem, certreq); + ptrlen = BIO_get_mem_data(reqmem, &ptr); + + *reqtxt = malloc(ptrlen + 1); + memcpy(*reqtxt, ptr, ptrlen); + (*reqtxt)[ptrlen] = '\0'; + + BIO_free(reqmem); + + X509_REQ_free(certreq); + + return 0; +} + +/// Make and store a X.509 request for a GSI proxy +int GRSTx509MakeProxyRequest(char **reqtxt, char *proxydir, + char *delegation_id, char *user_dn) +/// +/// Returns GRST_RET_OK on success, non-zero otherwise. Request string +/// is PEM encoded, and the key is stored in the temporary cache under +/// proxydir +{ + char prvkeyfile[16384], *ptr, *user_dn_enc; + size_t ptrlen; + FILE *fp; + RSA *keypair; + X509_NAME *subject; + X509_NAME_ENTRY *ent; + EVP_PKEY *pkey; + X509_REQ *certreq; + BIO *reqmem; + const EVP_MD *digest; + + if (strcmp(user_dn, "cache") == 0) return GRST_RET_FAILED; + + user_dn_enc = GRSThttpUrlEncode(user_dn); + + /* create directories if necessary */ + + mkdir_printf(S_IRUSR | S_IWUSR | S_IXUSR, + "%s/cache", proxydir); + mkdir_printf(S_IRUSR | S_IWUSR | S_IXUSR, + "%s/cache/%s", proxydir, user_dn_enc); + mkdir_printf(S_IRUSR | S_IWUSR | S_IXUSR, + "%s/cache/%s/%s", proxydir, user_dn_enc, delegation_id); + + /* make the new proxy private key */ + + sprintf(prvkeyfile, "%s/cache/%s/%s/userkey.pem", + proxydir, user_dn_enc, delegation_id); + + if (prvkeyfile == NULL) + { + free(user_dn_enc); + return GRST_RET_FAILED; + } + + if ((keypair = RSA_generate_key(GRST_KEYSIZE, 65537, NULL, NULL)) == NULL) + return 1; + + if ((fp = fopen(prvkeyfile, "w")) == NULL) return 2; + + chmod(prvkeyfile, S_IRUSR | S_IWUSR); + free(user_dn_enc); + + if (!PEM_write_RSAPrivateKey(fp, keypair, NULL, NULL, 0, NULL, NULL)) + return 3; + + if (fclose(fp) != 0) return 4; + + /* now create the certificate request */ + + certreq = X509_REQ_new(); + if (certreq == NULL) return 5; + + OpenSSL_add_all_algorithms(); + + pkey = EVP_PKEY_new(); + EVP_PKEY_assign_RSA(pkey, keypair); + + X509_REQ_set_pubkey(certreq, pkey); + + subject = X509_NAME_new(); + ent = X509_NAME_ENTRY_create_by_NID(NULL, OBJ_txt2nid("organizationName"), + MBSTRING_ASC, (unsigned char*)"Dummy", -1); + X509_NAME_add_entry (subject, ent, -1, 0); + X509_REQ_set_subject_name (certreq, subject); + + digest = EVP_md5(); + X509_REQ_sign(certreq, pkey, digest); + + reqmem = BIO_new(BIO_s_mem()); + PEM_write_bio_X509_REQ(reqmem, certreq); + ptrlen = BIO_get_mem_data(reqmem, &ptr); + + *reqtxt = malloc(ptrlen + 1); + memcpy(*reqtxt, ptr, ptrlen); + (*reqtxt)[ptrlen] = '\0'; + + BIO_free(reqmem); + + X509_REQ_free(certreq); + + return 0; +} + +/// Destroy stored GSI proxy files +int GRSTx509ProxyDestroy(char *proxydir, char *delegation_id, char *user_dn) +/// +/// Returns GRST_RET_OK on success, non-zero otherwise. +/// (Including GRST_RET_NO_SUCH_FILE if the private key or cert chain +/// were not found.) +{ + int ret = GRST_RET_OK; + char filename[16384], *user_dn_enc; + + if (strcmp(user_dn, "cache") == 0) return GRST_RET_FAILED; + + user_dn_enc = GRSThttpUrlEncode(user_dn); + + /* proxy file */ + + sprintf(filename, "%s/%s/%s/userproxy.pem", + proxydir, user_dn_enc, delegation_id); + + if (unlink(filename) != 0) ret = GRST_RET_NO_SUCH_FILE; + + /* voms file */ + + sprintf(filename, "%s/%s/%s/voms.attributes", + proxydir, user_dn_enc, delegation_id); + + unlink(filename); + + return ret; +} + +/// Get start and finish validity times of stored GSI proxy file +int GRSTx509ProxyGetTimes(char *proxydir, char *delegation_id, char *user_dn, + time_t *start, time_t *finish) +/// +/// Returns GRST_RET_OK on success, non-zero otherwise. +/// (Including GRST_RET_NO_SUCH_FILE if the cert chain was not found.) +{ + char filename[16384], *user_dn_enc; + FILE *fp; + X509 *cert; + + if (strcmp(user_dn, "cache") == 0) return GRST_RET_FAILED; + + user_dn_enc = GRSThttpUrlEncode(user_dn); + + sprintf(filename, "%s/%s/%s/userproxy.pem", + proxydir, user_dn_enc, delegation_id); + + free(user_dn_enc); + + if (filename == NULL) return GRST_RET_FAILED; + + fp = fopen(filename, "r"); + + if (fp == NULL) return GRST_RET_NO_SUCH_FILE; + + cert = PEM_read_X509(fp, NULL, NULL, NULL); /* first cert is X.509 PC */ + + fclose(fp); + + *start = GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(cert)),0); + *finish = GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(cert)),0); + + X509_free(cert); + + return GRST_RET_OK; +} + +/// Create a stack of X509 certificate from a PEM-encoded string +int GRSTx509StringToChain(STACK_OF(X509) **certstack, char *certstring) +/// +/// Creates a dynamically allocated stack of X509 certificate objects +/// by walking through the PEM-encoded X509 certificates. +/// +/// Returns GRST_RET_OK on success, non-zero otherwise. +{ + STACK_OF(X509_INFO) *sk=NULL; + BIO *certbio; + X509_INFO *xi; + + *certstack = sk_X509_new_null(); + if (*certstack == NULL) return GRST_RET_FAILED; + + certbio = BIO_new_mem_buf(certstring, -1); + + if (!(sk=PEM_X509_INFO_read_bio(certbio, NULL, NULL, NULL))) + { + BIO_free(certbio); + sk_X509_INFO_free(sk); + sk_X509_free(*certstack); + return GRST_RET_FAILED; + } + + while (sk_X509_INFO_num(sk)) + { + xi=sk_X509_INFO_shift(sk); + if (xi->x509 != NULL) + { + sk_X509_push(*certstack, xi->x509); + xi->x509=NULL; + } + X509_INFO_free(xi); + } + + if (!sk_X509_num(*certstack)) + { + BIO_free(certbio); + sk_X509_INFO_free(sk); + sk_X509_free(*certstack); + return GRST_RET_FAILED; + } + + BIO_free(certbio); + sk_X509_INFO_free(sk); + + return GRST_RET_OK; +} + +/// Returns a Delegation ID based on hash of GRST_CRED_0, ... +char *GRSTx509MakeDelegationID(void) +/// +/// Returns a malloc'd string with Delegation ID made by SHA1-hashing the +/// values of the compact credentials exported by mod_gridsite +{ + unsigned char hash_delegation_id[EVP_MAX_MD_SIZE]; + int i; + unsigned int delegation_id_len; + char cred_name[14], *cred_value, *delegation_id; + const EVP_MD *m; + EVP_MD_CTX ctx; + + OpenSSL_add_all_digests(); + + m = EVP_sha1(); + if (m == NULL) return NULL; + + EVP_DigestInit(&ctx, m); + + for (i=0; i <= 999; ++i) + { + snprintf(cred_name, sizeof(cred_name), "GRST_CRED_%d", i); + if ((cred_value = getenv(cred_name)) == NULL) break; + + EVP_DigestUpdate(&ctx, cred_value, strlen(cred_value)); + } + + EVP_DigestFinal(&ctx, hash_delegation_id, &delegation_id_len); + + delegation_id = malloc(17); + + for (i=0; i <=7; ++i) + sprintf(&delegation_id[i*2], "%02x", hash_delegation_id[i]); + + delegation_id[16] = '\0'; + + return delegation_id; +} + +#if 0 +/// Return the short file name for the given delegation_id and user_dn +char *GRSTx509MakeProxyFileName(char *delegation_id, + STACK_OF(X509) *certstack) +/// +/// Returns a malloc'd string with the short file name (no paths) that +/// derived from the hashed delegation_id and user_dn +/// +/// File name is SHA1_HASH(DelegationID)+"-"+SHA1_HASH(DN) where DN +/// is DER encoded version of user_dn with any trailing CN=proxy removed +/// Hashes are the most significant 8 bytes, in lowercase hexadecimal. +{ + int i, depth, prevIsCA = 1, IsCA, hash_name_len, delegation_id_len, + der_name_len; + unsigned char *der_name, *buf, hash_name[EVP_MAX_MD_SIZE], + hash_delegation_id[EVP_MAX_MD_SIZE], + filename[34]; + X509_NAME *subject_name; + X509 *cert; + const EVP_MD *m; + EVP_MD_CTX ctx; + + depth = sk_X509_num(certstack); + + for (i=depth-1; i >= 0; --i) + /* loop through the proxy chain starting at CA end */ + { + if (cert = sk_X509_value(certstack, i)) + { + IsCA = (GRSTx509IsCA(cert) == GRST_RET_OK); + + if (prevIsCA && !IsCA) /* the full certificate of the user */ + { + break; + } + } + } + + if (i < 0) return NULL; /* not found: something wrong with the chain */ + + if ((subject_name = X509_get_subject_name(cert)) == NULL) return NULL; + + der_name_len = i2d_X509_NAME(X509_get_subject_name(cert), NULL); + if (der_name_len == 0) return NULL; + + buf = OPENSSL_malloc(der_name_len); + der_name = buf; + + + if (!i2d_X509_NAME(X509_get_subject_name(cert), &der_name)) + { + OPENSSL_free(der_name); + return NULL; + } + + OpenSSL_add_all_digests(); + + m = EVP_sha1(); + if (m == NULL) + { + OPENSSL_free(der_name); + return NULL; + } + + + EVP_DigestInit(&ctx, m); + EVP_DigestUpdate(&ctx, delegation_id, strlen(delegation_id)); + EVP_DigestFinal(&ctx, hash_delegation_id, &delegation_id_len); + + /* lots of nasty hard coded numbers: + "8bytes/16chars delegation ID" + "-" + "8bytes/16chars DN" */ + + for (i=0; i <=7; ++i) + sprintf(&filename[i*2], "%02x", hash_delegation_id[i]); + + filename[16] = '-'; + + EVP_DigestInit(&ctx, m); + EVP_DigestUpdate(&ctx, buf, der_name_len); + EVP_DigestFinal(&ctx, hash_name, &hash_name_len); + + for (i=0; i <=7; ++i) + sprintf(&filename[17 + i*2], "%02x", hash_name[i]); + + return strdup(filename); +} +#endif + +/// Store a GSI proxy chain in the proxy cache, along with the private key +int GRSTx509CacheProxy(char *proxydir, char *delegation_id, + char *user_dn, char *proxychain) +/// +/// Returns GRST_RET_OK on success, non-zero otherwise. The existing +/// private key with the same delegation ID and user DN is moved out of +/// the temporary cache. +{ + int c, i; + char *user_dn_enc, *ptr, *prvkeyfile, proxyfile[16384]; + STACK_OF(X509) *certstack; + BIO *certmem; + X509 *cert; + long ptrlen; + FILE *ifp, *ofp; + + if (strcmp(user_dn, "cache") == 0) return GRST_RET_FAILED; + + /* find the existing private key file */ + + prvkeyfile = GRSTx509CachedProxyKeyFind(proxydir, delegation_id, user_dn); + + if (prvkeyfile == NULL) + { + return GRST_RET_FAILED; + } + + /* open it ready for later */ + + if ((ifp = fopen(prvkeyfile, "r")) == NULL) + { + free(prvkeyfile); + return GRST_RET_FAILED; + } + + /* get the X509 stack */ + + if (GRSTx509StringToChain(&certstack, proxychain) != GRST_RET_OK) + { + fclose(ifp); + free(prvkeyfile); + return GRST_RET_FAILED; + } + + /* create directories if necessary, and set proxy filename */ + + user_dn_enc = GRSThttpUrlEncode(user_dn); + + mkdir_printf(S_IRUSR | S_IWUSR | S_IXUSR, + "%s/%s", proxydir, user_dn_enc); + mkdir_printf(S_IRUSR | S_IWUSR | S_IXUSR, + "%s/%s/%s", proxydir, user_dn_enc, delegation_id); + + sprintf(proxyfile, "%s/%s/%s/userproxy.pem", + proxydir, user_dn_enc, delegation_id); + + free(user_dn_enc); + + /* set up to write proxy file */ + + ofp = fopen(proxyfile, "w"); + chmod(proxyfile, S_IRUSR | S_IWUSR); + + if (ofp == NULL) + { + fclose(ifp); + free(prvkeyfile); + return GRST_RET_FAILED; + } + + /* write out the most recent proxy by itself */ + + if ( (cert = sk_X509_value(certstack, 0)) ) + { + certmem = BIO_new(BIO_s_mem()); + if (PEM_write_bio_X509(certmem, cert) == 1) + { + ptrlen = BIO_get_mem_data(certmem, &ptr); + fwrite(ptr, 1, ptrlen, ofp); + } + + BIO_free(certmem); + } + + /* insert proxy private key, read from private key file */ + + while ((c = fgetc(ifp)) != EOF) fputc(c, ofp); + unlink(prvkeyfile); + free(prvkeyfile); + + for (i=1; i <= sk_X509_num(certstack) - 1; ++i) + /* loop through the proxy chain starting at 2nd most recent proxy */ + { + if ( (cert = sk_X509_value(certstack, i))) + { + certmem = BIO_new(BIO_s_mem()); + if (PEM_write_bio_X509(certmem, cert) == 1) + { + ptrlen = BIO_get_mem_data(certmem, &ptr); + fwrite(ptr, 1, ptrlen, ofp); + } + + BIO_free(certmem); + } + } + + sk_X509_free(certstack); + + if (fclose(ifp) != 0) return GRST_RET_FAILED; + if (fclose(ofp) != 0) return GRST_RET_FAILED; + + return GRST_RET_OK; +} diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_xacml.c b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_xacml.c new file mode 100644 index 0000000000000000000000000000000000000000..89fc50142c90363a1cb8573730f1d538c404e470 --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdSecssl/libsslGridSite/grst_xacml.c @@ -0,0 +1,565 @@ +/* + Andrew McNab and Shiv Kaushal, University of Manchester. + Copyright (c) 2002-3. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, are permitted provided that the following + conditions are met: + + o Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + o Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/*------------------------------------------------------------------------* + * For more information about GridSite: http://www.gridpp.ac.uk/gridsite/ * + *------------------------------------------------------------------------*/ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <string.h> +#include <dirent.h> +#include <ctype.h> + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include <fnmatch.h> + +#include <libxml/xmlmemory.h> +#include <libxml/tree.h> +#include <libxml/parser.h> + +#include "gridsite.h" + +//#define XACML_DEBUG + +#ifdef XACML_DEBUG + #define XACML_DEBUG_FILE "/tmp/grstxacmldebug.out" +#endif + + +/* * + * Global variables, shared by all GACL functions by private to libgacl * + * */ + +extern char *grst_perm_syms[]; +extern GRSTgaclPerm grst_perm_vals[]; + + +FILE* debugfile; + +GRSTgaclAcl *GRSTgaclAclParse(xmlDocPtr, xmlNodePtr, GRSTgaclAcl *); +GRSTgaclAcl *GRSTxacmlAclParse(xmlDocPtr, xmlNodePtr, GRSTgaclAcl *); +int GRSTxacmlPermPrint(GRSTgaclPerm perm, FILE *fp); + +/* * + * Functions to read in XACML 1.1 compliant format ACL * + * Functions based on method for opening GACL format * + * */ + +// need to check these for libxml memory leaks? - what needs to be freed? + + +static GRSTgaclCred *GRSTxacmlCredParse(xmlNodePtr cur) +/* + GRSTxacmlCredParse - parse a credential stored in the libxml structure cur, + returning it as a pointer or NULL on error. +*/ +{ + xmlNodePtr attr_val; + xmlNodePtr attr_des; + GRSTgaclCred *cred; + + // cur points to <Subject> or <AnySubjects/>, loop done outside this function. + + if ( (xmlStrcmp(cur->name, (const xmlChar *) "AnySubject") == 0)) cred = GRSTgaclCredNew("any-user"); + + else{ + + attr_val=cur->xmlChildrenNode->xmlChildrenNode; + attr_des=attr_val->next; + + cred = GRSTgaclCredNew((char *) xmlNodeGetContent(attr_des->properties->children)); + + cred->next = NULL; + + //Assumed that there is only one name/value pair per credential + GRSTgaclCredAddValue(cred, (char *) xmlNodeGetContent(attr_des->properties->next->children), + (char *) xmlNodeGetContent(attr_val)); + } + + return cred; +} + +static GRSTgaclEntry *GRSTxacmlEntryParse(xmlNodePtr cur) +/* + GRSTxacmlEntryParse - parse an entry stored in the libxml structure cur, + returning it as a pointer or NULL on error. Also checks to see if the following + <Rule> tag refers to the same <Target> by checking the <RuleId> of both +*/ +{ + int i, check=0; + xmlNodePtr cur2; + xmlNodePtr rule_root=cur; + GRSTgaclEntry *entry; + GRSTgaclCred *cred; + + + // Next line not needed as function only called if <Rule> tag found + // if (xmlStrcmp(cur->name, (const xmlChar *) "Rule") != 0) return NULL; + // cur and rule_root point to the <Rule> tag + + cur = cur->xmlChildrenNode->xmlChildrenNode; + // cur should now be pointing at <Subjects> tag +#ifdef XACML_DEBUG + fprintf (debugfile, "Starting to Parse Entry\n"); +#endif + entry = GRSTgaclEntryNew(); + + while (cur!=NULL){ + + if (xmlStrcmp(cur->name, (const xmlChar *) "Subjects") == 0){ +#ifdef XACML_DEBUG + fprintf (debugfile, "Starting to Parse Credentials\n"); +#endif + if (check==0){ + // cur still pointing at <Subjects> tag make cur2 point to <Subject> and loop over them. + cur2=cur->xmlChildrenNode; + while (cur2!=NULL){ + if ( ((cred = GRSTxacmlCredParse(cur2)) != NULL) && (!GRSTgaclEntryAddCred(entry, cred))){ + GRSTgaclCredFree(cred); + GRSTgaclEntryFree(entry); + return NULL; + } + cur2=cur2->next; + } + } + } + + else if (xmlStrcmp(cur->name, (const xmlChar *) "Actions") == 0){ +#ifdef XACML_DEBUG + fprintf (debugfile, "Starting to Parse Permissions\n"); +#endif + if (xmlStrcmp(xmlNodeGetContent(rule_root->properties->next->children), (const xmlChar *) "Permit") == 0 ){ +#ifdef XACML_DEBUG + fprintf (debugfile, "\tPermit-ed actions: "); +#endif + for (cur2 = cur->xmlChildrenNode; cur2 != NULL; cur2=cur2->next) //cur2-><Action> + for (i=0; grst_perm_syms[i] != NULL; ++i) + if (xmlStrcmp(xmlNodeGetContent(cur2->xmlChildrenNode->xmlChildrenNode), (const xmlChar *) grst_perm_syms[i]) == 0) + { +#ifdef XACML_DEBUG + fprintf (debugfile, "%s ", grst_perm_syms[i]); +#endif + GRSTgaclEntryAllowPerm(entry, grst_perm_vals[i]); + } + } + + if (xmlStrcmp(xmlNodeGetContent(rule_root->properties->next->children), (const xmlChar *) "Deny") == 0 ) { +#ifdef XACML_DEBUG + fprintf (debugfile, "\tDeny-ed actions: "); +#endif + for (cur2 = cur->xmlChildrenNode; cur2 != NULL; cur2=cur2->next) //cur2-><Action> + for (i=0; grst_perm_syms[i] != NULL; ++i) + if (xmlStrcmp(xmlNodeGetContent(cur2->xmlChildrenNode->xmlChildrenNode), (const xmlChar *) grst_perm_syms[i]) == 0) + { + +#ifdef XACML_DEBUG + fprintf (debugfile, "%s ", grst_perm_syms[i]); +#endif + GRSTgaclEntryDenyPerm(entry, grst_perm_vals[i]); + } + } + + } + else{ // I cannot parse this - give up rather than get it wrong +#ifdef XACML_DEBUG + fprintf (debugfile, "OOOPSIE\n"); +#endif + GRSTgaclEntryFree(entry); + return NULL; + } + + cur=cur->next; + + // Check if next Rule should be included when end of current rule reached + // If RuleId are from the same entry (eg Entry1A and Entry1D) + // make cur point to the next Rule's <Subjects> tag + if (cur==NULL) + if (check==0) + if (rule_root->next!=NULL) + if ( strncmp((const char*)xmlNodeGetContent(rule_root->properties->children), // RuleId of this Rule + (const char*)xmlNodeGetContent(rule_root->next->properties->children), // RuleId of next Rule + 6) == 0){ +#ifdef XACML_DEBUG + fprintf (debugfile, "End of perms and creds, next is %s \n", xmlNodeGetContent(rule_root->next->properties->children)); +#endif + rule_root=rule_root->next; + cur=rule_root->xmlChildrenNode->xmlChildrenNode; +#ifdef XACML_DEBUG + fprintf (debugfile, "skipped to <%s> tag of next Rule\n", cur->name); +#endif + check++; + } + } + + return entry; +} + +GRSTgaclAcl *GRSTxacmlAclLoadFile(char *filename) +{ + xmlDocPtr doc; + xmlNodePtr cur; + GRSTgaclAcl *acl=NULL; + + doc = xmlParseFile(filename); + if (doc == NULL) return NULL; + + cur = xmlDocGetRootElement(doc); + if (cur == NULL) return NULL; + + if (!xmlStrcmp(cur->name, (const xmlChar *) "Policy")) { acl=GRSTxacmlAclParse(doc, cur, acl);} + else if (!xmlStrcmp(cur->name, (const xmlChar *) "gacl")) {acl=GRSTgaclAclParse(doc, cur, acl);} + else /* ACL format not recognised */ + { + xmlFreeDoc(doc); + free(cur); + return NULL; + } + + xmlFreeDoc(doc); + return acl; +} + +GRSTgaclAcl *GRSTxacmlAclParse(xmlDocPtr doc, xmlNodePtr cur, GRSTgaclAcl *acl) +{ + GRSTgaclEntry *entry; + + #ifdef XACML_DEBUG + debugfile=fopen(XACML_DEBUG_FILE, "w"); + fprintf (debugfile, "ACL loaded..\n"); + fprintf (debugfile, "Parsing XACML\n"); + #endif + + // Have an XACML policy file. + // Skip <Target> tag and set cur to first <Rule> tag + cur = cur->xmlChildrenNode->next; + + acl = GRSTgaclAclNew(); + + while (cur != NULL){ + + if ( !xmlStrcmp(cur->name, (const xmlChar *)"Rule") ) + { // IF statement not needed? + #ifdef XACML_DEBUG + fprintf (debugfile, "Rule %s found\n", xmlNodeGetContent(cur->properties->children) ); + fprintf (debugfile, "Parsing Entry for this rule\n"); + #endif + entry = GRSTxacmlEntryParse(cur); + + if (entry == NULL) + { + GRSTgaclAclFree(acl); + return NULL; + } + else GRSTgaclAclAddEntry(acl, entry); + + #ifdef XACML_DEBUG + fprintf (debugfile, "Entry read in\n\n"); + #endif + } + + // If the current and next Rules are part of the same entry then advance two Rules + // If not then advance 1 + if (cur->next != NULL) + { + if ( strncmp((const char*)xmlNodeGetContent(cur->properties->children), // RuleId of this Rule + (const char*)xmlNodeGetContent(cur->next->properties->children), // RuleId of next Rule + 6) == 0) + { + #ifdef XACML_DEBUG + fprintf (debugfile, "skipping next rule %s, should have been caught previously\n\n", xmlNodeGetContent(cur->next->properties->children) ); + #endif + cur=cur->next; + } // Check first 6 characters i.e. Entry1**/ + } + + cur=cur->next; + + } + + #ifdef XACML_DEBUG + fprintf (debugfile, "Finished loading ACL - Fanfare!\n"); + fclose(debugfile); + #endif + + return acl; +} + + +int GRSTxacmlFileIsAcl(char *pathandfile) +/* Return 1 if filename in *pathandfile starts GRST_ACL_FILE + Return 0 otherwise. */ +{ + char *filename; + + filename = rindex(pathandfile, '/'); + if (filename == NULL) filename = pathandfile; + else filename++; + + return (strncmp((const char*)filename, GRST_ACL_FILE, sizeof(GRST_ACL_FILE) - 1) == 0); +} + +char *GRSTxacmlFileFindAclname(char *pathandfile) +/* Return malloc()ed ACL filename that governs the given file or directory + (for directories, the ACL file is in the directory itself), or NULL if none + can be found. */ +{ + char *path, *p; + struct stat statbuf; + + path = malloc(strlen(pathandfile) + sizeof(GRST_ACL_FILE) + 1); + strcpy(path, pathandfile); + + if (stat(path, &statbuf) == 0) + { + if (!S_ISDIR(statbuf.st_mode)) /* can strip this / off straightaway */ + { + p = rindex(path, '/'); + if (p != NULL) *p = '\0'; + } + } + + while (path[0] != '\0') + { + strcat(path, "/"); + strcat(path, GRST_ACL_FILE); + + if (stat(path, &statbuf) == 0) return path; + + p = rindex(path, '/'); + *p = '\0'; /* strip off the / we added for ACL */ + + p = rindex(path, '/'); + if (p == NULL) break; /* must start without / and we there now ??? */ + + *p = '\0'; /* strip off another layer of / */ + } + + free(path); + return NULL; +} + +GRSTgaclAcl *GRSTxacmlAclLoadforFile(char *pathandfile) +/* Return ACL that governs the given file or directory (for directories, + the ACL file is in the directory itself.) */ +{ + char *path; + GRSTgaclAcl *acl; + + path = GRSTxacmlFileFindAclname(pathandfile); + + if (path != NULL) + { + acl = GRSTxacmlAclLoadFile(path); + free(path); + return acl; + } + + return NULL; +} + + + +/* * + * Functions to save ACL in XACML 1.1 compliant format * + * Functions based on method for saving to GACL format * + * */ + + +int GRSTxacmlCredPrint(GRSTgaclCred *cred, FILE *fp) +/* + GRSTxacmlCredPrint - print a credential and any name-value pairs is contains in XACML form +*/ +{ + char *q; + + if (cred->auri != NULL) + { + fputs("\t\t\t\t<Subject>\n", fp); + fputs("\t\t\t\t\t<SubjectMatch MatchId=\"urn:oasis:names:tc:xacml:1.0:function:string-equal\">\n", fp); + fputs("\t\t\t\t\t\t<AttributeValue DataType=\"http://www.w3.org/2001/XMLSchema#string\">", fp); + for (q=cred->auri; *q != '\0'; ++q) + if (*q == '<') fputs("<", fp); + else if (*q == '>') fputs(">", fp); + else if (*q == '&') fputs("&" , fp); + else if (*q == '\'') fputs("'", fp); + else if (*q == '"') fputs(""", fp); + else fputc(*q, fp); + + + fputs("</AttributeValue>\n", fp); + + fputs("\t\t\t\t\t\t<SubjectAttributeDesignator\n", fp); + fputs("\t\t\t\t\t\t\tAttributeId=", fp); + fprintf(fp, "\"cred\"\n"); + fputs("\t\t\t\t\t\t\tDataType=", fp); + fprintf(fp, "\"auri\"/>\n"); + fputs("\t\t\t\t\t</SubjectMatch>\n", fp); + fputs("\t\t\t\t</Subject>\n", fp); + } + else fputs("\t\t\t\t<AnySubject/>\n", fp); + + return 1; +} + + +int GRSTxacmlEntryPrint(GRSTgaclEntry *entry, FILE *fp, int rule_number) +{ + GRSTgaclCred *cred; + GRSTgaclPerm i; + + if (entry->allowed){ + + fprintf(fp, "\t<Rule RuleId=\"Entry%dA\" Effect=\"Permit\">\n", rule_number); + fputs("\t\t<Target>\n", fp); + fputs("\t\t\t<Subjects>\n", fp); + + for (cred = entry->firstcred; cred != NULL; cred = cred->next) + GRSTxacmlCredPrint(cred, fp); + + fputs("\t\t\t</Subjects>\n", fp); + fputs("\t\t\t<Actions>\n", fp); + + for (i=GRST_PERM_READ; i <= GRST_PERM_ADMIN; ++i) + if ((entry->allowed) & i) GRSTxacmlPermPrint(i, fp); + + fputs("\t\t\t</Actions>\n", fp); + fputs("\t\t</Target>\n", fp); + fputs("\t</Rule>\n", fp); + } + + if (entry->denied){ + + fprintf(fp, "\t<Rule RuleId=\"Entry%dD\" Effect=\"Deny\">\n", rule_number); + fputs("\t\t<Target>\n", fp); + fputs("\t\t\t<Subjects>\n", fp); + + for (cred = entry->firstcred; cred != NULL; cred = cred->next) + GRSTxacmlCredPrint(cred, fp); + + fputs("\t\t\t</Subjects>\n", fp); + fputs("\t\t\t<Actions>\n", fp); + + for (i=GRST_PERM_READ; i <= GRST_PERM_ADMIN; ++i) + if (entry->denied & i) GRSTxacmlPermPrint(i, fp); + + fputs("\t\t\t</Actions>\n", fp); + fputs("\t\t</Target>\n", fp); + fputs("\t</Rule>\n", fp); + } + return 1; +} + + +int GRSTxacmlPermPrint(GRSTgaclPerm perm, FILE *fp) +{ + GRSTgaclPerm i; + + for (i=GRST_PERM_READ; grst_perm_syms[i] != NULL; ++i) + if (perm == grst_perm_vals[i]) + { + + fputs("\t\t\t\t<Action>\n", fp); + fputs("\t\t\t\t\t<ActionMatch MatchId=\"urn:oasis:names:tc:xacml:1.0:function:string-equal\">\n", fp); + fputs("\t\t\t\t\t\t<AttributeValue DataType=\"http://www.w3.org/2001/XMLSchema#string\">", fp); + fprintf(fp, "%s", grst_perm_syms[i]); + fputs("</AttributeValue>\n", fp); + fputs("\t\t\t\t\t\t<ActionAttributeDesignator\n", fp); + fputs("\t\t\t\t\t\t\tAttributeId=\"urn:oasis:names:tc:xacml:1.0:action:action-id\"\n", fp); + fputs("\t\t\t\t\t\t\tDataType=\"http://www.w3.org/2001/XMLSchema#string\"/>\n", fp); + fputs("\t\t\t\t\t</ActionMatch>\n", fp); + fputs("\t\t\t\t</Action>\n",fp); + + return 1; + } + + return 0; +} + +int GRSTxacmlAclPrint(GRSTgaclAcl *acl, FILE *fp, char* dir_uri) +{ + GRSTgaclEntry *entry; + int rule_number=1; + + fputs("<Policy", fp); + fputs("\txmlns=\"urn:oasis:names:tc:xacml:1.0:policy\"\n", fp); + fputs("\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n", fp); + fputs("\txsi:schemaLocation=\"urn:oasis:names:tc:xacml:1.0:policy cs-xacml-schema-policy-01.xsd\"\n", fp); + fputs("\tPolicyId=\"GridSitePolicy\"\n", fp); + fputs("\tRuleCombiningAlgId=\"urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:deny-overrides\">\n\n", fp); + + fputs("\t<Target>\n\t\t<Resources>\n\t\t\t<Resource>\n", fp); + fputs("\t\t\t\t<ResourceMatch MatchId=\"urn:oasis:names:tc:xacml:1.0:function:string-equal\">\n", fp); + fputs("\t\t\t\t\t<AttributeValue DataType=\"http://www.w3.org/2001/XMLSchema#string\">", fp); + fprintf(fp, "%s", dir_uri); + fputs("</AttributeValue>\n", fp); + fputs("\t\t\t\t\t<ResourceAttributeDesignator\n", fp); + fputs("\t\t\t\t\t\tAttributeId=\"urn:oasis:names:tc:xacml:1.0:resource:resource-id\"\n", fp); + fputs("\t\t\t\t\t\tDataType=\"http://www.w3.org/2001/XMLSchema#string\"/>\n", fp); + + fputs("\t\t\t\t</ResourceMatch>\n\t\t\t</Resource>\n\t\t</Resources>\n\t\t<Subjects>\n\t\t\t<AnySubject/>\n\t\t</Subjects>", fp); + fputs("\n\t\t<Actions>\n\t\t\t<AnyAction/>\n\t\t</Actions>\n\t</Target>\n\n", fp); + + for (entry = acl->firstentry; entry != NULL; entry = entry->next){ + + GRSTxacmlEntryPrint(entry, fp, rule_number); + rule_number++; + } + + fputs("</Policy>\n", fp); + + return 1; +} + +int GRSTxacmlAclSave(GRSTgaclAcl *acl, char *filename, char* dir_uri) +{ + int ret; + FILE *fp; + + fp = fopen(filename, "w"); + if (fp == NULL) return 0; + + fprintf(fp,"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); + + ret = GRSTxacmlAclPrint(acl, fp, dir_uri); + + fclose(fp); + + return ret; +} + + + + diff --git a/net/xrootd/src/xrootd/src/XrdSecssl/xrootd-secssl.spec.in b/net/xrootd/src/xrootd/src/XrdSecssl/xrootd-secssl.spec.in new file mode 100644 index 0000000000000000000000000000000000000000..00561bacd71ac1402e2c369961688b5200f63590 --- /dev/null +++ b/net/xrootd/src/xrootd/src/XrdSecssl/xrootd-secssl.spec.in @@ -0,0 +1,38 @@ +%define _unpackaged_files_terminate_build 0 +%define __os_install_post /bin/true + +Summary: ssl plugin with VOMS support +Name: xrootd-secssl +Version: @VERSION@ +Release: 1 +Prefix: /opt/xrootd +License: none +Group: Applications/File +Source: @PACKAGE_TARNAME@-@PACKAGE_VERSION@.tar.gz +BuildRoot: %{_tmppath}/%{name}-root +AutoReqProv: no + +%description +SSL Plugin for xrootd with VOMS/GSI support and session implementation. + +%prep + +# TODO: change this explicit path +%setup -n @PACKAGE_TARNAME@-@PACKAGE_VERSION@ + +%build +./bootstrap.sh +./configure --prefix=/opt/xrootd --with-xrootd-location=/opt/xrootd +make -j 4 + + +%install +make install DESTDIR=$RPM_BUILD_ROOT + + +%files +/opt/xrootd/* + + +%clean +rm -rf $RPM_BUILD_ROOT diff --git a/net/xrootd/src/xrootd/src/XrdSut/XrdSutTrace.hh b/net/xrootd/src/xrootd/src/XrdSut/XrdSutTrace.hh index 5395304923411abe43c6002982348d4968d66705..b88fc607e1708874bd3b355a0d7430910526345e 100644 --- a/net/xrootd/src/xrootd/src/XrdSut/XrdSutTrace.hh +++ b/net/xrootd/src/xrootd/src/XrdSut/XrdSutTrace.hh @@ -26,7 +26,7 @@ cerr <<y; sutTrace->End();}} #define TRACE(act,x) if (QTRACE(act)) PRINT(x) #define DEBUG(y) TRACE(Debug,y) -#define EPNAME(x) const char *epname = x; +#define EPNAME(x) static const char *epname = x; #else diff --git a/net/xrootd/src/xrootd/src/XrdSys/XrdSysLogger.cc b/net/xrootd/src/xrootd/src/XrdSys/XrdSysLogger.cc index e96bc8b76ee6a66fe0ee0532324e63c2ea2b38ff..090e33cdc36028c94e2ae51c95f1d285384b76e7 100644 --- a/net/xrootd/src/xrootd/src/XrdSys/XrdSysLogger.cc +++ b/net/xrootd/src/xrootd/src/XrdSys/XrdSysLogger.cc @@ -158,6 +158,32 @@ int XrdSysLogger::xlogFD() {return -1;} /******************************************************************************/ /* P r i v a t e M e t h o d s */ /******************************************************************************/ +/******************************************************************************/ +/* p u t E m s g */ +/******************************************************************************/ + +// This internal logging method is used when the caller already has the mutex! + +void XrdSysLogger::putEmsg(char *msg, int msz) +{ + struct iovec eVec[2]; + int retc; + char tbuff[24]; + +// Prefix message with time +// + eVec[0].iov_base = tbuff; + eVec[0].iov_len = (int)Time(tbuff); + eVec[1].iov_base = msg; + eVec[1].iov_len = msz; + +// In theory, writev may write out a partial list. This rarely happens in +// practice and so we ignore that possibility (recovery is pretty tough). +// + do { retc = writev(eFD, (const struct iovec *)eVec, 2);} + while (retc < 0 && errno == EINTR); +} + /******************************************************************************/ /* R e B i n d */ /******************************************************************************/ @@ -219,9 +245,6 @@ int XrdSysLogger::ReBind(int dorename) /* T r i m */ /******************************************************************************/ -#define putEmsg(msg,msz) eVec[1].iov_base = msg; eVec[1].iov_len = msz; \ - eVec[0].iov_base = 0; Put(2, eVec) - #ifndef WIN32 void XrdSysLogger::Trim() { @@ -240,7 +263,6 @@ void XrdSysLogger::Trim() } logList(0,0,0); struct LogFile *logEnt, *logPrev, *logNow; - struct iovec eVec[2]; char eBuff[2048], logFN[256], logDir[1024], *logSfx; struct dirent *dp; struct stat buff; diff --git a/net/xrootd/src/xrootd/src/XrdSys/XrdSysLogger.hh b/net/xrootd/src/xrootd/src/XrdSys/XrdSysLogger.hh index bf7c302aeba433c6ef819f42ada6ec89cafdd0ba..36204fb32558f429c24ce02775c60ed1cddffd88 100644 --- a/net/xrootd/src/xrootd/src/XrdSys/XrdSysLogger.hh +++ b/net/xrootd/src/xrootd/src/XrdSys/XrdSysLogger.hh @@ -88,6 +88,7 @@ int eInt; time_t eNow; int doLFR; +void putEmsg(char *msg, int msz); int ReBind(int dorename=1); void Trim(); }; diff --git a/net/xrootd/src/xrootd/src/XrdSys/XrdSysPlatform.hh b/net/xrootd/src/xrootd/src/XrdSys/XrdSysPlatform.hh index d7722850b6124e6a63ef5cec5a31d93b049ab4af..06ebb34e771dd18ceec66d9c81073b3d87e7353c 100644 --- a/net/xrootd/src/xrootd/src/XrdSys/XrdSysPlatform.hh +++ b/net/xrootd/src/xrootd/src/XrdSys/XrdSysPlatform.hh @@ -229,4 +229,8 @@ extern "C" #define net_errno errno #endif +// The following gets arround a relative new gcc compiler bug +// +#define XRDABS(x) (x < 0 ? -x : x) + #endif // __XRDSYS_PLATFORM_H__ diff --git a/net/xrootd/src/xrootd/src/XrdSys/XrdSysPthread.hh b/net/xrootd/src/xrootd/src/XrdSys/XrdSysPthread.hh index b924a3af2e2cd8d1380f9b0f76c82d420292c3f6..f60e1a6439651bacb41ccd14d71cb6a93e0dbc48 100644 --- a/net/xrootd/src/xrootd/src/XrdSys/XrdSysPthread.hh +++ b/net/xrootd/src/xrootd/src/XrdSys/XrdSysPthread.hh @@ -23,11 +23,6 @@ #else #include <semaphore.h> #endif -#if defined(__solaris__) -#define SEM_IS_BLOCKED EBUSY -#else -#define SEM_IS_BLOCKED EAGAIN -#endif #include "XrdSys/XrdSysError.hh" @@ -237,10 +232,10 @@ class XrdSysSemaphore public: inline int CondWait() - {if (sem_trywait( &h_semaphore )) - {if (errno == SEM_IS_BLOCKED) return 0; - else { throw "sem_CondWait() failed";} - } + {while(sem_trywait( &h_semaphore )) + {if (errno == EAGAIN) return 0; + if (errno != EINTR) { throw "sem_CondWait() failed";} + } return 1; } diff --git a/net/xrootd/src/xrootd/src/XrdVersion.hh b/net/xrootd/src/xrootd/src/XrdVersion.hh index f51487090da3bb18539e8951cc1a8192950b49aa..dce338e752596394092bf1cee3a53a0f93d54ec5 100644 --- a/net/xrootd/src/xrootd/src/XrdVersion.hh +++ b/net/xrootd/src/xrootd/src/XrdVersion.hh @@ -1,7 +1,7 @@ // $Id$ #ifndef __XRD_VERSION_H__ #define __XRD_VERSION_H__ -#define XrdVERSION "20091202-0509-root-1" +#define XrdVERSION "v20100205-0000-root-1" #if XrdDEBUG #define XrdVSTRING XrdVERSION "_dbg" #else diff --git a/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdCallBack.cc b/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdCallBack.cc index abefc8b8eda89cb73b83b72ee6416499d75ef3e2..16d9fb7b7327d6db5556822686a8ac5d7aa1ab76 100644 --- a/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdCallBack.cc +++ b/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdCallBack.cc @@ -41,7 +41,7 @@ static XrdXrootdCBJob *Alloc(XrdXrootdCallBack *cbF, XrdOucErrInfo *erp, int rva void DoIt(); -inline void Recycle(){myMutex.Lock(); // eInfo is deleted elsewhere +inline void Recycle(){myMutex.Lock(); Next = FreeJob; FreeJob = this; myMutex.UnLock(); @@ -125,9 +125,10 @@ void XrdXrootdCBJob::DoIt() } } else cbFunc->sendError(Result, eInfo); -// Recycle ourselves before returning +// Tell the requestor that the callback has completed // - delete eInfo; + if (eInfo->getErrCB()) eInfo->getErrCB()->Done(Result, eInfo); + else delete eInfo; eInfo = 0; Recycle(); } @@ -182,7 +183,8 @@ void XrdXrootdCallBack::Done(int &Result, //I/O: Function result // if (!(cbj = XrdXrootdCBJob::Alloc(this, eInfo, Result))) {eDest->Emsg("Done",ENOMEM,"get call back job; user",eInfo->getErrUser()); - delete eInfo; + if (eInfo->getErrCB()) eInfo->getErrCB()->Done(Result, eInfo); + else delete eInfo; } else Sched->Schedule((XrdJob *)cbj); } @@ -277,7 +279,6 @@ void XrdXrootdCallBack::sendResp(XrdOucErrInfo *eInfo, int ovhd) { const char *TraceID = "sendResp"; - unsigned long long Dest; struct iovec rspVec[4]; XrdXrootdReqID ReqID; int dlen = 0, n = 1; @@ -292,10 +293,10 @@ void XrdXrootdCallBack::sendResp(XrdOucErrInfo *eInfo, { rspVec[n].iov_base = (caddr_t)Msg; dlen += rspVec[n].iov_len = strlen(Msg)+ovhd; n++; // 2 } -// Decode the destination + +// Set the destination // - eInfo->getErrCB(Dest); - ReqID.setID(Dest); + ReqID.setID(eInfo->getErrArg()); // Send the async response // diff --git a/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdConfig.cc b/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdConfig.cc index 3ae9705d9a99ea9452557e8965afa702830b949f..f113b1dd6b5d7119b0c404b459018cd5e96542ad 100644 --- a/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdConfig.cc +++ b/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdConfig.cc @@ -178,10 +178,11 @@ int XrdXrootdProtocol::Configure(char *parms, XrdProtocol_Config *pi) // for ( ; optind < pi->argc; optind++) xexpdo(pi->argv[optind]); -// Pre-initialize some i/o values +// Pre-initialize some i/o values. Note that we now set maximum readv element +// transfer size to the buffer size (before it was a reasonable 256K). // if (!(as_miniosz = as_segsize/2)) as_miniosz = as_segsize; - maxBuffsz = BPool->MaxSize(); + maxTransz = maxBuffsz = BPool->MaxSize(); // Now process and configuration parameters // diff --git a/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdProtocol.hh b/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdProtocol.hh index b4aee676f8876990d8e991cc65486f428c73e8cc..b9d8243f6ca32b303fbe46a5a274394b0d8d1c6f 100644 --- a/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdProtocol.hh +++ b/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdProtocol.hh @@ -29,9 +29,9 @@ /* D e f i n e s */ /******************************************************************************/ -#define XROOTD_VERSBIN 0x00000290 +#define XROOTD_VERSBIN 0x00000291 -#define XROOTD_VERSION "2.9.0" +#define XROOTD_VERSION "2.9.1" #define ROOTD_PQ 2012 diff --git a/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdXeq.cc b/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdXeq.cc index 168418841795e3e4cfd1890699973e6b81de23f0..2202abf9f9f815f237c720daeb0b632fbe46c556 100644 --- a/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdXeq.cc +++ b/net/xrootd/src/xrootd/src/XrdXrootd/XrdXrootdXeq.cc @@ -512,7 +512,7 @@ int XrdXrootdProtocol::do_Locate() static XrdXrootdCallBack locCB("locate"); int rc, opts, fsctl_cmd = SFS_FSCTL_LOCATE; const char *opaque; - char *fn = argp->buff, opt[8], *op=opt; + char *Path, *fn = argp->buff, opt[8], *op=opt; XrdOucErrInfo myError(Link->ID, &locCB, ReqID.getID()); // Unmarshall the data @@ -532,10 +532,21 @@ int XrdXrootdProtocol::do_Locate() return Response.Send(kXR_redirect, Route[RD_locate].Port, Route[RD_locate].Host); +// Check if this is a non-specific locate +// + if (*fn != '*') Path = fn; + else if (*(fn+1)) Path = fn+1; + else {Path = 0; + fn = XPList.Next()->Path(); + fsctl_cmd |= SFS_O_TRUNC; + } + // Prescreen the path // - if (rpCheck(fn, &opaque)) return rpEmsg("Locating", fn); - if (!Squash(fn)) return vpEmsg("Locating", fn); + if (Path) + {if (rpCheck(Path, &opaque)) return rpEmsg("Locating", Path); + if (!Squash(Path)) return vpEmsg("Locating", Path); + } // Preform the actual function // @@ -2226,14 +2237,16 @@ int XrdXrootdProtocol::fsError(int rc, XrdOucErrInfo &myError) } // Process the deferal. We also synchronize sending the deferal response with -// sending the actual defered response as these can violate time causality. +// sending the actual defered response by calling Done() in the callback object. +// This allows the requestor of he callback know that we actually send the +// kXR_waitresp to the end client and avoid violating time causality. // if (rc == SFS_STARTED) {SI->stallCnt++; if (ecode <= 0) ecode = 1800; TRACEI(STALL, Response.ID() <<"delaying client up to " <<ecode <<" sec"); rc = Response.Send(kXR_waitresp, ecode, eMsg); - myError.getErrCB()->Done(ecode, &myError); + if (myError.getErrCB()) myError.getErrCB()->Done(ecode, &myError); return (rc ? rc : 1); } diff --git a/net/xrootd/src/xrootd/utils/getCRLcert b/net/xrootd/src/xrootd/utils/getCRLcert index df9cfbd743747c8c084c1d67e21a11f7cb115727..a0d51ee23f59bf1db08420e38f1a4f8b19b43907 100755 --- a/net/xrootd/src/xrootd/utils/getCRLcert +++ b/net/xrootd/src/xrootd/utils/getCRLcert @@ -1,4 +1,3 @@ -<<<<<<< .working #! /bin/sh # $Id: getCRLcert,v 1.1 2009/05/06 15:31:45 ganis Exp $ diff --git a/net/xrootd/src/xrootd/utils/testXrdSecssl b/net/xrootd/src/xrootd/utils/testXrdSecssl new file mode 100755 index 0000000000000000000000000000000000000000..16fc5972d753d4b6910e17c0a8c9184c3099d39b --- /dev/null +++ b/net/xrootd/src/xrootd/utils/testXrdSecssl @@ -0,0 +1,41 @@ +# +# Test script for the secssl built with the classic build. +# Run it like this in a bash shell: +# +# source utils/testXrdSecssl +# +# NB: 1) If not bash, start a bash shell first: $ bash +# 2) You need to include <xrddir>/bin in PATH and <xrddir>/lib +# in LD_LIBRARY_PATH first +# +# For the autotools build, the test can be run in the following way: +# +# cd src/XrdSecssl +# make test +# + +BINEXE="xrdsecssltest" + +if test -d "/etc/grid-security/certificates" ; then + if test -e "/etc/grid-security/hostcert.pem" && test -e "/etc/grid-security/hostkey.pem" ; then + if test -e "/tmp/x509up_u$UID" ; then + # Start the test + echo "Starting Server ..." + $BINEXE server >& /tmp/xrdsecssltest-server.log & + echo "Starting Client ..." + sleep 1 + $BINEXE client 2> /tmp/xrdsecssltest-client.log + # Cleanup processes + /usr/bin/killall -9 `basename $BINEXE` >& /dev/null + sleep 1 + echo "Client and Server stopped!" + else + echo "Error: you need /tmp/x509up_u$UID";\ + fi + else + echo "Error: you need /etc/grid-security/hostcert.pem and /etc/grid-security/hostkey.pem";\ + fi +else + echo "Error: you need /etc/grid-security/certificates" +fi +