Event handler or Function hook for AddonDomain and Mysql actions

B12Org

Well-Known Member
Jul 15, 2003
691
1
168
Seattle Washington
cPanel Access Level
Root Administrator
Hi,

I want to create an event handler that would be called on a couple of different events:

- add on domain created/deleted
- mysql user created/deleted
- mysql db created/deleted

These events would call an external API donig a regular get/post web request.

Can you guys please advise on how I can get started, where I put the files, how I tell cpanel to hook into my files, etc ?

The documentation is a little convoluted and hard to understand in this regard.

Thanks
 

cPanelDavidN

Well-Known Member
Staff member
Dec 17, 2009
571
3
68
Houston, TX
cPanel Access Level
Root Administrator
Hi B12Org,

You mention that you need a CustomEventHandler. Have you considered Function Hooks? Those are the two methods to hook into cPanel events

CustomEventHandlers
Function Hooks

Function Hooks will be fired as an individual process everytime that particular event is invoked in cPanel. They are typically only used on API2 events so you might have mixed results since the MySQL calls you're wanting to fire on are API1. I haven't made a Function Hook in awhile, so I can't speak to your expected success with those particular API1 calls. Function Hooks have the advantage of being language agnostic. You'd create one, say for Mysql::adddb, by make /usr/local/cpanel/hooks/mysql/adddb. On the shebang line, set whatever scripting language you need; script away. You should checkout /usr/local/cpanel/hooks/README. It shouldn't be too difficult to write a test.

CustomEventHandlers are written in Perl. So, unless you're familar with Perl, this may not be the right method for you. That said, there are examples that should help you if have beginner to intermediate Perl skills. If you create one, /usr/local/cpanel/Cpanel/CustomEventHandler.pm, every event that the system performs will pass through it. In that script you'd grep for the particular events (Mysql adddb, etc) and do the desired call to the external API. Each cPanel event will pass through the CustomEventHandler twice: once be for the event is performed, and once after. This gives fine grain control if you need to terminate an event prematurely or munge the data before/after the event.

Some examples of CustomEventHandlers:
/usr/local/cpanel/Cpanel/CustomEventHandler.pm.sample
API Tracer, used to debug and log API calls to error_log
Email Blacklist, an example of blacklist functionality implemented at the event handling level
 

B12Org

Well-Known Member
Jul 15, 2003
691
1
168
Seattle Washington
cPanel Access Level
Root Administrator
so If I want to create one for Adding and Removing of a domain I am guessing it would be

/usr/local/cpanel/hooks/AddonDomain/addaddondomain
/usr/local/cpanel/hooks/AddonDomain/deladdondomain

??

In this xml sample on that site
Function Hooks

PHP:
<xml>
	<cpanelevent>
		<errors></errors>
		<event>addpop</event>
		<module>email</module>
		<params>
			<param0>emailuser</param0>
			<param1>emailpassword</param1>
			<param2>250</param2>
			<param3>testdomain.com</param3>
		</params>
	</cpanelevent>
	<CPDATA>
		<DOMAIN>testdomain.com</DOMAIN>
		<USER>testdoma</USER>
	</CPDATA>
</xml>
its specifically for email accounts - but I am guessing that the CPDATA nodes will always be there for any action - but the cpanelevent nodes will be different each time right?

is there a way to get a list of what nodes will come back, what paramaters are passed in, in what order so that I can properly get the values and create an association?

Is it safe to assume that if there is nothing in the errors node that the request was successfull - or will it always be sucessfull if it gets to this point and calls my hook ?


PHP:
	$xml = "";
	
	//read in STDIN and convert to XML String
	$stdin_fh = fopen('php://stdin', 'r');
	while ($line = fgets( $stdin_fh )) {
		$xml .= $line;
	}
	fclose($stdin_fh);

	/*
	<xml>
		<cpanelevent>
			<errors></errors>
			<event>addpop</event>
			<module>email</module>
			<params>
				<param0>emailuser</param0>
				<param1>emailpassword</param1>
				<param2>250</param2>
				<param3>testdomain.com</param3>
			</params>
		</cpanelevent>
		<CPDATA>
			<DOMAIN>testdomain.com</DOMAIN>
			<USER>testdoma</USER>
		</CPDATA>
	</xml>
	*/	

	$xml_string = new SimpleXMLElement($xml);
	$eventName = $xml_string->xml->cpanelevent->event;
	$moduleName = $xml_string->xml->cpanelevent->module;
	$param0 = $xml_string->xml->cpanelevent->params->param0;
	$param1 = $xml_string->xml->cpanelevent->params->param1;
	$param2 = $xml_string->xml->cpanelevent->params->param2;
	$param3 = $xml_string->xml->cpanelevent->params->param3;
 

B12Org

Well-Known Member
Jul 15, 2003
691
1
168
Seattle Washington
cPanel Access Level
Root Administrator
it doesnt seem to work - I took the change_password.example and renamed it to change_password and made sure it was set to 0755

I then put some code in it that creates a log file and dumps the XML to the file, and prints some value

the log file does not get created and nothing gets written to the page.

If I run the file manually it does create the log file although its empty becuase it gets stuck in reading the STDIN since I dont know how to manipulate that or send it data, but if I comment that and put in static XML and run the file manually it does work as expected - but it does *not* appear to being called from the cpanel when a user changes their password.
 
Last edited:

cPanelDavidN

Well-Known Member
Staff member
Dec 17, 2009
571
3
68
Houston, TX
cPanel Access Level
Root Administrator
Hi Scott,

Did you register the hook? It's kinda easy to miss in the documentation, I know I did when I went to double check that it was working this morning

per: Function Hooks
Important: In order for the function hook to take effect, you will need to run the following script: /usr/local/cpanel/bin/register_hooks
Also, be aware that these will fire at each call, regardless if the call itself was successful. You need to check the "status" of the call. And to further complicate it, API1 call are notorious for having "status" reflect the execution of the cPanel code path, and not the result (change in data state) of the action. Just something to consider if in your testing you find inconsistent results.
FYI: The change_password example illustrates how one might make a check of API2.

As far as parameter order: If the call is an API1, then it will be in the same order as required by the API1 call (the same order in which the arguements are listed in the documentation for that call). Otherwise, you should be able to grep on the parameter name.

Also, it would appear (by the given convention of the pre-established directories below /usr/local/cpanel/hooks/) that the module name does not follow the capitalization rules that Perl or the API interface require. i.e. addondomain, not AddonDomain. Just a guess if you're still having issues after registering the hooks.

Regards,
-DavidN
 

B12Org

Well-Known Member
Jul 15, 2003
691
1
168
Seattle Washington
cPanel Access Level
Root Administrator
David,

Thanks for the reply - I feel like a dolt now missing something so simple. That was teh culprit

Additionally it also output'ed
Code:
X-Powered-By: PHP/5.2.14 Content-type: text/html
to the page as well - can I not have it do that ?
 
Last edited:

cPanelDavidN

Well-Known Member
Staff member
Dec 17, 2009
571
3
68
Houston, TX
cPanel Access Level
Root Administrator
B12Org,

Firstly, I guess I called you Scott in my last post. I don't where that came from, but my apologies.

Try this:
Code:
// I suggest using the load string method, but both should work AFAIK
//$xml_string = new SimpleXMLElement($xml);
$xml_string = simplexml_load_string($xml);

// now reference all you elements relative from the root node
$pwchangestatus = $xml_string->cpanelevent->result->status;

// debug; yields SimpleXMLElement object in the dump; will envoke
// the magic method _toString() if referenced as a string
//var_dump($pwchangestatus);
X-Powered-By: PHP/5.2.14 Content-type: text/html
Where is that output being sent/see?

Regards,
-DavidN
 

B12Org

Well-Known Member
Jul 15, 2003
691
1
168
Seattle Washington
cPanel Access Level
Root Administrator
The xpowered by is showin up in the control panel, as the script being called does an echo or print like in the example change password file - it appears that if I set expose php to off it gets rid of the powered by, but the content type still shows up.

Everything else I got working, but now I need to try and do the mysql ones which are using the API1 - based on your last response that may prove to be a little harder :)

oh and btw - you can call me w/e you want :D
 

B12Org

Well-Known Member
Jul 15, 2003
691
1
168
Seattle Washington
cPanel Access Level
Root Administrator
So I created teh hooks and they do the same thing as the others, log to a file and make an external call.

Unfortunately these dont seem to be doing either of those things - additionally when I change password, do an addon domain, etc the pages take a little while becuase of that external call - however the db pages are exteremely quick, so I dont think its calling anything there either.

They did get registered
Code:
# /usr/local/cpanel/bin/register_hooks
Registered hook addondomain::addaddondomain
Registered hook addondomain::deladdondomain
Registered hook passwd::change_password
Registered hook mysql::adddb
Registered hook mysql::deluser
Registered hook mysql::deldb
Registered hook mysql::adduser
Registered 7 hooks
when I go to /frontend/x3/sql/addb.html and try and add a new db, delete that db, create a user and then add that user to the db and then delete the user it doesnt seem to call the mysql::* hooks as it doesnt write the files, and it doesnt throw any errors on the page (like it would if the log file directory doesnt exist for example)

any ideas ?

Code:
#pwd
/var/log/mylog
#ls -la hooks/*

hooks/addaddondomain:
total 8
drwxr-xr-x 2 root root 4096 Sep  3 11:03 .
drwxr-xr-x 9 root root 4096 Sep  3 11:02 ..
-rw-r--r-- 1 root wheel 1506 Sep  3 11:07 log-09-03-2010

hooks/adddb:
total 8
drwxr-xr-x 2 root root 4096 Sep  3 11:01 .
drwxr-xr-x 9 root root 4096 Sep  3 11:02 ..

hooks/adduser:
total 8
drwxr-xr-x 2 root root 4096 Sep  3 11:02 .
drwxr-xr-x 9 root root 4096 Sep  3 11:02 ..

hooks/deladdondomain:
total 8
drwxr-xr-x 2 root root 4096 Sep  3 11:03 .
drwxr-xr-x 9 root root 4096 Sep  3 11:02 ..
-rw-r--r-- 1 root wheel 1566 Sep  3 11:08 log-09-03-2010


hooks/deldb:
total 8
drwxr-xr-x 2 root root 4096 Sep  3 11:02 .
drwxr-xr-x 9 root root 4096 Sep  3 11:02 ..

hooks/deluser:
total 8
drwxr-xr-x 2 root root 4096 Sep  3 11:02 .
drwxr-xr-x 9 root root 4096 Sep  3 11:02 ..

hooks/passwd:
total 8
drwxr-xr-x 2 root root 4096 Sep  3 10:14 .
drwxr-xr-x 9 root root 4096 Sep  3 11:02 ..
 

cPanelDavidN

Well-Known Member
Staff member
Dec 17, 2009
571
3
68
Houston, TX
cPanel Access Level
Root Administrator
B12Org,

1) I haven't a clue why you're getting any output to the control panel. AFAIK, all STDOUT from the function hook should be trapped and not passed to the output buffer within the cPanel code execution. If I get chance, I try and investigate where I can.

2) At the risk of sounding silly, all your hooks have the execute bit set and a proper shebang line, right?

3) I wrote a sample PHP hook for Mysql::adddb. It's a little long, so I've attached it as a .txt file to the post, otherwise all 186 lines would just look aweful on the thread :p
It will log to /tmp/hookdump.txt file; check the script to change or add your own logging messages.

heck, I might even clean this up and make a little blog post some day ;)

Give it whirl, and let me know how it goes.

-DavidN

PS. if you what to give it a try without use the actual cPanel UI, you can just call this while in the same directory. It just push a XML string, similar to that generated by Mysql::adddb and pipes to the envoked hook.
Code:
echo '<xml><cpanelevent><params><param0>db1</param0></params><result><error></error></result></cpanelevent><CPDATA><USER>dave</USER></CPDATA></xml>'|php -f adddb
 

Attachments

B12Org

Well-Known Member
Jul 15, 2003
691
1
168
Seattle Washington
cPanel Access Level
Root Administrator
Thanks, I was able to get it working - I deleted and recreated the file and for some reason that fixed it, so it must have been a problem with the file somehow.