6. Action Modules

What’s An Action Module?

An administrative action module provides the ability to AMPS to run an action at a specified time, in response to a particular Linux signal, or in response to an event such as startup or shutdown. These modules are intended to ease AMPS administration, particularly for installations where access to facilities such as cron is limited.

When to Implement an Administrative Action Module

Implement an action module when there is a specific action that needs to be taken in your environment that AMPS does not provide by default, when you can programmatically perform the action, and when your policies do not centralize administrative functions in another system.

Action Context

AMPS uses action modules to perform an action when a certain condition occurs. The same module may have different configuration settings for different instances of the action. Because the same module may be used with different parameters in different action configurations, AMPS allows you to save the parameters in an action context.

Your module implements the amps_action_create_context function to create a n authentication context. The function has this signature:

amps_action_context amps_action_create_context(amps_module_options options);

The action context is a pointer to data that your module defines. The AMPS server does not process or interpret this data. The server receives the pointer from your module when the module is initialized, and then provides the pointer back to your module for each authenticator request.

If your module is unable to successfully create the context, you return a NULL context and use the amps_logger provided to your module to log information about the problem.

Notice that you cast the pointer to amps_action_context before you return it, and cast the pointer back to the type that your application uses after AMPS returns the pointer to you.

AMPS passes configuration options to modules as a pointer to an array of amps_option structs. AMPS indicates the end of the array with an option that has a NULL key. Your module processes options until it reaches an option with a NULL key. If no options are specified in the configuration file, the first amps_option in the array will have a NULL key.

The following snippet shows one way to process options:

for (; options->key != NULL; ++options) {
    // process the key/value pair here

}

When AMPS exits, AMPS calls amps_action_destroy_context with each of the action contexts created. This gives your module an opportunity to do any resource management, cleanup or logging necessary before AMPS shuts down. The function has this signature:

int amps_action_destroy_context(amps_action_context context);

Implementing an Action

Action modules implement a single function for performing an action:

Function  
amps_action_do Perform an action. Called when the condition that triggers the action occurs: for example, when AMPS receives the specified Linux signal.

Table 6.1: Administrative Action function

To implement an action module, your module implements this method.

When AMPS runs the action, the AMPS server calls the following method:

int amps_action_do(amps_action_context context)

The action context provided is the pointer returned by the call to amps_action_create_context() for the instance of the action that is being run.

Following is a simple, minimal implementation of amps_action_do:

int amps_action_do(amps_action_context context)
{
    amps_logger(amps_module_log_level_warning, "my_action_module: just logging a message");

   // You can run any code here.

    return AMPS_SUCCESS;
}

This simple implementation simply logs a message to the AMPS error log. For your module, you would substitute code that implements whatever action you need to perform. If the action succeeds, return AMPS_SUCCESS. If the action fails, use the logger provided in amps_module_init to log information about the failure and return AMPS_FAILURE.

Working With Variables

Your module can set and expand variables within actions, in the same way that the actions provided by 60East do. Your action can set a variable for use by other actions that follow it in the action configuration, and can expand variables, whether they are provided by AMPS or whether they are set by another action module.

AMPS tracks variables in symbol scopes. Within a scope, the variable is stored as a key / value pair, where the key is the name of the variable and the value is the string that should replace the variable. Symbol scopes are stored in an ordered list, with the most recent scope at the beginning of the list. When expanding a string that contains a variable, AMPS searches the scopes in order, and expands the variable with the first value found.

AMPS maintains a global scope with the default variables common to all actions, as described in the AMPS User Guide. When an action runs, AMPS constructs a scope for that action. The scope is shared between the On action and the set of Do actions that the On action triggers.

To add variables to the current scope, you use the amps_symbol_scope_put function. This function has the following signature:

int amps_symbol_scope_put(const char* key_,   size_t keyLength_,
                          const char* value_, size_t valueLength_);

For example, to add a variable called MyCoolValue with a value of Amazing to the current scope, you would use the following code:

amps_symbol_scope_put("MyCoolValue", 11,
                      "Amazing", 7);

To expand a string that uses variables and insert the current value of the variables, you use the amps_expand_action_string function. This function requires a preallocated buffer to write the expanded string to, as shown in the following code:

char * buffer = NULL;
size_t bufferSize = 25;
int rc = AMPS_FAILURE;

const char *sampleTemplate = "AMPS expansion is {{MyCoolValue}}";
amps_symbol_scope_put("MyCoolValue", 11, "Amazing", 7);

// Start with a small size. If that doesn't fit,
// make a new buffer with the correct size.
while (rc == AMPS_FAILURE)
{
    if (buffer != NULL) { free(buffer); };
    buffer = (char*)malloc(bufferSize);
    if (buffer == NULL)
    {
        return AMPS_FAILURE;
    }
    rc = amps_expand_action_string(sampleTemplate, strlen(sampleTemplate), buffer, &bufferSize);
}

// .. use buffer
free(buffer);

To retrieve the value of a variable in the current scope, you can also use

int amps_symbol_scope_get(const char* key_, size_t keyLength,
                          const char** value_, size_t* valueLength_);

This function updates the provided value_ pointer to the location of the value and updates the location specified by valueLength_ with the length of the value.

The data returned by this function is guaranteed to be valid for the lifetime of the current function call from AMPS, but is not guaranteed to persist after that.