Getting Started
For cPanel & WHM 11.32
Summary
Before beginning development, you should be sure to read the
Standardized Hooks Introduction article, as it provides rudimentary information about the Standardized Hooks System.
There are 3 key elements to a hook:
- The Hookable Event — The cPanel & WHM event and the stage (e.g.,
pre or post) of the event to hook.
- The Hook Action — The code that the Standardized Hooks System will execute.
- Hook Registration — The reference between the hoookable event and the desired hook action.
Basic logic flow of a Standardized Hook
Within a given cPanel & WHM event — an API call or binary process — there are usually well defined phases within the code. For simplicity, one can think of these phases as being something similar to the preparation of variables and environment, the execution of one or more specific operations, and the rendering the operation's results via HTTP content headers and body.
cPanel & WHM events that implement the Standardized Hook System have dispatch routines
before and
after the execution phase. The
before and
after dispatches are defined as the
pre and
post stages, respectively.
When the given event is requested, and the Standardized Hooks dispatch occurs, any registered hooks relating to that specific event and its respective dispatch stage will be executed. The result is then returned to the event's code for evaluation.
Most
pre stage dispatches have the opportunity to signal a failure that skips the execution phase of the event entirely. This signal is then used by the rendering phase as if the failure occurred natively before execution.
There are other complexities of the Standardized Hooks System covered in the
Advanced Usage article.
Determining which event to hook
The
Hookable Events article provides a reference for the different categories and their respective events and stages. Each hookable event defined there will provide information that is required later in the process.
When
creating the hook's action code, the detailed input parameters will be passed to the hook's action code.
When
registering the hook, you will provide the stage, and a reference to the hook action code to the hook management interface.
Components of the hook action code
Input received by the hook action
Hook action code will be passed a context and a dataset, respectively.
The context is a string that contains the category, followed by 2 colons, followed by the event (e.g.,
Passwd::ChangePasswd). This string is provided as a convenience for any internal logic within the hook action code. Simply stated, it is the first argument received by the action code.
The dataset will typically be a set of key/value pairs; however, some events do not provide any relevant data and simply pass an undefined value instead. The
hookable events article outlines each dataset the hook action code can expect to receive.
How the action code should digest these arguments is explained below, due to inherent differences between a
hook action as a Perl module and
hook action code as a script.
Output returned by the hook action
The return value from a hook action can contain 1 or 2 values. These values will be captured by the Standardized Hooks System as an array reference. How the action code should return these values is explained below, due to inherent differences between a
hook action as a Perl module and
hook action code as a script.
At minimum, a boolean value must be returned. This value is the
hook action result.
A value of
1 indicates that the hook action code performed successfully, while a value of
0 indicates that the hook action code failed.
A second returned value is optional. This optional value needs to be a string that provides a success or failure message. This message should bubble up to the Standardized Hooks System report faculties.
Writing your hook action code
The choice of writing a hook action as a Perl module or a script is left to the developer and the needs of the hook action.
Hook action as a Perl module
A hook action can be encapsulated in a Perl module subroutine. The subroutine's name is arbitrary, as it will be referenced later as part of the hook descriptor, when
registering your code.
The subroutine should receive 2 arguments. The first argument will be a string representing the context. The second argument will be a hash reference representing the dataset.
The return value fo the subroutine should be the result, as a boolean, and an optional message, as a string.
The following boilerplate can be used for Perl module hook action:
sub foo_bar_hook_action {
my ( $context, $data ) = @_;
my $result = 0; # boolean set to fail
my $message = 'reason'; # string set to a reason for $result
# Do stuff here
return $result, $message;
}
Note: Hook action Perl modules must be installed in the
/var/cpanel/perl5/lib/ directory.
Hook action code as a script
A hook action can be an executable script. The Standardized Hooks System does not restrict which scripting languages a developer can use, so long as the underlying Linux shell can execute it. The script's name and location is arbitrary, as it will be referenced later, as part of the hook descriptor, when
registering your hook code.
This script will receive arguments from the hookable event via the STDIN stream;
not a magic shell argument array,
$argv,
@ARGV, etc. The script will need to read the entire STDIN stream (until EOL) and treat it as a JSON encoded data structure.
Once the JSON string from the STDIN stream is decoded, the native data structure will be a hash. The
context element will contain a string representing the context. The
data element will contain a hash, representing a dataset.
Note: Different scripting languages define these data structures differently. In the context of this paragraph, an array is a list of 2 key/value pairs where the
key is a numerical index starting at 0. A hash is a list of key/value pairs where the key is an explicit string. Hashes are often referred to as associative arrays.
The return value of the script must be a single line print statement to the STDOUT stream. This statement must contain at least one value — the success or failure result of the hook action, i.e.,
1 or
0. An optional second return value may be a string that contains a message of success or reason for failure.
The following boilerplate can be used as a Perl script:
#!/usr/bin/perl
use IO::Select;
use JSON::Syck;
$input = get_passed_data();
my ( $result_status, $result_msg ) = do_something($input);
print "$result_status $result_msg";
exit;
##
# Supporting Functions Below Here
##
sub do_something {
my ($input) = @_;
my ( $status, $msg );
#...do something here
$status = 1; # or zero for fail
$msg = "Success or fail message here.";
return $status, $msg;
}
sub get_passed_data {
my $raw_data = '';
my $input_data = {};
my $selects = IO::Select->new();
$selects->add( \*STDIN );
if ( $selects->can_read(.1) ) {
while (<STDIN>) {
$raw_data .= $_;
}
$input_data = JSON::Syck::Load($raw_data);
}
return $input_data;
}
The following boilerplate can be used for a PHP script:
#!/usr/bin/php -q
<?php
// Get the STDIN input
$input = get_passed_data();
// Do whatever hook action is desired
list($result_status, $result_msg) = do_something($input);
// Print the result to STDOUT stream
echo "$result_status $result_msg";
/**
* Supporting Functions Below Here
*/
function do_something($input = array()) {
//...do stuff here;
$status = "1"; //or zero for fail
$msg = "Success or fail message here.";
return array($status, $msg);
}
/**
* Read the STDIN stream
* Data passed to this script will be a JSON serialized structure
*/
function get_passed_data() {
$raw_data;
$stdin_fh = fopen('php://stdin', 'r');
if ( is_resource($stdin_fh) ) {
stream_set_blocking($stdin_fh, 0);
while ( ($line = fgets( $stdin_fh, 1024 )) !== false ) {
$raw_data .= trim($line);
}
fclose($stdin_fh);
}
if ($raw_data) {
$input_data = json_decode($raw_data, true);
} else {
$input_data = array('context'=>array(),'data'=>array(), 'hook'=>array());
}
return $input_data;
}
?>
Registering your hook action code
Once the hook action code has been written, it can be associated with a hookable event so that it is executed by the respective cPanel & WHM functionality.
Hook action code and hookable events are associated using the Standardized Hooks System's CLI utility. This command line utility registers key elements required by the Standardized Hooks System in the Standardized Hook System's database.
The utility provides 2 methodologies to register a hook:
- Manual CLI options — A traditional CLI pattern of options and values.
- The Describe Pattern — A programmatic way for hook action code to provide descriptive information on demand, such as when registering the hook.
In either method, there are a minimum of 3 arguments that need to be provided on the command line when invoking
/usr/local/cpanel/bin/manage_hooks.
- The Utility Action — The action the utility should perform against the hook database. Valid actions are
add, delete, list, and fullhelp; however, we will only concern ourselves with the add action in this article.
- Hook Action Type — Valid types are
script and module and correspond directly to the hook action code authored in the section above.
- Hook Action Reference — If the hook action code is a script, this value will need to be the absolute path and filename of that script. If the hook action code is a Perl module, this value will need to be the same as the package within the Perl module.
In addition to those arguments, the utility will also need other information:
- Category — The logical category of the hookable event. (e.g.,
Stats)
- Event — The hookable event name. (e.g.,
RunAll)
- Stage — The dispatch stage of the hookable event. (e.g.,
pre or post)
How the utility receives this information is dependent on which registration method was chosen. You can find more information in the following subsections.
For a complete guide on managing hooks, please review our
Standardized Hooks Management document.
Manual registration via the CLI utility
Manual registration of hooks works similarly to many *nix CLI utilities. The required arguments are positional and the optional arguments trail afterwards, using a flag and value schema.
The following example would add a hook for a script called
foo in the
/opt/myapp/ directory. The script will be invoked before new cPanel accounts are created:
/usr/local/cpanel/bin/manage_hooks add script /opt/myapp/foo --category Whostmgr --event Accounts::Create --stage pre
Note: The The
--manual 1 option should be specified when registering a hook outside of a describe pattern. When this flag is specified, all provided CLI options are used. If this flag is missing, the utility will first attempt to find a describe pattern subroutine and use those options, regardless of any existing CLI options. We recommend using the describe pattern for any hook action code to simplify this complex, but necessary, CLI logic.
The Describe Pattern
The describe pattern allows developers to embed category, event, stage, and other attributes related to the hook action code alongside the action code itself. This provides a programmatic means for the action code to express the developer's intended use of the hook. Once a developer has incorporated that pattern, registration of the hook action is simple.
The following code example would add any described hooks from the
Foo::Bar module located in
/var/cpanel/perl5/lib/Foo. Their category, event, stage, and other attributes are sourced from the return value of the
describe() subroutine.
[/] $ /usr/local/cpanel/bin/manage_hooks add module Foo::Bar
Note: All attributes in the embedded describe pattern will be ignored when the
--manual flag is present; only CLI descriptor options will be referenced.
For a complete guide on managing hooks, please review our
Standardized Hooks Management document.
Enabling Standardized Hooks' debug mode
The Standardized Hook System provides a multi-level debug mode. The debug mode is controlled by the
debughooks key in the cPanel & WHM configuration file
/var/cpanel/cpanel.config. The
debughook key should be one of the following values:
-
0 — Disabled (default value)
-
1 — Log information about defined hooks as they are executed. Do not include data.
-
2 — Log information about defined hooks as they are executed and their respective data.
-
Important: This setting can output a large amount of data. This data is potentially log-sensitive. You should be careful when using this debug value.
-
3 — Log information about every hookable event that is traversed, even if there are no defined hooks for the hookable event. This output includes details for defined hooks with data, i.e., the same as level 2.
For a complete guide on managing hooks, please review our
Standardized Hooks Management document.