@@ -387,41 +387,93 @@ func checkPluginExists(pluginName string, includeClientPaths bool) bool {
387387 return false
388388}
389389
390+ // pluginPackageMap maps plugin base names (without extension) to the RPM package that provides them.
391+ // These were determined from an OSG EL9 environment; names may differ in other distros but
392+ // should be correct for the primary Pelican deployment targets.
393+ var pluginPackageMap = map [string ]string {
394+ "libXrdHttpPelican" : "xrdhttp-pelican" ,
395+ "libXrdS3" : "xrootd-s3-http" ,
396+ "libXrdClPelican" : "xrdcl-pelican" ,
397+ "libXrdThrottle" : "xrootd-server-libs" ,
398+ "libXrdMacaroons" : "xrootd-server-libs" ,
399+ "libXrdMultiuser" : "xrootd-multiuser" ,
400+ "libXrdOssPosc" : "xrootd-s3-http" ,
401+ "libXrdHTTPServer" : "xrootd-s3-http" ,
402+ "libXrdOssGlobus" : "xrootd-s3-http" ,
403+ "libXrdPurgeLotMan" : "xrootd-lotman" ,
404+ }
405+
406+ // pluginBaseName strips the extension from a plugin filename (e.g. "libXrdS3.so" -> "libXrdS3").
407+ func pluginBaseName (name string ) string {
408+ for _ , ext := range []string {".so" , ".dylib" } {
409+ name = strings .TrimSuffix (name , ext )
410+ }
411+ return name
412+ }
413+
414+ // checkAndAppendMissing checks if a plugin exists and appends it to the missing list if not found.
415+ func checkAndAppendMissing (missingPlugins * []string , pluginName string , includeClientPaths bool ) {
416+ if ! checkPluginExists (pluginName , includeClientPaths ) {
417+ * missingPlugins = append (* missingPlugins , pluginName )
418+ }
419+ }
420+
390421// ValidateRequiredPlugins checks for the existence of required XRootD plugins based on the configuration.
391422// It returns an error with a detailed message if any required plugin is missing.
392423func ValidateRequiredPlugins (isOrigin bool , xrdConfig * XrootdConfig ) error {
393424 missingPlugins := []string {}
394425
395426 if isOrigin {
396- // Check for libXrdHttpPelican if drop privileges is enabled
427+ // libXrdHttpPelican is required when drop privileges is enabled
397428 if xrdConfig .Server .DropPrivileges {
398- if ! checkPluginExists ("libXrdHttpPelican.so" , false ) {
399- missingPlugins = append (missingPlugins , "libXrdHttpPelican.so" )
400- }
429+ checkAndAppendMissing (& missingPlugins , "libXrdHttpPelican.so" , false )
430+ }
431+
432+ // libXrdMacaroons is needed when macaroons are enabled
433+ if xrdConfig .Origin .EnableMacaroons {
434+ checkAndAppendMissing (& missingPlugins , "libXrdMacaroons.so" , false )
435+ }
436+
437+ // libXrdThrottle is needed when concurrency is configured
438+ if xrdConfig .Origin .Concurrency > 0 {
439+ checkAndAppendMissing (& missingPlugins , "libXrdThrottle.so" , false )
401440 }
402441
403- // Check for libXrdS3 if using S3 storage type
404- if xrdConfig .Origin .StorageType == "s3" {
405- if ! checkPluginExists ("libXrdS3.so" , false ) {
406- missingPlugins = append (missingPlugins , "libXrdS3.so" )
442+ switch xrdConfig .Origin .StorageType {
443+ case "posix" :
444+ // libXrdMultiuser is needed for multiuser mode
445+ if xrdConfig .Origin .Multiuser {
446+ checkAndAppendMissing (& missingPlugins , "libXrdMultiuser.so" , false )
407447 }
448+ // libXrdOssPosc is needed for atomic uploads
449+ if xrdConfig .Origin .EnableAtomicUploads {
450+ checkAndAppendMissing (& missingPlugins , "libXrdOssPosc.so" , false )
451+ }
452+ case "s3" :
453+ checkAndAppendMissing (& missingPlugins , "libXrdS3.so" , false )
454+ case "https" :
455+ checkAndAppendMissing (& missingPlugins , "libXrdHTTPServer.so" , false )
456+ case "globus" :
457+ checkAndAppendMissing (& missingPlugins , "libXrdHTTPServer.so" , false )
458+ checkAndAppendMissing (& missingPlugins , "libXrdOssGlobus.so" , false )
408459 }
409460 } else {
410461 // Cache-specific checks
411- // Check for libXrdHttpPelican if drop privileges is enabled
412- if xrdConfig .Server .DropPrivileges {
413- if ! checkPluginExists ("libXrdHttpPelican.so" , false ) {
414- missingPlugins = append (missingPlugins , "libXrdHttpPelican.so" )
415- }
416- }
462+ // libXrdHttpPelican is always required for caches (unconditional in cache template)
463+ checkAndAppendMissing (& missingPlugins , "libXrdHttpPelican.so" , false )
417464
418- // Check for client plugin - this is needed for cache servers to support pelican:// URLs.
419- // The cache configuration writes client plugin settings to the cache-client.plugins.d directory
420- // (see CheckCacheEnv in xrootd_config.go), which configures XRootD to use libXrdClPelican.so
421- // for handling pelican:// protocol requests.
465+ // libXrdClPelican is needed for cache servers to support pelican:// URLs.
422466 // Include client plugin configuration paths for this check.
423- if ! checkPluginExists ("libXrdClPelican.so" , true ) {
424- missingPlugins = append (missingPlugins , "libXrdClPelican.so" )
467+ checkAndAppendMissing (& missingPlugins , "libXrdClPelican.so" , true )
468+
469+ // libXrdPurgeLotMan is needed when Lotman is enabled
470+ if xrdConfig .Cache .LotmanCfg .Enabled {
471+ checkAndAppendMissing (& missingPlugins , "libXrdPurgeLotMan.so" , false )
472+ }
473+
474+ // libXrdThrottle is needed when concurrency is configured
475+ if xrdConfig .Cache .Concurrency > 0 {
476+ checkAndAppendMissing (& missingPlugins , "libXrdThrottle.so" , false )
425477 }
426478 }
427479
@@ -431,14 +483,34 @@ func ValidateRequiredPlugins(isOrigin bool, xrdConfig *XrootdConfig) error {
431483 if runtime .GOOS == "darwin" {
432484 envVars = "DYLD_LIBRARY_PATH, or DYLD_FALLBACK_LIBRARY_PATH"
433485 }
486+
487+ // Build per-plugin install advice, deduplicating packages
488+ seenPkgs := make (map [string ]bool )
489+ var pkgAdvice []string
490+ for _ , plugin := range missingPlugins {
491+ base := pluginBaseName (plugin )
492+ if pkg , ok := pluginPackageMap [base ]; ok && ! seenPkgs [pkg ] {
493+ seenPkgs [pkg ] = true
494+ pkgAdvice = append (pkgAdvice , pkg )
495+ }
496+ }
497+
498+ var installHint string
499+ if len (pkgAdvice ) > 0 {
500+ installHint = "\n The missing plugin(s) are provided by the following package(s): " +
501+ strings .Join (pkgAdvice , ", " )
502+ }
503+
434504 return errors .Errorf (
435505 "Required XRootD plugin(s) not found: %s\n " +
436506 "Please install the missing plugin(s) in one of the following directories:\n %s\n " +
437- "Or set the %s environment variable to include the plugin location.\n " +
438- "Note: XRootD may add a version suffix to plugin names (e.g., libXrdHttpPelican-5.so for XRootD 5.x)" ,
507+ "Or set the %s environment variable to include the plugin location.%s\n " +
508+ "When searching on disk, XRootD version suffixes are automatically added to the filename\n " +
509+ "(e.g., libXrdHttpPelican.so in config is expected to be libXrdHttpPelican-5.so on disk)" ,
439510 strings .Join (missingPlugins , ", " ),
440511 strings .Join (searchPaths , "\n " ),
441512 envVars ,
513+ installHint ,
442514 )
443515 }
444516
0 commit comments