logo_kerberos.gif

Projects/Plugin support improvements

From K5Wiki
< Projects(Difference between revisions)
Jump to: navigation, search
(Domain-specific and Domain-independent Interfaces and Modules)
(Plugin Interface)
Line 51: Line 51:
   
 
=== Plugin Interface ===
 
=== Plugin Interface ===
  +
  +
A plugin or pluggable interface is an interface, possibly internal to a library, that can be implemented by a third party in a modular, well-compartmentalized manner. These implementations of pluggable interfaces are called plugin modules. Pluggable interfaces allow a consumer to use the capabilities of the interface without needing to be aware of the implementation details. In particular, a pluggable interface prevents the consumer from needing to know whether the module is a built-in or a dynamically loadable module. Pluggable interfaces can be one-to-one, or one-to-many. An example of one-to-one is the DAL, and an example of one-to-many is preauth.
  +
  +
A pluggable interface has two parts: a consumer interface and a provider interface. Typically, library code implements the consumer interface, and application code or other library code calls the functions of the consumer interface. The provider interface is what the plugin module implements.
  +
  +
==== Consumer interface ====
  +
  +
The consumer interface isolates the consumer from implementation details of the pluggable interface. The consumer does not generally need to know about whether a given module is built-in or dynamically loaded. The implementation of a consumer interface is essentially a glue layer, and can make use of domain-independent (not specific to any pluggable interface) capabilities of the plugin framework. The consumer might explicitly register a new plugin module that it implements: this capability is part of the plugin manager.
  +
  +
A consumer of a pluggable interface uses an opaque handle, obtained from a loader function that is part of the pluggable interface, to call the methods of a plugin module. Each method of the consumer interface is an ordinary C function that takes the opaque handle either explicitly as its first argument or implicitly by some means such as a module name. Conceptually, these pluggable interface functions are wrapper functions that call through function pointers contained in the opaque plugin module handle object. (though this is not the only possible implementation)
  +
  +
A handle can represent:
  +
  +
* the plugin module itself
  +
* a resource to which the plugin module provides access (e.g., a ccache handle)
  +
* a set of plugin modules (e.g., the set of all available preauth mechanisms)
  +
  +
One rationale for using wrapper functions instead of having the consumer directly invoke methods through a function pointer is to make it easier for debuggers and analysis tools to recognize when a particular interface method is being called. (Function pointers might have identifier names that look nothing like the actual name of the function they point to, in addition to enabling confusing aliasing.)
  +
  +
The loader function is specific to the pluggable interface. One reason is for type safety: there will be a distinct opaque handle type for each pluggable interface, allowing compile-time checking to catch some sorts of programming errors. Another reason is backward compatibility: it allows a pluggable interface to support plugin modules that implement an older provider interface.
  +
  +
==== Provider interface ====
  +
  +
A plugin module is a unit of code that implements the provider interface portion of a pluggable interface. Plugin modules can be built in or dynamically loaded. Several alternatives exist for the form of the provider interface, but some have significant advantages in allowing the plugin module to use identical source code for both built-in and loadable modules.
  +
  +
A built-in module is a module whose executable code is located within the library shared object or executable program file, or behaves as if it were. (While separate library shared objects that the calling library depends on can contain "built-in" modules for the calling library, this can cause problems with cyclic references.) The distinguishing characteristic of a built-in module is that, as part of program startup, the operating system automatically maps the executable code of the module into the address space of the process that calls it, without any explicit action by the library or program.
  +
  +
A dynamically loaded module is a module whose executable code is located within a file that is distinct from the library or program that calls it. The plugin support framework uses the runtime linker (or equivalent) to explicitly map the executable code of the module into the process address space. In POSIX systems, this is typically done using <code>dlopen()</code>.
  +
  +
===== Loadable module provider interface =====
  +
  +
The domain-independent part of the provider interface of a loadable module consists of a single exported function symbol, which denotes the vtable constructor function for that module. The signature of the constructor is specific to the pluggable interface the module implements. The contents of the vtable are also specific to the pluggable interface.
  +
  +
The constructor usually takes as arguments an interface version number, a pointer to a caller-allocated vtable structure for the interface, and the size of the structure (as an added precaution against version mismatches). The name of the function symbol is constructed from the name of the pluggable interface and the name of the plugin module. (Alternatively, all plugin modules implementing a given interface have the same name for the symbol, but see the section on built-in modules for why this might be a bad idea.)
  +
  +
Although the caller (actually the plugin support code) allocates the vtable structure in the above description, one alternative is to have the module perform the allocation of the structure itself. This can cause problems if the module uses a different memory allocator than the caller.
  +
  +
Another alternative is to have the vtable constructor instead return a pointer to a compiled-in vtable. This might cause performance problems related to copy relocations.
  +
  +
===== Built-in module provider interface =====
  +
  +
A built-in module provides the same interface as a loadable module. In the alternative where we use same exported function symbol for each loadable module implementing a pluggable interface, the built-in modules will still need distinct prefixes for each vtable constructor function. This requires slightly different source code for a built-in module versus a loadable module, or a technique like using macros to do renaming.
   
 
== Interfaces ==
 
== Interfaces ==

Revision as of 10:19, 21 July 2010

Contents

Motivations, Priorities & Requirements

Motivations: there are a number of motivations behind the creation of the plugin architecture framework.

  • Desire to separate plugin interface from its implementation;
  • Desire to provide simple and clear mechanism that facilitates additions of new plugin interfaces and their implementations(modules);
  • Handles both built-in and dynamic plugin modules;
  • Allows multiple implementation of the same plugin interface;
  • Provides uniform way to supply parameters for plugin configuration;
  • Allows one plugin implementation to use services provided by the other plugin implementations.

Requirements: from these items we have developed a more formal set of requirements covering the design and the implementation of the framework to support the plugins. These are as follows:

  1. Allow third parties to implement multiple plugin modules for each pluggable interface.
  2. Allow a plugin module to build as dynamic or built-in from the same source code.
  3. Allow third parties to more easily create new plugin modules.
  4. Provide a uniform method for configuring discovery of plugin modules.
  5. Improve readability of code that calls pluggable interfaces.
  6. Allow easier creation of new pluggable interfaces.
  7. Allow incremental transition of existing pluggable interfaces to the new framework.

Architecture Overview and Concepts

Introduction

Participants

The following is a summary of participants and components within the architecture. Further details are provided in the sections below.

Plugin Manager: The plugin manager provides a set of generic capabilities that are independent of individual pluggable interfaces. The plugin manager implements operations that manage plugin configuration and plugin registry services. It is responsible for managing plugin handles and provides discovery capabilities for consumers or callers.

Plugin Interface: A plugin interface is an interface that can be implemented by a third party in a modular manner. An implementation of a plugin interfaces is referred to as a plugin module. Furthermore, a plugin interface itself consist of a consumer interface and provider interface.

Plugin Module: A plugin module is an implementation of a pluggable interface.

Consumer: The consumer or caller is the entity that uses the plugin module. The consumer or caller may also perform discovery of available modules prior to using them.


Architecture Components

In this section we provide further details on the components of the architecture, describing its features and behaviors.

Plugin Manager

Plugin Interface

A plugin or pluggable interface is an interface, possibly internal to a library, that can be implemented by a third party in a modular, well-compartmentalized manner. These implementations of pluggable interfaces are called plugin modules. Pluggable interfaces allow a consumer to use the capabilities of the interface without needing to be aware of the implementation details. In particular, a pluggable interface prevents the consumer from needing to know whether the module is a built-in or a dynamically loadable module. Pluggable interfaces can be one-to-one, or one-to-many. An example of one-to-one is the DAL, and an example of one-to-many is preauth.

A pluggable interface has two parts: a consumer interface and a provider interface. Typically, library code implements the consumer interface, and application code or other library code calls the functions of the consumer interface. The provider interface is what the plugin module implements.

Consumer interface

The consumer interface isolates the consumer from implementation details of the pluggable interface. The consumer does not generally need to know about whether a given module is built-in or dynamically loaded. The implementation of a consumer interface is essentially a glue layer, and can make use of domain-independent (not specific to any pluggable interface) capabilities of the plugin framework. The consumer might explicitly register a new plugin module that it implements: this capability is part of the plugin manager.

A consumer of a pluggable interface uses an opaque handle, obtained from a loader function that is part of the pluggable interface, to call the methods of a plugin module. Each method of the consumer interface is an ordinary C function that takes the opaque handle either explicitly as its first argument or implicitly by some means such as a module name. Conceptually, these pluggable interface functions are wrapper functions that call through function pointers contained in the opaque plugin module handle object. (though this is not the only possible implementation)

A handle can represent:

  • the plugin module itself
  • a resource to which the plugin module provides access (e.g., a ccache handle)
  • a set of plugin modules (e.g., the set of all available preauth mechanisms)

One rationale for using wrapper functions instead of having the consumer directly invoke methods through a function pointer is to make it easier for debuggers and analysis tools to recognize when a particular interface method is being called. (Function pointers might have identifier names that look nothing like the actual name of the function they point to, in addition to enabling confusing aliasing.)

The loader function is specific to the pluggable interface. One reason is for type safety: there will be a distinct opaque handle type for each pluggable interface, allowing compile-time checking to catch some sorts of programming errors. Another reason is backward compatibility: it allows a pluggable interface to support plugin modules that implement an older provider interface.

Provider interface

A plugin module is a unit of code that implements the provider interface portion of a pluggable interface. Plugin modules can be built in or dynamically loaded. Several alternatives exist for the form of the provider interface, but some have significant advantages in allowing the plugin module to use identical source code for both built-in and loadable modules.

A built-in module is a module whose executable code is located within the library shared object or executable program file, or behaves as if it were. (While separate library shared objects that the calling library depends on can contain "built-in" modules for the calling library, this can cause problems with cyclic references.) The distinguishing characteristic of a built-in module is that, as part of program startup, the operating system automatically maps the executable code of the module into the address space of the process that calls it, without any explicit action by the library or program.

A dynamically loaded module is a module whose executable code is located within a file that is distinct from the library or program that calls it. The plugin support framework uses the runtime linker (or equivalent) to explicitly map the executable code of the module into the process address space. In POSIX systems, this is typically done using dlopen().

Loadable module provider interface

The domain-independent part of the provider interface of a loadable module consists of a single exported function symbol, which denotes the vtable constructor function for that module. The signature of the constructor is specific to the pluggable interface the module implements. The contents of the vtable are also specific to the pluggable interface.

The constructor usually takes as arguments an interface version number, a pointer to a caller-allocated vtable structure for the interface, and the size of the structure (as an added precaution against version mismatches). The name of the function symbol is constructed from the name of the pluggable interface and the name of the plugin module. (Alternatively, all plugin modules implementing a given interface have the same name for the symbol, but see the section on built-in modules for why this might be a bad idea.)

Although the caller (actually the plugin support code) allocates the vtable structure in the above description, one alternative is to have the module perform the allocation of the structure itself. This can cause problems if the module uses a different memory allocator than the caller.

Another alternative is to have the vtable constructor instead return a pointer to a compiled-in vtable. This might cause performance problems related to copy relocations.

Built-in module provider interface

A built-in module provides the same interface as a loadable module. In the alternative where we use same exported function symbol for each loadable module implementing a pluggable interface, the built-in modules will still need distinct prefixes for each vtable constructor function. This requires slightly different source code for a built-in module versus a loadable module, or a technique like using macros to do renaming.

Interfaces

Sample Code and Proof of Concept

Deliverables

Personal tools