-
Tim Wickberg authored
These have been awaiting an implementation since 2003.
Tim Wickberg authoredThese have been awaiting an implementation since 2003.
plugrack.c 15.61 KiB
/*****************************************************************************\
* plugrack.c - an intelligent container for plugins
*****************************************************************************
* Copyright (C) 2002-2007 The Regents of the University of California.
* Copyright (C) 2008-2009 Lawrence Livermore National Security.
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
* Written by Jay Windley <jwindley@lnxi.com>.
* CODE-OCEC-09-009. All rights reserved.
*
* This file is part of Slurm, a resource management program.
* For details, see <https://slurm.schedmd.com/>.
* Please also read the included file: DISCLAIMER.
*
* Slurm is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two. You must obey the GNU
* General Public License in all respects for all of the code used other than
* OpenSSL. If you modify file(s) with this exception, you may extend this
* exception to your version of the file(s), but you are not obligated to do
* so. If you do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source files in
* the program, then also delete it here.
*
* Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along
* with Slurm; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\*****************************************************************************/
#include <dirent.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "src/common/macros.h"
#include "src/common/xassert.h"
#include "src/common/xmalloc.h"
#include "src/common/xstring.h"
#include "src/common/plugrack.h"
strong_alias(plugrack_create, slurm_plugrack_create);
strong_alias(plugrack_destroy, slurm_plugrack_destroy);
strong_alias(plugrack_read_dir, slurm_plugrack_read_dir);
strong_alias(plugrack_set_major_type, slurm_plugrack_set_major_type);
strong_alias(plugrack_set_paranoia, slurm_plugrack_set_paranoia);
strong_alias(plugrack_use_by_type, slurm_plugrack_use_by_type);
/*
* Represents a plugin in the rack.
*
* full_type is the fully-qualified plugin type, e.g., "auth/kerberos".
* For the low-level plugin interface the type can be whatever it needs
* to be. For the rack-level interface, the type exported by the plugin
* must be of the form "<major>/<minor>".
*
* fq_path is the fully-qualified pathname to the plugin.
*
* plug is the plugin handle. If it is equal to PLUGIN_INVALID_HANDLE
* then the plugin is not currently loaded in memory.
*
* refcount shows how many clients have requested to use the plugin.
* If this is zero, the rack code may decide to unload the plugin.
*/
typedef struct _plugrack_entry {
const char *full_type;
const char *fq_path;
plugin_handle_t plug;
int refcount;
} plugrack_entry_t;
/*
* Implementation of the plugin rack.
*
* entries is the list of plugrack_entry_t.
*
* uid is the Linux UID of the person authorized to own the plugin
* and write to the plugin file and the directory where it is stored.
* This field is used only if paranoia is nonzero.
*
* paranoia is a set of bit flags indicating what operations should be
* done to verify the integrity and authority of the plugin before
* loading it.
*/
struct _plugrack {
List entries;
const char *major_type;
uid_t uid;
uint8_t paranoia;
};
#define PLUGRACK_UID_NOBODY 99 /* RedHat's, anyway. */
static bool _match_major ( const char *path_name, const char *major_type );
static int _plugrack_read_single_dir( plugrack_t rack, char *dir );
static bool _so_file( char *pathname );
/*
* Destructor function for the List code. This should entirely
* clean up a plugin_entry_t.
*/
static void
plugrack_entry_destructor( void *v )
{
plugrack_entry_t *victim = v;
if ( victim == NULL )
return;
/*
* Free memory and unload the plugin if necessary. The assert
* is to make sure we were actually called from the List destructor
* which should only be callable from plugrack_destroy().
*/
xassert( victim->refcount == 0 );
xfree( victim->full_type );
xfree( victim->fq_path );
if ( victim->plug != PLUGIN_INVALID_HANDLE )
plugin_unload( victim->plug );
xfree( victim );
}
/*
* Check a pathname to see if it is owned and writable by the appropriate
* users, and writable by no one else. The path can be either to a file
* or to a directory. This is so, when fishing for plugins in a whole
* directory, we can test the directory once and then each file.
*
* Returns non-zero if the file system node indicated by the path name
* is owned by the user in the plugin rack and not writable by anyone
* else, and these actions are requested by the rack's paranoia policy.
*/
static int
accept_path_paranoia( plugrack_t rack,
const char *fq_path,
int check_own,
int check_write )
{
struct stat st;
/* Internal function, so assert rather than fail gracefully. */
xassert( rack );
xassert( fq_path );
if ( stat( fq_path, &st ) < 0 ) {
debug3( "accept_path_paranoia: stat(%s) failed", fq_path );
return 0;
}
/* Is path owned by authorized user? */
if ( check_own ) {
if ( st.st_uid != rack->uid ) {
debug3( "accept_path_paranoia: %s not owned by "
"proper user", fq_path );
return 0;
}
}
/* Is path writable by others? */
if ( check_write ) {
if ( ( st.st_mode & S_IWGRP ) || ( st.st_mode & S_IWOTH ) ) {
debug3( "accept_path_paranoia: %s writable by others",
fq_path );
return 0;
}
}
return 1;
}
plugrack_t plugrack_create( void )
{
plugrack_t rack = (plugrack_t) xmalloc( sizeof( struct _plugrack ) );
rack->paranoia = PLUGRACK_PARANOIA_NONE;
rack->major_type = NULL;
rack->uid = PLUGRACK_UID_NOBODY;
rack->entries = list_create( plugrack_entry_destructor );
return rack;
}
int
plugrack_destroy( plugrack_t rack )
{
ListIterator it;
plugrack_entry_t *e;
if ( ! rack )
return SLURM_ERROR;
/*
* See if there are any plugins still being used. If we unload them,
* the program might crash because cached virtual mapped addresses
* will suddenly be outside our virtual address space.
*/
it = list_iterator_create( rack->entries );
while ( ( e = list_next( it ) ) != NULL ) {
if ( e->refcount > 0 ) {
debug2( "plugrack_destroy: attempt to destroy "
"plugin rack that is still in use" );
list_iterator_destroy( it );
return SLURM_ERROR; /* plugins still in use. */
}
}
list_iterator_destroy( it );
FREE_NULL_LIST( rack->entries );
xfree( rack->major_type );
xfree( rack );
return SLURM_SUCCESS;
}
int
plugrack_set_major_type( plugrack_t rack, const char *type )
{
if ( ! rack )
return SLURM_ERROR;
if ( ! type )
return SLURM_ERROR;
/* Free any pre-existing type. */
xfree( rack->major_type );
/* Install a new one. */
if ( type != NULL ) {
rack->major_type = xstrdup( type );
if ( rack->major_type == NULL ) {
debug3( "plugrack_set_major_type: unable to set type");
return SLURM_ERROR;
}
}
return SLURM_SUCCESS;
}
int
plugrack_set_paranoia( plugrack_t rack,
const uint32_t flags,
const uid_t uid )
{
if ( ! rack )
return SLURM_ERROR;
rack->paranoia = flags;
if ( flags ) {
rack->uid = uid;
}
return SLURM_SUCCESS;
}
static int
plugrack_add_plugin_path( plugrack_t rack,
const char *full_type,
const char *fq_path )
{
plugrack_entry_t *e;
if ( ( ! rack ) || ( ! fq_path ) )
return SLURM_ERROR;
e = (plugrack_entry_t *) xmalloc( sizeof( plugrack_entry_t ) );
e->full_type = xstrdup( full_type );
e->fq_path = xstrdup( fq_path );
e->plug = PLUGIN_INVALID_HANDLE;
e->refcount = 0;
list_append( rack->entries, e );
return SLURM_SUCCESS;
}
/* test for the plugin in the various colon separated directories */
int
plugrack_read_dir( plugrack_t rack, const char *dir )
{
char *head, *dir_array;
int i, rc = SLURM_SUCCESS;
if ( ( ! rack ) || ( ! dir ) )
return SLURM_ERROR;
dir_array = xstrdup(dir);
head = dir_array;
for (i=0; ; i++) {
if (dir_array[i] == '\0') {
if ( _plugrack_read_single_dir( rack, head ) ==
SLURM_ERROR)
rc = SLURM_ERROR;
break;
}
else if (dir_array[i] == ':') {
dir_array[i] = '\0';
if ( _plugrack_read_single_dir( rack, head ) ==
SLURM_ERROR)
rc = SLURM_ERROR;
head = dir_array + i + 1;
}
}
xfree( dir_array );
return rc;
}
static int
_plugrack_read_single_dir( plugrack_t rack, char *dir )
{
char *fq_path;
char *tail;
DIR *dirp;
struct dirent *e;
struct stat st;
static const size_t type_len = 64;
char plugin_type[ type_len ];
static int max_path_len = 0;
/* Allocate a buffer for fully-qualified path names. */
if (max_path_len == 0) {
max_path_len = pathconf("/", _PC_NAME_MAX);
if (max_path_len <= 0)
max_path_len = 256;
}
fq_path = xmalloc( strlen( dir ) + max_path_len + 1 );
xassert( fq_path );
/*
* Write the directory name in it, then a separator, then
* keep track of where we want to write the individual file
* names.
*/
strcpy( fq_path, dir );
tail = &fq_path[ strlen( dir ) ];
*tail = '/';
++tail;
/* Check whether we should be paranoid about this directory. */
if ( ! accept_path_paranoia( rack,
dir,
rack->paranoia &
PLUGRACK_PARANOIA_DIR_OWN,
rack->paranoia &
PLUGRACK_PARANOIA_DIR_WRITABLE ) ) {
xfree( fq_path );
return SLURM_ERROR;
}
/* Open the directory. */
dirp = opendir( dir );
if ( dirp == NULL ) {
error( "cannot open plugin directory %s", dir );
xfree( fq_path );
return SLURM_ERROR;
}
while ( 1 ) {
e = readdir( dirp );
if ( e == NULL )
break;
/*
* Compose file name. Where NAME_MAX is defined it represents
* the largest file name given in a dirent. This macro is used
* in the allocation of "tail" above, so this unbounded copy
* should work.
*/
strcpy( tail, e->d_name );
/* Check only regular files. */
if ( (xstrncmp(e->d_name, ".", 1) == 0) ||
(stat( fq_path, &st ) < 0) ||
(! S_ISREG(st.st_mode)) )
continue;
/* Check only shared object files */
if (! _so_file(e->d_name))
continue;
/* file's prefix must match specified major_type
* to avoid having some program try to open a
* plugin designed for a different program and
* discovering undefined symbols */
if ((rack->major_type) &&
(!_match_major(e->d_name, rack->major_type)))
continue;
/* See if we should be paranoid about this file. */
if (!accept_path_paranoia( rack,
fq_path,
rack->paranoia &
PLUGRACK_PARANOIA_FILE_OWN,
rack->paranoia &
PLUGRACK_PARANOIA_FILE_WRITABLE )) {
debug3( "plugin_read_dir: skipping %s for security "
"reasons", fq_path );
continue;
}
/* Test the type. */
if ( plugin_peek( fq_path,
plugin_type,
type_len,
NULL ) == SLURM_ERROR ) {
continue;
}
if ( rack->major_type &&
( xstrncmp(rack->major_type,
plugin_type,
strlen( rack->major_type ) ) != 0 ) ) {
continue;
}
/* Add it to the list. */
(void) plugrack_add_plugin_path( rack, plugin_type, fq_path );
}
closedir( dirp );
xfree( fq_path );
return SLURM_SUCCESS;
}
/* Return true if the specified pathname is recognized as that of a shared
* object (i.e. containing ".so\0") */
static bool
_so_file ( char *file_name )
{
int i;
if (file_name == NULL)
return false;
for (i=0; file_name[i] ;i++) {
if ( (file_name[i] == '.') && (file_name[i+1] == 's') &&
(file_name[i+2] == 'o') && (file_name[i+3] == '\0') )
return true;
}
return false;
}
/* Return true of the specified major_type is a prefix of the shared object
* pathname (i.e. either "<major_name>..." or "lib<major_name>...") */
static bool
_match_major ( const char *path_name, const char *major_type )
{
char *head = (char *)path_name;
/* Special case for BlueGene systems */
if (xstrncmp(head, "libsched_if", 11) == 0)
return false;
if (xstrncmp(head, "lib", 3) == 0)
head += 3;
if (xstrncmp(head, major_type, strlen(major_type)))
return false;
return true;
}
int
plugrack_purge_idle( plugrack_t rack )
{
ListIterator it;
plugrack_entry_t *e;
if ( ! rack )
return SLURM_ERROR;
it = list_iterator_create( rack->entries );
while ( ( e = list_next( it ) ) != NULL ) {
if ( ( e->plug != PLUGIN_INVALID_HANDLE ) &&
( e->refcount == 0 ) ){
plugin_unload( e->plug );
e->plug = PLUGIN_INVALID_HANDLE;
}
}
list_iterator_destroy( it );
return SLURM_SUCCESS;
}
int
plugrack_load_all( plugrack_t rack )
{
ListIterator it;
plugrack_entry_t *e;
if ( ! rack )
return SLURM_ERROR;
it = list_iterator_create( rack->entries );
while ( ( e = list_next( it ) ) != NULL ) {
if ( e->plug == PLUGIN_INVALID_HANDLE ) {
plugin_load_from_file(&e->plug, e->fq_path);
}
}
list_iterator_destroy( it );
return SLURM_SUCCESS;
}
plugin_handle_t
plugrack_use_by_type( plugrack_t rack,
const char *full_type )
{
ListIterator it;
plugrack_entry_t *e;
if ( (!rack) || (!full_type) )
return PLUGIN_INVALID_HANDLE;
it = list_iterator_create(rack->entries);
while ((e = list_next(it))) {
plugin_err_t err;
if (xstrcmp(full_type, e->full_type) != 0)
continue;
/* See if plugin is loaded. */
if (e->plug == PLUGIN_INVALID_HANDLE &&
(err = plugin_load_from_file(&e->plug, e->fq_path)))
error ("%s: %s", e->fq_path, plugin_strerror (err));
/* If load was successful, increment the reference count. */
if (e->plug != PLUGIN_INVALID_HANDLE)
e->refcount++;
/*
* Return the plugin, even if it failed to load -- this serves
* as an error return value.
*/
list_iterator_destroy(it);
return e->plug;
}
/* Couldn't find a suitable plugin. */
list_iterator_destroy(it);
return PLUGIN_INVALID_HANDLE;
}
int
plugrack_finished_with_plugin( plugrack_t rack, plugin_handle_t plug )
{
ListIterator it;
plugrack_entry_t *e;
if ( ! rack )
return SLURM_ERROR;
it = list_iterator_create( rack->entries );
while ( ( e = list_next( it ) ) != NULL ) {
if ( e->plug == plug ) {
e->refcount--;
if ( e->refcount < 0 )
e->refcount = 0;
/* Do something here with purge policy. */
list_iterator_destroy( it );
return SLURM_SUCCESS;
}
}
/* Plugin not in this rack. */
list_iterator_destroy( it );
return SLURM_ERROR;
}
extern int plugrack_print_all_plugin(plugrack_t rack)
{
ListIterator itr;
plugrack_entry_t *e = NULL;
char *sep, tmp[64];
int i;
xassert(rack->entries);
itr = list_iterator_create(rack->entries);
info("MPI types are...");
while ((e = list_next(itr)) != NULL) {
/*
* Support symbolic links for various pmix plugins with names
* that contain version numbers without listing duplicates
*/
sep = strstr(e->fq_path, "/mpi_");
if (sep) {
sep += 5;
i = snprintf(tmp, sizeof(tmp), "%s", sep);
if (i >= sizeof(tmp))
tmp[sizeof(tmp)-1] = '\0';
sep = strstr(tmp, ".so");
if (sep)
sep[0] = '\0';
sep = tmp;
} else
sep = (char *) e->full_type; /* Remove "const" */
info("%s", sep);
}
list_iterator_destroy(itr);
return SLURM_SUCCESS;
}