Name

UNIX Password, Roles & Node Management: Plugins


Plugins

The two core applications in Password Management - password_generator.pl and password_xfer.pl - are based almost entirely on plugins. These plugins are little snippets of perl code that can be modified or augmented as needed. All plugins are registered with Covad::Pwman::register_plugins().

When and how do they fire?

There are three classes of plugins: obtain, gen, and push. Each class of plugin has two phases: pre, and post. The gen class has a third phase: format. These classes and phases are detailed below:

obtain
obtain plugins are run when a username (eg, alice) needs to be converted to a user entry (eg, alice:x:2000:2000:Alice User:/home/alice:/bin/sh). It's the first class of plugins executed by password_generator.pl.

You can have as many obtain_pre_* plugins that you need. You must have one obtain_post_* plugin, and only one.

gen
gen plugins are executed by password_generator.pl. gen_pre_* plugins are used to examine and modify entries before they're generated. gen_format_* plugins acutally do the formatting nessecary; they translate the user entry hashrefs into passwd(4) and shadow(4) file formats. gen_post_* plugins can be used to check and verify work files after they've been formatted.

push
push plugins operate against nodes when they're being transferred to by password_xfer.pl. push_pre_* plugins run before the work files are copied and the password_rcvr.pl is run on the node. push_post_* are run after the password_rcvr.pl application has successfully exited.

If you were to look at the order of all classes and phases of plugins by application, it would look something like this:

Each plugin is loaded at startup via a require within an eval. If the plugin doesn't compile it is discarded.

When it comes time for a plugin to execute, all valid plugins for this type and phase are essentially foreach'd around with the same argument set. There is no particular order to plugin execution; you can only be sure that you'll get all the phases for a particular type at the same time (in other words, you can ensure that during a particular phase - let's say the pre phase for type gen - you'll only be working on plugins who are slated for gen_pre). If a plugin unexpectedly quits or times out, the node will be considered in an undeterminiable state and will not continue being processed.

Writing new plugins

It's fairly straightfoward to write new plugins; they're just little blocks of perl that get turned into coderefs, and executed one at a time. They have some rules, which are in this section.

Naming conventions

Plugins need to be called what they are. The name of a plugin needs to meet the following syntax:

  {type}_{phase}_{name}

...where:

type:
is one of gen, push, or obtain

phase;
is one of pre or post. Note that if type is gen, then phase can also be format.

name:
is any valid filename character that is not whitespace nor unprintable nor ``meta''. To be safe, the range of valid characters is: a-z A-Z 0-9 . - _

Basic structure

Each plugin must assign a reference to a subroutine to a scalar called $plugin. The following should suffice:

  $plugin = sub {
    ## do stuff here. 
  };
  1;

You can specify any other file-scoped variables or subroutines that you like. To avoid namespace collision, ensure that any additional subroutines or variables are prefixed with _plugin_name.

You can also, optionally, specify a single scalar called $info, which more clearly identifies what this plugin is supposed to accomplish. Something like:

  $info = "Sprekulates the antipasto.";
  $plugin = sub {
    unless ( sprekulate(@_) ) {
      return undef;
    } else {
      return 1;
    } 
  };

...or similar.

What arguments are passed?

Every plugin is passed a hashref of arguments. Accessing them is as simple as:

  my(%arg) = @_;

For the most part, every plugin receives the following arguments:

hostname
The name of the node currently being processed as a scalar.

props
All currently active properties as parsed via Covad::Pwman::Properties as a hashref.

users_file
Absolute pathname of the NCF for this node, as a scalar. This file will more than likely reside at pwman.active_inventory/hostname.

tags
Pre-parsed NCF data for this node (via Covad::Pwman::Parsers::pwman_parse_ncf), as a hashref.

users
Pre-generated structure of users as returned from Covad::Pwman::Roles::pwman_role_builder, as a hashref.

password_file
Absolute pathname of the passwd working file as a scalar. This file will more than likely reside at pwman.work_dir/hostname.passwd.

Note that for gen_format plugins, this argument is a filehandle opened in write mode for the work file.

shadow_file
Absolute pathname of the shadow working file as a scalar. This file will more than likely reside at pwman.work_dir/hostname.shadow.

Note that for gen_format plugins, this argument is a filehandle opened in write mode for the work file.

sudoers_file
Absolute pathname of the sudoers working file as a scalar. This file will more than likely reside at pwman.work_dir/hostname.sudoers.

Note that for gen_format plugins, this argument is a filehandle opened in write mode for the work file.

digest_file
Absolute pathname of the digest working file as a scalar. This file will more than likely reside at pwman.work_dir/hostname.digest.

Note that for gen_format plugins, this argument is a filehandle opened in write mode for the work file.

The exceptions to this list of arguments are in the obtain_pre and obtain_post plugins. These two groups only receive the following arguments: hostname, users, users_file, tags, and props. The format and content of these arguments is unchanged.

What options are available?

All options listed in etc/pwman.properties are made available to all plugins.

The property pwman.plugin_dir specifies where all plugins are located.

How should they exit?

If a plugin needs to signal that it was not successful but that the node is still intact (for example, to indicate that it did no work), it should return undef. A non-zero return status (eg, return 1;) indicates success, and that something was done. Plugins that reach a bad state may exit abnormally with die. If a plugin abnormally exits, the node is considered in an undeterminable state and will be skipped.

Plugins are wrapped within an eval, so an exit or die by a plugin gets caught as you'd expect. If die is used, then the contents of $@ are dropped into the log and presented to the user. As a courtesy, please remember to die with a message containing the name of the plugin and a newline, something like:

  die "gen_pre_sprocket: can't auto-frob the thrombosifier - please frob it manually.\n";

The distinct lack of returnable values means you have to edit the argments as they are passed in order to actually accomplish something. Ensure that plugins limit their modifications to only bits needed to accomplish it's goal.

Disabling select plugins

If you need to disable a plugin (because it doesn't work in your environment, or you don't need that particular feature check), you can just move it out of the way. Or you can just delete it. (I prefer the method of renaming it to DISABLED.plugin_filename, but that's just me.)


See Also

the Covad::Pwman manpage, the Covad::Pwman::Parsers manpage, the Covad::Pwman::Roles manpage, the Covad::Pwman::Properties manpage


Author

Jon Gilbert <jong@jong.org>


Version

$Id: pwman_docs_plugins.pod,v 1.2 2005/10/20 08:46:34 jgilbertsjc Exp $


SourceForge.net Logo