SOLVED Can't locate Cpanel/Logger.pm

Matt Paplham

Member
Apr 12, 2019
11
3
3
WI - USA
cPanel Access Level
Root Administrator
Hi All,

I've created a hook.pm that automates some of the things I do after I create a cPanel account. It compiled and registered without issue.

However, when I create an account, none of the actions are completed. So I need to trace through my code to see whats going on.

I tried to add the cPanel::Logger module, but get a compile error:

Can't locate Cpanel/Logger.pm in @INC (@INC contains: /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .)

I tried sudo yum install perl-Logger since it worked for the JSON.pm that I also needed, but that didn't work.

I also checked WHM>>Software>>Install a Perl Module, but it wasn't there either.

Any ideas?
 

kdub

Active Member
Apr 19, 2019
29
8
3
California
cPanel Access Level
Root Administrator
Logger is a module specific to Cpanel and should be automatically loaded by cpanel core. I believe it's located at /usr/local/cpanel/Cpanel/Logger.pm

You can enable debugging in WHM >> Home >> Server Configuration >> Tweak Settings and tail /usr/local/cpanel/logs/error_log to see what's happening. Guide to Standardized Hooks - Debug Mode - Developer Documentation - cPanel Documentation

Is this a Perl module or script? Does your logging code look like this?
Code:
# Use cPanel's error logging module.
use Cpanel::Logger;
my $logger = Cpanel::Logger->new();
$logger->info("message to log");
Did you save your hook in the right place as per Tutorial - Create a Standardized Hook - Developer Documentation - cPanel Documentation
Save the file.
Save your hook action code to one of the following locations on your cPanel & WHM server:
  • /usr/local/cpanel
  • /usr/local/cpanel/3rdparty/perl/514/lib64/perl5/cpanel_lib/x86_64-linux-64int
  • /usr/local/cpanel/3rdparty/perl/514/lib64/perl5/cpanel_lib
  • /usr/local/cpanel/3rdparty/perl/514/lib64/perl5/5.14.4/x86_64-linux-64int
  • /usr/local/cpanel/3rdparty/perl/514/lib64/perl5/5.14.4
  • /opt/cpanel/perl5/514/site_lib/x86_64-linux-64int
  • /opt/cpanel/perl5/514/site_lib
  • /var/cpanel/perl5/lib
  • /var/cpanel/perl5/lib
Important:

You must save hook action code in one of these locations, or you will receive an @INC error when you attempt to register the hook.

Also, Guide to Standardized Hooks - Debug Mode - Developer Documentation - cPanel Documentation states the following at the bottom:
Can't locate Module.pm in @INC
You may receive a Can't locate Module.pm in @INC warning.

This error may occur for one of the following reasons:

  • If your server runs cPanel & WHM version 11.44 and earlier, a module hook claims a syntax error, but the code passes the perl -c command, which checks the syntax of the program. This issue occurs because cPanel & WHM's internal Perl does not automatically use the same version of Perl that your system's Perl uses.
  • If your server runs cPanel & WHM version 11.46 and later, it is likely that the module is not installed to the cPanel-included Perl environment on your server. To troubleshoot this problem, run the /usr/local/cpanel/3rdparty/bin/perl -c /example/script command, where /example/script is the location of the module file.
For more information about cPanel & WHM's Perl environments, read our Guide to Perl in cPanel & WHM documentation.

For more information about the perl -c command, read the perlrun perldoc.
 

Matt Paplham

Member
Apr 12, 2019
11
3
3
WI - USA
cPanel Access Level
Root Administrator
kdub - Thanks for your quick reply.

I think my problem is that perl -c is not looking in /usr/local/cpanel/Cpanel for Cpanel::Logger.
Code:
[[email protected] ~]# perl -c /usr/local/cpanel/OnCreate.pm
Can't locate Cpanel/Logger.pm in @INC (@INC contains: /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .) at /usr/local/cpanel/OnCreate.pm line 12.
At any rate, it fails perl -c, but it does let me register the hook.
Code:
[[email protected] ~]# /usr/local/cpanel/bin/manage_hooks add module OnCreate
Added hook for Whostmgr::Accounts::Create to hooks registry
[[email protected] ~]# /usr/local/cpanel/bin/manage_hooks list
Whostmgr:
        Accounts::Create:
                stage: post
                weight: 100
                exectype: module
                hook: OnCreate::add
                blocking: 0
                id: Na5nNZBe5nFJzvaakxq1OYKw
Still no luck actually getting the hook to work. When I access the error log
Code:
[[email protected] ~]# nano /usr/local/cpanel/logs/error_log
I get
Code:
[2019-04-25 15:06:02 -0400] warn [whostmgr5] Can't call method "uapi" on an undefined value at /usr/local/cpanel/OnCreate.pm line 44.
That correlates to this block of code:
Code:
# Create the [email protected] email address.
    my $new_support_email = $cpliveapi->uapi(
        'Email', 'add_pop',
        {
            'email'           => 'support',
            'password'        => '********',
            'quota'           => '0',
            'skip_update_db'  => '1',
        }
    );
Earlier in the code I have:
Code:
# LiveAPI module
use LiveAPI;

# Connect to cPanel - only do this once.
my $cpliveapi = Cpanel::LiveAPI->new();
Not sure why I am getting that error since all required parameters are there.
 

kdub

Active Member
Apr 19, 2019
29
8
3
California
cPanel Access Level
Root Administrator
It would probably help if you posted your complete script.

'use LiveAPI;' would need to be 'use Cpanel::LiveAPI();' you would also need to deconstruct it with $cpliveapi->end(); after you're done with it. Either way, I don't believe you can utilize LiveAPI locally in perl hook modules. Or at least I haven't found any working examples.

I believe all the references to the LiveAPI code in the cpanel documentation are talking about code in a LiveAPI perl module and not an action hook module. Guide to the LiveAPI System - Perl Module - Developer Documentation - cPanel Documentation

From what I've read, calls to the LiveAPI from an action script need to be done externally. Unfortunately, the only example I could find is from 3 years ago: Create database(LiveAPI) inside Perl Module Standardized Hook

A fresh example of LiveAPI in a hook would be amazing?!

I would think you could move your LiveAPI code to an actual LiveAPI perl module and call that module from your hook module but I have no idea how that would be done. In fact, the documentation is clear as mud as to how these LiveAPI perl modules are actually called or used. These examples would be much appreciated as well.

By the way, to save you the time of looking at the example LiveAPI cpanel provides:
Code:
 # /usr/local/cpanel/base/frontend/paper_lantern/integration_examples/example.live.pl
#!/usr/local/cpanel/3rdparty/bin/perl
# cpanel - base/frontend/paper_lantern/test.live.pl
#                                                    Copyright 2014 cPanel, Inc.
#                                                           All rights Reserved.
# [email protected]                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited

use strict;

use Cpanel::LiveAPI ();

my $cpanel = Cpanel::LiveAPI->new();

print "Content-type: text/html\r\n\r\n";

print $cpanel->header('Example perl Page');

print "We recommend that you create a new template toolkit file and place it in the paper lantern theme's root directory.";

print $cpanel->footer();

$cpanel->end();
 

Matt Paplham

Member
Apr 12, 2019
11
3
3
WI - USA
cPanel Access Level
Root Administrator
Thanks for all the help so far. I've spent a lot of time reading the documentation and searching for examples on the web but am also turning up empty handed.

If you can't use LiveAPI Perl Modules from hooks, what version of the UAPI should a person use? Or you can't use UAPI at all?

I figured since the hook was a Perl Module, LiveAPI Perl Modules would make sense. But again, this is my first time making one. And it did seem too easy at the beginning...

Here is what I am trying to do with a post account create hook:
  • Compress website data
  • Create a database
  • Create a database user
  • Set user privileges for database
  • Create [email protected] email
  • Encrypt that email
  • Create [email protected] email
  • Encrypt that email
This was my first pass at creating the hook Perl Module. I basically copied and pasted from the documentation and added the LiveAPI functions for each of the above in the #insert your actions here.

Code:
# Package this module.
package AcctCreationHook;

# Return errors if Perl experiences problems.
use strict;
use warnings;

# Properly decode JSON.
use JSON;

# LiveAPI module
use LiveAPI;

# Embed hook attributes alongside the action code.
sub describe {
    my $my_add = {
        'category' => 'Whostmgr',
        'event'    => 'Accounts::Create',
        'stage'    => 'post',
        'hook'     => 'AcctCreationHook::add',
        'blocking' => 0,
        'exectype' => 'module',
    };
 
    return [ $my_add ];
}

sub add {
    # Get the data that the system passes to the hook.
    my ( $context, $data ) = @_;              
 
    # Set success and failure messages.
    my $result  = 0;                             # This Boolean value is set to fail.  
    my $message = 'This is an error message.';   # This string is a reason for $result.
   
    # Connect to cPanel - only do this once.
    my $cpliveapi = Cpanel::LiveAPI->new();
   
    # Compress website data
    # This is commented out because it is causing a compile error
    # my $configure_mod_deflate = $cpliveapi->api1(OptimizeWS, optimizews ['all', 'text/html',]);
   
    # Create a new database.
    my $create_db = $cpliveapi->uapi(
        'Mysql', 'create_database',
        {
            'name'    => 'wp500',
        }
    );
   
    # Create a new database user.
    my $create_db_user = $cpliveapi->uapi(
        'Mysql', 'create_user',
        {
            'name'       => 'wp500',
            'password'   => 'password',
        }
    );
   
    # Set the user's privileges for the database.
    my $set_dbuser_privs = $cpliveapi->uapi(
        'Mysql', 'set_privileges_on_database',
        {
            'user'       => 'wp500',
            'database'   => 'wp500',
            'privileges' => 'ALL PRIVILEGES',
        }
    );
   
    # Create the [email protected] email address.
    my $new_support_email = $cpliveapi->uapi(
        'Email', 'add_pop',
        {
            'email'           => 'support',
            'password'        => 'password',
            'quota'           => '0',
            'skip_update_db'  => '1',
        }
    );
   
    # Encrypt [email protected]
    my $generate_key = $cpliveapi->api1('Gpg', 'genkey', [$new_support_email, $new_support_email, $new_support_email, '0y', '2048', 'password'] );
   
    # Create the [email protected] email address.
    my $new_office_email = $cpliveapi->uapi(
        'Email', 'add_pop',
        {
            'email'           => 'office',
            'password'        => 'password',
            'quota'           => '0',
            'skip_update_db'  => '1',
        }
    );
   
    # Encrypt [email protected]
    my $generate_key = $cpliveapi->api1('Gpg', 'genkey', [$new_office_email, $new_office_email, $new_office_email, '0y', '2048', 'password'] );
   
    my $cpliveapi->end();
 
 
    # Return the hook's result and message.
    return $result, $message;
}
 
1;
 

kdub

Active Member
Apr 19, 2019
29
8
3
California
cPanel Access Level
Root Administrator
All this is a bit above my experience level so I'm afraid I don't have a clear answer for you. As I mentioned, I think you can only use Cpanel::LiveAPI in LiveAPI modules. I don't think you can use Cpanel::LiveAPI in action hooks directly. Based on what I've found you would need to call the UAPI externally. cpanel gurus will have to provide some clarification and hopefully some examples of how this is done. Ideally, rather than call uapi externally from action hooks, you could put the uapi code in a LiveAPI perl module and bring it into your action hook so you don't have to mess with all the overhead of making webrequests. But I need someone to show us how that could be done.

Here's an example of external uapi requests from Tutorial - Call UAPI's SSL::install_ssl Function in Custom Code - Developer Documentation - cPanel Documentation
Code:
#!/usr/local/cpanel/3rdparty/bin/perl
 
# Return errors if Perl experiences problems.
use strict;
use warnings;
 
# Allow my code to perform web requests.
use LWP::UserAgent;
use LWP::Protocol::https;
 
# Use the correct encoding to prevent wide character warnings.
use Encode;
use utf8;
 
# Properly decode JSON.
use JSON;
 
# Function properly with Base64 authentication headers.
use MIME::Base64;
 
# Authentication information.
my $username = 'username';
my $password = '12345luggage';
 
# The URL for the SSL::install_ssl UAPI function.
my $request = "https://localhost:2083/execute/SSL/install_ssl";
 
# Required to allow HTTPS connections to unsigned services.
# Services on localhost are always unsigned.
$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0;
 
# Create a useragent object.
my $ua = LWP::UserAgent->new();
 
# Add authentication headers.
$ua->default_header(
    'Authorization' => 'Basic ' . MIME::Base64::encode("$username:$password"),
);
 
# Read in the SSL certificate and key file.
my ( $cert, $key );
{
    local $/;
    open ( my $fh, '<', '/path/to/certificate.crt' );
    $cert = <$fh>;
    close $fh;
    
    open ( $fh, '<', '/path/to/key.key' );
    $key = <$fh>;
    close $key;
}
 
# Make the call.
my $response = $ua->post($request,
    Content_Type => 'form-data',
    Content => [
        domain => 'example.com',
        cert   => $cert,
        key    => $key,
    ],
);
 
# Create an object to decode the JSON.
# Sorted by keys and pretty-printed.
my $json_printer = JSON->new->pretty->canonical(1);
 
# UTF-8 encode before decoding to avoid wide character warnings.
my $content = JSON::decode_json(Encode::encode_utf8($response->decoded_content));
 
# Print output, UTF-8 encoded to avoid wide character warnings.
print Encode::encode_utf8($json_printer->encode($content));
 
  • Like
Reactions: cPanelMichael

cPanelMichael

Administrator
Staff member
Apr 11, 2011
47,908
2,216
463
Hello @Matt Paplham,

The previous post is correct. Here's some more information about why you should not use LiveAPI in the example you provided:

Problem with API call

Let me know if this information and the information in the previous post is helpful.

Thank you.
 

kdub

Active Member
Apr 19, 2019
29
8
3
California
cPanel Access Level
Root Administrator
Ideally, rather than call uapi externally from action hooks, you could put the uapi code in a LiveAPI perl module and bring it into your action hook so you don't have to mess with all the overhead of making webrequests. But I need someone to show us how that could be done.
@cPanelMichael Is something like I mention in the above quote possible? Can an action hook utilize a custom liveAPI perl module or is external api requests our only option? Thanks.
 

Matt Paplham

Member
Apr 12, 2019
11
3
3
WI - USA
cPanel Access Level
Root Administrator
@cPanelMichael - Are you able to share any hook.pm examples using uapi for the hook actions? The tutorial doesn't use uapi/api2/api1 functions at all, just File::Copy.

The Guide to Standardized Hooks - Hook Action Code equally does not show how to use uapi/api2/api1 functions, simply:
# Insert your actions here.

I feel like if one example was shared, I could figure out how to implement the rest.

Problem with API call explains why I can't use the live api environment - makes sense it is only for cPanel apps. It doesn't explain how to use uapi/api2/api1 functions in hook, which ultimately is my problem.

Problem with API call also gives various examples of using uapi in PHP, but nothing for Perl.

Thanks for your patience as I learn this stuff.
 

cPanelMichael

Administrator
Staff member
Apr 11, 2011
47,908
2,216
463
Hello,

When creating a hook, generally the first step is to find the corresponding event that you want to hook into. To find this information, enable "Debug mode is on. The system displays information about every stage for every hookable event, even if no hooks exist for that stage." under the "Development" tab in WHM >> Tweak Settings. Then, perform the specific action you want to hook into (e.g. create a new account in WHM) and watch the output in /usr/local/cpanel/logs/error_log.

To test this, create a new addon domain from the cPanel UI after turning debug mode on. You'll see from the output to /usr/local/cpanel/logs/error_log that the following is the hook event to utilize when creating a hook for the addition of a new domain using cPanel >> Addon Domains:

Cpanel::Api2::AddonDomain::addaddondomain

Next, you'll want to create/write the hook action code that will run automatically "pre" or "post" the hook event described above. Your hook action code can be in any language that the Linux shell can execute (e.g. bash, Perl, PHP). In the resources below, you'll see how to write a real hook action code in bash:

Tutorial - How to Setup Standardized Hooks with BASH in cPanel & WHM
Tutorial - How To Automatically Install WordPress On New Accounts

Hook action code as a script cannot access cPanel environment variables.

Hook action code in a custom Perl module can access cPanel environment variables. For steps to create a hook action code Perl module, read our Create a Standardized Hook tutorial to see how the example Perl module below was created:

Code:
#!/usr/bin/perl
 
# Package this module.
package MyHook;
 
# Return errors if Perl experiences problems.
use strict;
use warnings;
 
# Use cPanel's error logging module.
use Cpanel::Logger;
 
# Use the core Perl module with file-copying functionality.
use File::Copy;
 
# Properly decode JSON.
use JSON;
 
# Instantiate the cPanel logging object.
my $logger = Cpanel::Logger->new();
 
# Embed hook attributes alongside the action code.
sub describe {
    my $hooks = [
        {
            'category' => 'System',
            'event'    => 'upcp',
            'stage'    => 'pre',
            'hook'     => 'MyHook::copyfile',
            'exectype' => 'module',
        },{
            'category' => 'System',
            'event'    => 'upcp',
            'stage'    => 'post',
            'hook'     => 'MyHook::replacefile',
            'exectype' => 'module',
        }
    ];
    return $hooks;
}
 
# Set the file to preserve and the temp file location.
my $preserve = "/usr/local/cpanel/base/horde/turba/config/backends.php";
my $temp = "/usr/local/cpanel/base/horde/turba/config/backends.php.temp";
 
# Before upcp, copy the file to the temp file.
sub copyfile {
    # Get the data that the system passes to the hook.
    my ( $context, $data ) = @_;
 
    # Add a log entry for debugging.
    $logger->info("***** Copy my file before upcp *****");
 
    # Copy my file.
    copy($preserve,$temp) or die "Copy failed: $!";
};
 
# After upcp, copy the file back into place.
sub replacefile {
    # Get the data that the system passes to the hook.
    my ( $context, $data ) = @_;
 
    # Add a log entry for debugging.
    $logger->info("***** Replace the temp file *****");
 
    # Replace my file.
    copy($temp,$preserve) or die "Replacement failed: $!";
 
    # Add a log entry for debugging.
    $logger->info("***** Delete the temp file *****");
 
    # Delete the temp file to keep cruft off my system.
    unlink($temp) or die "Cruft removal failed: $!";
};
 
1;
Note that you may need to run the "yum install perl-DBD-MySQL" command to address the "Can't locate Cpanel/Logger.pm in @INC" error message you reported. If that doesn't help, can you open a support ticket letting us know that you are following all of the example steps in our documentation and that it's not working? While we can't assist with creating the custom script, we can run through the example steps in a support ticket to make sure everything is working on your server the way we document it.

Thank you.
 

cPanelMichael

Administrator
Staff member
Apr 11, 2011
47,908
2,216
463
Hello @Matt Paplham,

That's great! I'm glad to see you were able to implement the hook using BASH.