This guide is for cPanel users who have root access on their cPanel server and who would like to add custom functionality to cPanel and/or WHM using the BASH scripting language.
It is important to keep in mind that this guide only covers the installation of a basic script. This is useful for simple functionality that needs to 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, which are not available 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. Using the basics outlined here, the systems administrator or developer can take advantage of the flexible and powerful Standardized Hooks system to bolt on many kinds of custom functionality.
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 so that it is 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 that 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: Hookable Events.
Writing the script
The main cPanel-specific question you'll have is how to access the data that is provided by a cPanel Hookable Event. Hookable Events in cPanel output their data to STDIN. You can read in 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 of obtaining values from the JSON data is to use Python. For example, you can obtain 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. Make sure to read the full documentation on managing hooks in order 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 in 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 that is provided via STDIN is NOT formatted for human consumption. This can make it very difficult to review the output to debug and design your script properly.
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 own custom error messages by redirecting output to STDERR from your script. Let's improve our above script that captures data from STDIN, by adding some error-handling in case we are not able to write to the tmp file:
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 a number of 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
represents STDOUT.
When we redirect output to the /proc/self/fd/2
file descriptor, we send the output to STDERR, which is then caught by cPanel and published to the cPanel error_log
.
Beyond the basics
There is quite a lot more you can do with Standardized Hooks in cPanel and WHM. This guide is a pared-down introduction. Review our full documentation at Guide to Standardized Hooks if you require more advanced functionality.