[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[coldsync-hackers] Problem with Sync Conduit
- To: coldsync-hackers at lusars dot net
- Subject: [coldsync-hackers] Problem with Sync Conduit
- From: Marco van Beek <marco at 84andahalf dot com>
- Date: Thu, 17 Jun 2004 17:23:54 +0100
- Reply-to: coldsync-hackers at lusars dot net
- Sender: owner-coldsync-hackers at lusars dot net
- User-agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.5) Gecko/20031013 Thunderbird/0.3
Hi Guys,
I am probably doing something stupid. I am getting to following in the
coldsync log when I try to run my prototype sync conduit.
Inside mkpdbname("/opt/ssis/coldsync/users/mvanbeek/Device3/backup","MemoDB")
mkpdbname: -> "/opt/ssis/coldsync/users/mvanbeek/Device3/backup/MemoDB.pdb"
Undefined subroutine &main::dlp_ReadNextModifiedRec called at /opt/ssis/coldsync/conduits/ssis-sync.pl line 192.
Undefined subroutine &main::dlp_ReadNextModifiedRec called at /opt/ssis/coldsync/conduits/ssis-sync.pl line 192.
Running the program on the command line with the -config flag shows no
compiler errors, and I have checks ColdSync::SPC to check the spelling
is correct.
I have "use ColdSync::SPC" at the start of the program (which is
attached) and at the moment, all the program does at the moment is do
the logic of finding records that have changed.
"dlp_SetSysDateTime" seems to crash the Palm, (which might be badly
configured data), but doesn't cause an error message like below.
There are two copies of ColdSync::SPC on the hard drive, but both are
identical (one is where the snapshot zip file was uncompressed).
I am very confused (or else I am doing something very stupid).
Any help would be appreciated,
Regards,
Marco.
#!/usr/bin/perl
##################
# Copyright, etc #
##################
#########
# NOTES #
#########
# '$dbh' refers to the MySQl connection via DBI
# '$pdb' refers to the PDA database coonection via Coldsync
# Die() is a Coldsync function that passes the STDOUT error back to Coldsync so that it appears in the Coldsync log.
# Log levels
# ----------
# The higher the number the more detailed the logging
# 0: Application is unusable (EG: program crash)
# 1: Application is seriously flawed (EG: Unable to connect to PDA or database)
# 2: Critical conditions (Unable to write to database or PDA)
# 3: Errors (EG: Conflict resolution failed)
# 4: Warnings (EG: Conflict resolution solved)
# 5: Normal but significant (default level) (EG: Start of conduit)
# 6: informational (EG: id of each record as looped through)
# 7: Debug-level messages (EG: Full SQL code)
#################
# Start Of Code #
#################
use strict;
use ColdSync;
use ColdSync::SPC;
use Palm::StdAppInfo;
use DBI;
use Log::LogLite;
####################
# Set up Variables #
####################
my %settings;
open(CONFIG,"/etc/ssis-config");
while(<CONFIG>)
{
chomp; # no newline
s/#.*//; # no comments
s/^\s+//; # no leading whitespace
s/\s+$//; # no trailing whitespace
next unless length; # Anything left?
my ($var, $value) = split(/\s*=\s*/,$_, 2);
$settings{$var} = $value;
}
close CONFIG;
my $mysql_datasource = $settings{"mysql_datasource"};
my $mysql_username = $settings{"mysql_username"};
my $mysql_password = $settings{"mysql_password"};
my $mysql_palm_gluetable = $settings{"mysql_palm_gluetable"};
my $public_user = $settings{"public_user"};
my $Memo_user = $settings{"Memo_user"};
my $ToDo_user = $settings{"ToDo_user"};
my $Address_user = $settings{"Address_user"};
my $Datebook_user = $settings{"Datebook_user"};
################
# Main Conduit #
################
StartConduit("sync");
# We need the following (for example) in the .coldsyncrc file
#
# conduit sync {
# path: "/opt/ssis/coldsync/conduits/ssis-sync.pl";
# type: memo/DATA;
# arguments:
# ssisuser: mvanbeek;
# deviceID: 3
# logfile: /opt/ssis/coldsync/users/mvanbeek/Device3/ssis-sync-memo.log;
# loglevel: 5;
# synctarget: Memo;
# }
# Convert the headers to variables
my $ssisuser = $HEADERS{ssisuser}; # Current User
my $deviceID = $HEADERS{deviceID}; # Current PDA as identified by Coldsync
my $logfile = $HEADERS{logfile}; # Where do I save my logfile
my $loglevel = $HEADERS{loglevel}; # Log level, default should be 5
my $synctarget = $HEADERS{synctarget}; # We might want to sync the address book more than once
# Set up log files
my $log = new Log::LogLite($logfile,$loglevel);
$log->write("Start of Conduit", 5);
my $pda_log; # Variable for PDA log (which we can only write to once, usually at the end of the conduit)
###########################
# Connect to PDA Database #
###########################
my $dbinfo = spc_get_dbinfo() or
$log->write("Could not read PDA database info",1),
Die ("501 - Could not read PDA database info"); # YES: Coldsync Die() not Perl die()
# Set up if variables
my $ssis_table;
my $pda_table;
my $default_user;
if ( $synctarget eq "Memo" )
{
use Palm::Memo;
$ssis_table = "Memo"; # This is what we call it in the glue table
$pda_table = "memo"; # This is what we call it in the glue table
if ( $Memo_user == 0 ) { $default_user = $ssisuser; } # Is this application personal?
else { $default_user = $public_user; } # or public ?
$log->write("Syncronising $dbinfo->{creator} database", 5);
}
my $pdb = &dlp_OpenDB($dbinfo->{name}, 0x80) or
$log->write("Could not open PDA database", 1),
Die ("501 - Could not open PDA database"); # YES: Coldsync Die() not Perl die()
#############################
# Connect to MySQL Database #
#############################
$log->write("Connecting to MySQL",6);
my $dbh = DBI->connect( $mysql_datasource, $mysql_username, $mysql_password ) or
$log->write("Could not connect to SSiS Database: $DBI::errstr", 1),
Die ("501 - Can't connect to SSiS Database: $DBI::errstr \n"); # YES: Coldsync Die() not Perl die()
$log->write("Locking SSiS Tables",6);
my $sql_table_lock = $dbh->prepare("LOCK TABLES $ssis_table WRITE, Category WRITE");
$sql_table_lock->execute() or
$log->write("Could not lock SSiS tables: $DBI::errstr", 2),
Die ("501 - Could not lock SSiS tables: $DBI::errstr \n"); # YES: Coldsync Die() not Perl die()
############
# Get Time #
############
# Get current time
# See http://www.perldoc.com/perl5.6/pod/func/localtime.html
my $StartSyncTime = localtime;
$log->write("Current System Time is $StartSyncTime", 5);
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
$year += 1900; # Converts year to 4 digit
$mon += 1; # Converts month to human numbering
# Convert current time to PDA format
# my $new_pda_time;
# $new_pda_time->{year} = $year;
# $new_pda_time->{month} = $mon;
# $new_pda_time->{day} = $mday;
# $new_pda_time->{hour} = $hour;
# $new_pda_time->{minute} = $min;
# $new_pda_time->{second} = $sec;
#Update PDA Time
# dlp_SetSysDateTime( $year, $mon, $mday, $hour, $min, $sec ) or
# $log->write("Could not set PDA time",2),
# $pda_log .= "Could not set PDA time";
# Convert current time to mysql timestamp(14) format
$mon =~ s/^([1-9])$/0$1/; # Add leading zero where necessary
$mday =~ s/^([1-9])$/0$1/; # Add leading zero where necessary
$sec =~ s/^([1-9])$/0$1/; # Add leading zero where necessary
$min =~ s/^([1-9])$/0$1/; # Add leading zero where necessary
$hour =~ s/^([1-9])$/0$1/; # Add leading zero where necessary
my $CurrentSyncTimestamp = $year .$mon .$mday .$hour .$min .$sec;
$log->write("MySQL snapshot Timestamp: $CurrentSyncTimestamp", 6);
###########################################################################
###############################
# Start PDA Dirty Record Loop #
###############################
# Get dirty records
foreach my $PDA_record ( dlp_ReadNextModifiedRec($pdb) )
{
$log->write("Next Modified record id: $PDA_record->{id}",6);
# Set up loop variables
my $sql_select_code;
my $sth;
my $glue_record;
my $ssis_record;
# Get matching server record
$sql_select_code = "SELECT ID, LastSync, SSiS_Record_ID
FROM $mysql_palm_gluetable
WHERE PDA_Record_ID = '$PDA_record->{id}' # match the ID number
AND PDA_ID = '$deviceID' # and the PDA device number (from the colduit header)
AND PDA_Table = '$pda_table' # The pda table and
AND SSiS_Table = '$ssis_table' # ssis table from the earlier if statement
AND Conflict = '0' # and let's make sure it isn't a problem record.
";
$sth = $dbh->prepare($sql_select_code);
$log->write("Next modified record select code: $sql_select_code",7);
$sth->execute() or
$log->write("Could not run select code for PDA ID: $PDA_record->{id}: $DBI::errstr",3),
$pda_log .= "Failed to syncronise PDA:ID:$PDA_record->{id}: $DBI::errstr",
next;
# Does the record exist in SSiS?
if ( $sth->rows == 0 )
{
# Doesn't exist so we call the add record subroutine
# Add_To_SSiS($PDA_record->{id},$synctarget);
next;
}
# Are there too many records the glue table?
if ( $sth->rows > 1 )
{
$log->write("Too many matches for for PDA ID: $PDA_record->{id}",3);
$pda_log .= "Too many matches for PDA ID: $PDA_record->{id}";
next;
}
# If the record exists, check PDA record for {deleted} flag
if ( $PDA_record->{attributes}{deleted} )
{
# If exists check for {archive} flag
if ( $PDA_record->{attributes}{archive} )
{
# If it does, mark as archived
# Delete_From_PDA($PDA_record->{id},$synctarget,0)
next;
}
else
{
# Set as Archive in glue table and set Deleted flag
# Delete_From_PDA($PDA_record->{id},$synctarget,1)
next;
}
}
# Get server record timestamp
my $glue_record = $sth->fetchall_arrayref( { ID => 1, LastSync => 1, SSiS_Record_ID => } );
$sql_select_code = "SELECT LastChange
FROM $ssis_table
WHERE ID = '$glue_record->{SSiS_Record_ID}' # Matching the correct id number
AND (
User = '$ssisuser' # 'my' records or ...
OR User = '$public_user' # Everybody's records
)
AND Deleted = '0' # Hasn't been deleted
";
$sth = $dbh->prepare($sql_select_code);
$log->write("Getting ssis record ID: $glue_record->{SSiS_Record_ID}: $sql_select_code",7);
$sth->execute() or
$log->write("Could not run select code for SSiS ID: $glue_record->{SSiS_Record_ID}: $DBI::errstr",3),
$pda_log .= "Failed to syncronise SSiS:ID:$glue_record->{SSiS_Record_ID}: $DBI::errstr",
next;
$ssis_record = $sth->fetchall_arrayref( { LastChange => 1 } );
# If server record older than last sync timestamp, check server record deleted flag
if ( $ssis_record->{LastChange} > $glue_record->{LastSync} )
{
# Mark for conflict resolution
$log->write("Marking SSiS:ID:$glue_record->{ID} for conflict resolution",6);
# Mark_As_Conflict($glue_record->{ID});
next;
}
else # Server record hasn't been changed since last sync, so update it
{
# call update record subroutine
# Update_SSiS($glue_record->{ID},$synctarget);
next;
}
# End "foreach $PDA_record" Loop
}
################################
# End of PDA dirty record loop #
################################
###########################################################################
##################################
# Start Server dirty record loop #
##################################
# First we need to find all SSiS records that do not have a glue table entry.
# This could be dangerous for duplicates so we must do a lookup in the PDA
# before any new records are added. If necessary we mark them as conflicted to be resolved later.
# We could eventually do this as a 'fetch' if speed becomes an issue.
my $sql_select_code = "SELECT $ssis_table.ID, $mysql_palm_gluetable.ID
FROM $ssis_table LEFT JOIN $mysql_palm_gluetable ON $ssis_table.ID = $mysql_palm_gluetable.SSiS_Record_ID
WHERE $mysql_palm_gluetable.ID IS NULL";
my $sth = $dbh->prepare($sql_select_code);
$log->write("Getting unsync'd ssis records: $sql_select_code",7);
$sth->execute() or
$log->write("Could not run select code for unsync'd SSiS records: $DBI::errstr",3),
Die ("501 - Can't connect to select SSiS records: $DBI::errstr \n"); # YES: Coldsync Die() not Perl die()
my $ssis_unsynced = $sth->fetchall_arrayref( { SSiS_Record_ID => 1 , Glue_table_ID => 1 } );
foreach my $new_ssis_record ( @$ssis_unsynced )
{
# Add to glue table and add to PDA
$log->write("Adding ssis record:ID: $new_ssis_record->{SSiS_Record_ID} to PDA",6);
# Add_To_PDA($new_ssis_record->{SSiS_Record_ID},$synctarget);
}
# Get all dirty server records (by using timestamps)
$sql_select_code = "SELECT $mysql_palm_gluetable.ID, $mysql_palm_gluetable.PDA_Record_ID, $mysql_palm_gluetable.SSiS_Record_ID
FROM $mysql_palm_gluetable, $ssis_table
WHERE $mysql_palm_gluetable.SSiS_Record_ID = $ssis_table.ID # Join by ID numbers
AND $mysql_palm_gluetable.Archive = '0' # Ignore archived records
AND $ssis_table.Deleted = '0' # Ignore deleted records
AND $mysql_palm_gluetable.PDA_ID = '$deviceID' # Match the right PDA device
AND $mysql_palm_gluetable.PDA_Table = '$ssis_table' # Match the right ssis table
AND $mysql_palm_gluetable.SSiS_Table = '$pda_table' # Match the rigth PDA database
AND $mysql_palm_gluetable.Conflict = '0' # Ignore conflicted records
AND $ssis_table.LastChange < '$CurrentSyncTimestamp' # Older than when we started
AND $ssis_table.LastChange > $mysql_palm_gluetable.LastSync # Newer than the last sync
AND (
$ssis_table.User = '$ssisuser' # 'my' records or ...
OR $ssis_table.User = '$public_user' # Everybody's records
)
";
my $sth = $dbh->prepare($sql_select_code);
$log->write("Getting dirty ssis records: $sql_select_code",6);
$sth->execute() or
$log->write("Could not run select code for SSiS records: $DBI::errstr",3),
Die ("501 - Can't connect to select SSiS records: $DBI::errstr \n"); # YES: Coldsync Die() not Perl die()
my $ssis_record = $sth->fetchall_arrayref( { ID => 1 , PDA_Record_ID => 1 , SSiS_Record_ID => 1 } );
foreach my $ssis_record_row ( @$ssis_record )
{
$log->write("Looking for Palm record match: $ssis_record_row->{PDA_Record_ID}",6);
# Get PDA record
my $PDA_record = dlp_ReadRecordByID ( $pdb, $ssis_record_row->{PDA_Record_ID} ) or
$log->write("Could not retrieve PDA:ID:$ssis_record_row->{PDA_Record_ID}",4),
next;
# If exists check dirty flag (shouldn't be any left at this point)
if ( $PDA_record->{attributes}{dirty} )
{
# Mark for conflict resolution
$pda_log .= "Failed to syncronise SSiS:ID:$ssis_record_row->{SSiS_Record_ID}";
$log->write("Could not syncronise SSiS:ID:$ssis_record_row->{SSiS_Record_ID} to PDA",6);
# Mark_As_Conflict($glue_record->{ID});
next;
}
else
{
# Update PDA
$log->write("Updating SSiS:ID:$ssis_record_row->{SSiS_Record_ID} to PDA",6);
# Update_PDA($glue_record->{ID}, $synctarget);
next;
}
# End '@$ssis_record' foreach loop
}
###################################
# End of Server dirty record loop #
###################################
###########################################################################
#####################################
# Start of Conflict resolution Loop #
#####################################
# Get list of conflicts
# Reset failure flag
# Reset overwrite flag
# Get PDA record and Server record
# Match each field (do a while on a foreach ??)
# If same next field
# If different check contents
# If server field is empty check overwrite flag
# If set overwrite with PDA data
# If PDA field is empty check overwrite flag
# If set overwrite with Server data
# If both fields contain data set failure flag
# Next field
# Check for overwrite flag
# If set reset conflict resolution flag and get next record
# If not set check failure flag
# If not set, set overwrite flag and loop through fields again
# If set, send entry to log and next record
# End If
# End If
# End Loop
# End Loop
#########################################
# End of Conflict resolution subroutine #
#########################################
#########################################
# Unlock and close database connections #
#########################################
if ( $pda_log eq "" ) { $pda_log = "$dbinfo->{name}: OK"; }
DlpAddSyncLogEntry($pdb, $pda_log) or $log->write("Could not write PDA log",2);
&dlp_CloseDB($pdb) or $log->write("Could not close PDA Database",1);
my $sql_table_unlock = $dbh->prepare("UNLOCK TABLES");
$sql_table_unlock->execute() or
$log-write("Could not unlock MyQL tables",1),
Die ("501 - Could not unlock MySQL tables. \n"); # YES: Coldsync Die() not Perl die()
$dbh->disconnect or $log->write("Could not close PDA connection",1);
$log->write("Unlocked tables and Disconnected: End of conduit",5);
EndConduit;
##################
# End of conduit #
##################
####################
# Helper functions #
####################
# TO DO & memory joggers
# When add records we need to remember to check deleted and archive flag durying the add subroutine
# when adding & modifing records, we should force the timestamp to our preset value
# We have an issue with the all_user and when switching users in the records.
# public_user and new record defaults apply here...
# neeed to check log entries are before subroutine code calls
# Add_To_SSiS() # Needs to create glue table, & check for existance. If they already exist, mark as conflict.
# Delete_From_PDA() # Set Archive flag in glue table/ Mark as deleted & Delete record from PDA
# Mark_As_Conflict()
# Update_SSiS()
# Add_To_PDA()
# Update_PDA()