Introduction
This guide is for cPanel users with root access to their cPanel server who want to add custom functionality to cPanel and WHM using the BASH scripting language.
It is important to remember that this guide only covers the installation of a basic script. This is useful for simple functionality that must be implemented quickly and easily. You also have the option of writing a Perl module rather than writing a script. When writing a Perl module, your code is run by the cpsrvd daemon. This allows your Perl module access to cPanel environment variables that are unavailable with scripts.
The Standardized Hooks system in cPanel allows the systems administrator or developer to register scripts with cPanel to be run at the selected hook event. The systems administrator or developer can utilize the flexible and powerful Standardized Hooks system to bolt on additional functionality using the basics outlined here.
The basics
There are only three main steps required to set up a Standardized Hook script in cPanel:
- Identify the Hookable Event that you would like your script to run on.
- Write the script that you would like to use.
- Register the script in cPanel to be associated with the hookable event.
Identifying the appropriate Hookable Event
A Hookable Event is an event that happens within the regular operation of cPanel and WHM. Here are some examples: An account is suspended, backups run, an account is created, etc.
When identifying the appropriate Hookable Event, you should browse the available events and think about which one makes the most sense for the particular functionality you are adding. For example, if you wanted to automatically install WordPress for new accounts, you would likely choose the Post Account Creation event.
To find what Hookable Events are available, check our documentation here:
Writing the script
The main cPanel-specific question you'll have is how to access the data provided by a cPanel Hookable Event. All Hookable Events will output the response data to STDIN. You can read this data into bash by doing the following:
tmpfile="$(mktemp -p /tmp custom-script-data-XXXXXXXX)"
cat "${1:-/dev/stdin}" > $tmpfile
This data is provided in JSON format. One easy way to obtain JSON data values is to use Python. For example, you can get the username of a newly created account after the Create Account event with the following:
cpanelusername=$(python -c "import sys, json; print json.load(open('$tmpfile'))['data']['user']")
Registering your script and managing hooks
Here are some examples. Read the full documentation on managing hooks to fully understand these examples.
To register your executable with cPanel so it runs every time the Hookable event occurs, use the manage_hooks utility:
/usr/local/cpanel/bin/manage_hooks add script /usr/local/cpanel/3rdparty/bin/customScript.sh --manual --category Whostmgr --event Accounts::Create --stage=post
To list the existing hooks, run the following command:
/usr/local/cpanel/bin/manage_hooks list
To delete an existing hook, specify the script along with a few other details:
/usr/local/cpanel/bin/manage_hooks delete script /usr/local/cpanel/3rdparty/bin/customScript.sh --manual --category Whostmgr --event Accounts::Create --stage=post
Debugging hook-action scripts
There are three main methods for debugging when writing and testing your Standardized Hook scripts in BASH:
- Have your script direct output to a file.
Here is an example:
#!/bin/bash
data="This string will be put into your log file"
echo $data > /root/debugDataVariableContents.txt
cat "${1:-/dev/stdin}" > /root/debugHookOutputData.txt - Turn on "Standardized Hooks - Debug Mode."
Do this via "Home / Server Configuration / Tweak Settings" in WHM, then tail the log at usr/local/cpanel/logs/error_log.
- Write errors to the cPanel error_log.
You may also write to the cPanel error log, which is covered in the section below, Handling Errors.
If you have a script of any complexity, you'll likely use all three of these methods during the course of creating your script.
When you are trying to determine what kind of data is available to you after an event runs, you can use the first method, as shown above, to view that data. When you do this, you'll quickly find that the JSON data provided via STDIN is NOT formatted for human consumption. This can make reviewing the output to debug and design your script very difficult.
Luckily there is a tool that makes reading JSON easy. Once you have saved some JSON output to a file using the first debug method, you can use the json.tool module in Python to format the data. Here is an example of the JSON data generated upon account creation formatted by the json.tool Python module:
root@server [~]# python -m json.tool /root/debugHookOutputData.txt
{
"context": {
"category": "Whostmgr",
"event": "Accounts::Create",
"point": "main",
"stage": "post"
},
"data": {
"bwlimit": 0,
"contactemail": "",
"cpmod": "paper_lantern",
"digestauth": "n",
"dkim": "1",
"domain": "scripthook4.tld",
"featurelist": "default",
"force": null,
"forcedns": 0,
"gid": "",
"hascgi": "y",
"hasshell": "y",
"homedir": "/home/scripthook4",
"homeroot": "/home",
"is_restore": 0,
"locale": "en",
"mailbox_format": null,
"max_defer_fail_percentage": "unlimited",
"max_email_per_hour": "500",
"maxaddon": 0,
"maxftp": "n",
"maxlst": "n",
"maxpark": 0,
"maxpop": "n",
"maxsql": "n",
"maxsub": "n",
"mxcheck": "local",
"no_cache_update": 0,
"owner": "root",
"pass": "REDACTED",
"plan": "default",
"quota": "unlimited",
"skip_mysql_dbowner_check": 0,
"spf": "1",
"uid": "",
"useip": "n",
"user": "scripthook4",
"useregns": 0
},
"hook": {
"escalateprivs": 0,
"exectype": "script",
"hook": "/usr/local/cpanel/3rdparty/bin/customScript.sh",
"id": "JNVpUXPPLXpR6mxcXW1d46ma",
"stage": "post",
"weight": 200
}
}
In the example above, we used the following command to format the /root/debugHookOutputData.txt file into human-readable output:
python -m json.tool /root/debugHookOutputData.txt
Handling errors
cPanel automatically catches data sent to STDERR from a script that is executed as a script hook. cPanel then publishes this data to the cPanel error log at /usr/local/cpanel/logs/error_log.
Here is an example of what happens when mktemp is unable to write to a location because it does not exist on the server:
[2018-09-26 13:06:48 +0000] info [whostmgr5] STDERR output from hook: /usr/local/cpanel/3rdparty/bin/customFuncationalityScript.sh
[2018-09-26 13:06:48 +0000] info [whostmgr5] mktemp: failed to create file via template /fakedir/custom-tmp-file-XXXXXXXX': No such file or directory
You can also write your custom error messages by redirecting output to STDERR from your script. Let's improve our above script that captures data from STDIN, by adding error handling:
tmpfile="$(mktemp -p /tmp custom-script-data-XXXXXXXX)"
cat "${1:-/dev/stdin}" > $tmpfile
if [ $? -ne 0 ]; then
echo "Custom script is unable to create a temporary file and has exited early." > /proc/self/fd/2
exit 1
fi
Here is the result:
[2018-09-26 13:37:45 +0000] info [whostmgr5] STDERR output from hook: /usr/local/cpanel/3rdparty/bin/customFuncationalityScript.sh
[2018-09-26 13:37:45 +0000] info [whostmgr5] mktemp: failed to create file via template/fakedir/custom-script-data-XXXXXXXX': No such file or directory. Custom script is unable to create a temporary file and has exited early.
[2018-09-26 13:37:45 +0000] info [whostmgr5] End STDERR from hook
There are many ways that you can write to STDERR in BASH, which include--but are not limited to--these:
echo "Helpful error message here." > /dev/stderr
echo "Helpful error message here." 1>&2 &2 echo "Helpful error message here."
echo "Helpful error message here." > /proc/self/fd/2
/proc/self is a link to the current process. /proc/self/fd contains the open file descriptors, and 2 is the file descriptor for STDERR. /proc/self/fd/0 represents STDIN, and /proc/self/fd/1 means STDOUT.
When we redirect output to the error_log file descriptor, we send the output to STDERR, which is then caught by cPanel and published to the cPanel error_log.
Beyond the basics
You can do much more with Standardized Hooks in cPanel and WHM. This guide is a pared-down introduction. Review our complete documentation:
If you require more advanced functionality.