Perl crashes when script exits after closing database connection - windows

I have a Perl script that opens a ODBC connection to an Oracle database using DBI. Some data is selected, some work is done, no data is actually committed back to the database but I can't tell whether that makes any difference.
In the END block of the script the database is disconnected.
END
{
$$db->disconnect() if defined $$db;
exit 0;
}
Instead of exiting, perl.exe will crash with a fault in an Oracle DLL.
Faulting application name: perl.exe, version: 0.0.0.0, time stamp: 0x5407ac11
Faulting module name: OraOCIICUS19.dll_unloaded, version: 19.6.0.0, time stamp: 0x5e1181b2
Exception code: 0xc0000005
Fault offset: 0x0000000000df18b0
I tried adding a commit() method call before disconnect()ing but that didn't help.
Edit1: $$db is used throughout the script when calling a method on the object. I assume this is because the DBI connection is created within a module method.
my $db = MyModule::OpenDB();
my ( $foo, $bar ) = MyModule::GetFoo( $db );
my $rows = $$db->selectall_arrayref( $SQLStr, { Slice => {} } );
If I try to call a method on a single$ then I get the error Can't call method "commit" on unblessed reference
Edit2: the module that opens the DBI connection does so thusly:
sub OpenDB
{
my ($params) = #_;
my $db = DBI->connect( "dbi:ODBC:Driver={Oracle in instantclient_19_6};Dbq=MYDB", "MYUSER", "MYPASS", {AutoCommit => 0, RaiseError => 1, PrintError => 0, ShowErrorStatement => 1 } ) or croak $DBI::errstr;
$db->{LongReadLen} = 20480;
$db->{LongTruncOk} = 1;
return \$db;
}
I have already tried toggling AutoCommit on and off in case that was the source of any weirdness.

You don't need to disconnect the DB on script-exiting. Perl (DBI) will do this automatically for you when DB-handle gets out of scope.
Usually (if $db is an "un-weakend" reference to the $dbh-handle) this shouldn't have happened before so doing this should not throw any errors, but my guess is that the error stems from some other part of your code.
Anyway - removing the disconnect in the end-block is safe.

$$db is used throughout the script when calling a method on the object. I assume this is because the DBI connection is created within a module method.
Hi,
mhh, if youre really using DBI you maybe should take a look on your module.
thats how I do that:
sub mysql_connect($$$$) {
my ($f_database, $f_host, $f_user, $f_password) = (shift, shift, shift, shift);
my $f_handler = DBI->connect("DBI:mysql:database=$f_database;host=$f_host", $f_user, $f_password, {RaiseError=>0,PrintError=>1});
return $f_handler;
};
my $dbh = mysql_connect($db, $host, $user, $pass);
my $sql = "SELECT X FROM Y.Z";
my $stmt = $dbh->prepare($sql);
my $response = $stmt->execute();
while(my $rowref = $stmt->fetchrow_hashref()){
my %row = %$rowref;
for(keys %row){
print $_." => ".$row{$_}."\n";
}
}
$dbh->disconnect();
EDIT:
Try it out, but change mysql to oracle:
connect('DBI:Oracle:XE',"us01","us01")

Related

Collect a custom field data for a product at the time of order and termination

I'm currently working on some automation things in WHMCS. I've several custom products available in WHMCS:
Linux product 1
Linux product 2
Windows product 1
Windows product 2
I want to execute a bash script when an order accept and when the service terminate from the WHMCS. The script require an argument (IP address) which is a custom field for the above products. The script should fetch this custom field data containing IP address, and compare the products whether it is Linux or Windows and then pass it to the script as follows:
If the product is a Linux one, then the script pass would be like "autoaccept.sh linux [IP]"
If the product is Windows one, then the script call would be like "autoaccept.sh windows [IP]"
Similar way, when the package terminate on WHMCS we have to call the script again with "autoterminate.sh [IP]"
The WHMCS AcceptOrder and AfterModuleTerminate hook can be used I guess. But not sure how we can fetch these custom field data and compare the products within hook PHP code there. Can anyone shed some light on this or help our me to code this correctly.
Any responses would be much appreciated!
Created the Bash scripts already, and is working perfectly. I'm new to WHMCS hook and PHP things, so stucked here.
Use Product Modules: Product Module and implement the method _CreateAccount($params), You can find 'CustomFields' in $params Variable.
Here is example to executes python script:
<?php
function my_proc_execute($cmd)
{
$output='';
try
{
$descriptorspec = array(
0 => array("pipe", "r"), //STDIN
1 => array("pipe", "w"), //STDOUT
2 => array("pipe", "w"), //STDERR
);
$cwd = getcwd(); $env = null; $proc = proc_open($cmd, $descriptorspec, $pipes, $cwd, $env);
$buffer=array();
if(is_resource($proc))
{
fclose($pipes[0]);
stream_set_blocking($pipes[1], 1);
stream_set_blocking($pipes[2], 1);
stream_set_timeout($pipes[1],500);
stream_set_timeout($pipes[2],500);
while (!feof($pipes[1]))
{
$line = fread($pipes[1], 1024);
if(!strlen($line))continue;
$buffer[]=$line;
}
while(!feof($pipes[2]))
{
$line=fread($pipes[2], 1024);
if(!strlen($line))continue;
$buffer[]=$line;
}
$output=implode($buffer);
fclose($pipes[1]);fclose($pipes[2]);
$return_value = proc_close($proc);
}
else $output = "no resource; cannot open proc...";
}catch(Exception $e){$output = $e->getMessage();}
return $output;
}
function mymodule_CreateAccount($params)
{
$myData=$params['customfields']['the_name_of_field'];
$to_send_arr=array();
$to_send_arr['is_new']='0';
$to_send_arr['id']='xyz';
$to_send_arr['url']='my';
$to_send_arr['display_name']=$myData;
$exewin="c:/Python27/python.exe C:/xampp_my/htdocs/my/modules/servers/xxx/pyscripts/create_data.py";
$exelinux="/var/www/html/modules/servers/xxx/pyscripts/create_data.py";
$command=$exewin . ' ' . escapeshellcmd(base64_encode(json_encode($to_send_arr)));
$output=my_proc_execute($command);
$arr=json_decode($output,true);
}
?>
Python
import sys
import json
import time
import base64
if len(sys.argv) != 2:
print 'Error in the passed parameters for Python script.', '<br>'
sys.exit()
json_data = json.loads(base64.b64decode(sys.argv[1]))
id= json_data['id']

determining what ora_type values are supported

#!/usr/bin/perl
use diagnostics;
use DBI;
use DBI qw(:sql_types);
my $AIM_PSWD= $ENV{'AIM_PSWD'};
my $len=length($AIM_PSWD);
my $pwindex1=rindex($AIM_PSWD,'/');
my $pwindex2=rindex($AIM_PSWD,'#');
my $usr = substr($AIM_PSWD,0,$pwindex1);
my $pwd = substr($AIM_PSWD,$pwindex1+1,$pwindex2-($pwindex1+1));
my $db = substr($AIM_PSWD,$pwindex2+1,$len-($pwindex2+1));
my $attr = {AutoCommit => 0};
my $dbh;
my $qryHandle;
my $row;
my $query = "SELECT /*+ index(T_CAPITATION_HIST I_CAPITATION_HIST) */ SAK_CAPITATION FROM AIM.T_CAPITATION_HIST WHERE SAK_CAPITATION = ?";
$dbh = DBI->connect("dbi:Oracle:$db", $usr, $pwd, $attr)
or die "Unable to connect to $db\n";
$qryHandle = $dbh->prepare($query) or die "Unable to prepare query\n";
$qryHandle->bind_param(1, "765756556", {ora_type => SQL_VARCHAR});
$qryHandle->execute() or die "Unable to execute query\n";
if ($row = $qryHandle->fetchrow_hashref())
{
print "query succeeded\n";
}
else
{
print "query failed\n";
}
$qryHandle->finish();
$dbh->disconnect();
exit(0);
Running this produces the error message from the bind_param statement:
Uncaught exception from user code:
Can't bind :p1, ora_type 12 not supported by DBD::Oracle at ./test.pl line 26.
How do I determine what ora_type values are supported?
The available ora_type values are listed at https://metacpan.org/pod/DBD::Oracle#:ora_types and described at https://metacpan.org/pod/DBD::Oracle#ora_type, and must be imported from DBD::Oracle like:
use DBD::Oracle ':ora_types';
If you want to pass a standard SQL type that you imported from DBI :sql_types, just pass it directly as the bind type:
$qryHandle->bind_param(1, "765756556", SQL_VARCHAR);
See https://metacpan.org/pod/DBD::Oracle#bind_param.

PowerShell Pro Tools multiple forms not running as expected

I am using PowerShell Pro Tools to create a GUI application that consists of all the common scripts I would run on a clients server:
main.ps1
main.ps1 loads a ServerConnection form on load:
The code behind this is pretty basic, it just gets the database name and server address for an SQL server:
$btnConfirm_Click = {
$ServerConnectForm.Close();
}
$btnTest_Click = {
## Set database connection variables [global]
$Global:databaseName = $cmbDatabaseName.Text;
$Global:serverAddress = $txtServerAddress.Text;
## Check db connection
$testResult = Invoke-SqlCmd -serverInstance $serverAddress -Database $databaseName -Query "SELECT TOP 1 SettingName FROM Settings";
## Write results to user
if ( $testResult -ne $null ) {
$lblTestResult.ForeColor = "#2acc18";
$lblTestResult.Text = "Test connection successfull";
<# If test connection success enable confirm button #>
$btnConfirm.Enabled = $true;
}
else {
$lblTestResult.ForeColor = "#d61111";
$lblTestResult.Text = "Test connection failed";
}
}
$txtServerAddress_Leave = {
## Get TRIS database list
$databaseList = Invoke-Sqlcmd -ServerInstance $txtServerAddress.Text -Query "
SELECT name FROM sys.databases WHERE CASE WHEN state_desc = 'ONLINE' THEN OBJECT_ID(QUOTENAME(name) + '.[dbo].[settings]', 'U') END IS NOT NULL
"
## Clear combo box
$cmbDatabaseName.Items.Clear();
## Add to combo box
foreach ($database in $databaseList) {
$cmbDatabaseName.Items.Add($database.ItemArray[0]);
}
}
. (Join-Path $PSScriptRoot 'main.designer.ps1')
$MainForm.ShowDialog()
The problem is that when I either compile this into an executable or run main.ps1 directly from the project folder, none of the code outside of main.ps1 works. The form will show up but I cannot find a way to get the code behind the form to work. For example in the ServerConnection form, adding a server address does not populate the database names and the test connection button does nothing.
Running from within Visual Studio works as intended.
Any help on this would be greatly appreciated.
EDIT :: Show the server connection form call in main.ps1
MainForm_Load
$MainForm_Load = {
## Launch server connection form
. (Join-Path $PSScriptRoot 'ServerConnect.designer.ps1');
$ServerConnectForm.ShowDialog();
## Call prereq analysis
PrereqAnalysis
}
It might be an issue with the scoping of your code.
If code outside the current scope of a session depends on said session, it will not work.
You could try setting the scope of variables and functions to global while you troubleshoot to see if it makes a difference, then change it back until you find where the scope goes wrong.
Microsoft have a good MSDoc page about Powershell scopes

Perl service status codes from Win32::Service module GetStatus() call do not match that of Services.msc applet

I have been trying to write a Perl script that would check the service status of Remote Servers. I am using the Win32::Service module to accomplish my goal.
I find that the for some services value returned by Win32::Service::GetStatus CurrentStatus is not exactly the same as observed from services.msc applet.
Here is the piece of script I am using along with both outputs from script and servics.msc.
use Data::Dumper;
use Win32;
use Win32::Service;
use strict;
use warnings;
my %statcodeHash = ( '1' => 'stopped.',
'2' => 'start pending.',
'3' => 'stop pending.',
'4' => 'running.',
'5' => 'continue pending.',
'6' => 'pause pending.',
'7' => 'paused.' );
my #serviceNames = qw(NNMAction RpcEptMapper smstsmgr SNMPTRAP);
foreach my $serv (#serviceNames)
{ my %status;
my $ret = Win32::Service::GetStatus('nnmi.hclt.corp.hcl.in', $serv, \%status);
if ($ret)
{ print "success\t$statcodeHash{$status{CurrentState}}\t$serv\n";
}
else
{ print Win32::FormatMessage(Win32::GetLastError()), "\n";
}
}
OUTPUT from Script
D:\AVI MEHENWAL\PERL\SCRIPTS\PROJECTS\Serve Management>perl -w perl_RemoteServiceStatus.pl
success stopped. NNMAction
success running. RpcEptMapper
success stopped. smstsmgr
success stopped. SNMPTRAP
OUTPUT from Services.msc
Name Descrition Status StartupType LogOnAs
NNMAction bla bla bla Started Manual LocalSystem
Can anyone suggest me what approach should I follow to achieve my goal, Is my method wrong or there is something I am missing from Perl module point of view?
Updated
I noticed that nnmi.hclt.corp.hcl.in seems to be a domain name rather than a Windows style machine name. First, find out the IP address corresponding to nnmi.hclt.corp.hcl.in. Let's say it is x.x.x.x.
Now, run nbtstat -A x.x.x.x. That should tell you the name of the machine. Let's say it is NNMI. Then, you should specify that in the my $remote line below, replacing MYREMOTE with NNMI, and try again.
I am assuming you have the correct privileges etc. If you get an authorization related error, I am afraid I cannot help you, but you can ask on ServerFault to find out what you need to do so as to be able to query services on a remote machine in the domain.
use strict;
use warnings;
use Data::Dumper;
use Win32;
use Win32::Service;
my %status_codes = reverse (
SERVICE_STOPPED => 0x00000001,
SERVICE_START_PENDING => 0x00000002,
SERVICE_STOP_PENDING => 0x00000003,
SERVICE_RUNNING => 0x00000004,
SERVICE_CONTINUE_PENDING => 0x00000005,
SERVICE_PAUSE_PENDING => 0x00000006,
SERVICE_PAUSED => 0x00000007,
);
my $remote = '\\\\MYREMOTE';
my #services = qw(Fax ClipSrv AcrSch2Svc);
foreach my $svc (#services) {
my %status;
my $ret = Win32::Service::GetStatus($remote, $svc, \%status);
if ($ret) {
printf(
"success\t%s\t%s\n",
$status_codes{ $status{CurrentState} },
$svc
);
}
else
{
warn sprintf(
"failed to get '%s' status: %s\n",
$svc,
Win32::FormatMessage(Win32::GetLastError())
);
}
}
For reference, the SERVICE_STATUS structure is documented here.

Will this kind of bash work as expected?

#!/bin/bash
MYSQL="/usr/bin/mysql -uroot "
function create_db()
{
local db_name=${1}
$MYSQL<<!
create database IF NOT EXISTS ${db_name};
!
}
###-------------------------tb_bind_userid_xxx-------------------------------------
function create_table_userid
{
$MYSQL << !
create table if NOT EXISTS bind_pay.tb_bind_userid_${1}(
b_userid bigint not null,
b_membercode bigint not null ,
PRIMARY KEY (b_userid)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
!
}
Will $MYSQL be persistent across function calls,or reconnect each time?
If it reconnects every time,I don't think create_table_userid will work as expected though,because it hasn't specify a database name yet.
Well, because you will be calling the function each time you want to create the table, you will call mysql to connect to the database each time. If you want persistent connection, one way is to use a mysql library that is supported by most major programming languages these days, Perl/Python/Ruby/PHP etc. You can make a DB connection, then do whatever stuff you want, then finally close the connection. for example in the documentation of Python/Mysql
import MySQLdb
conn = MySQLdb.connect (host = "localhost",
user = "testuser",
passwd = "testpass",
db = "test")
cursor = conn.cursor ()
cursor.execute ("SELECT VERSION()")
row = cursor.fetchone ()
print "server version:", row[0]
cursor.close ()
conn.close ()
As you can see, a connection conn is opened to connect to database. Then using the connection handle (or rather database handle), stuff are done and then finally, the connection is closed.
$MYSQL is just a variable, so your code runs mysql each time it calls one of these functions.
You can create a persistant connection to mysql easily enough; just have your program write its sql to output, then pipe the whole results into mysql:
(
create_table "foo"
create_table "bar"
) | mysql
create_table() {
cat <<!
create table $1 ....
!
}

Resources