#!/usr/bin/perl # # Perl script to sync two ldap databases # # Author: Marty Lee # Contributors: Andreas Kotes, Martin Kreiner # # License: GPL (Version 2) # http://www.maui.co.uk/downloads/ldapsync/License # Version: $Revision: 1394 $ # # Copyright (C) 2002 Marty Lee, Maui Systems. # # ############################################################################# # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################# use strict; ############################################################################# # # Modules & defaults # use Net::LDAP; use AppConfig; my $config = AppConfig->new({ CASE => 0, GLOBAL => { DEFAULT => "unset", ARGCOUNT => AppConfig::ARGCOUNT_ONE, }, }); $config->define("srcsrv", { ARGCOUNT => AppConfig::ARGCOUNT_ONE }); $config->define("srcport", { ARGCOUNT => AppConfig::ARGCOUNT_ONE }); $config->define("srcdn", { ARGCOUNT => AppConfig::ARGCOUNT_ONE }); $config->define("srcpw", { ARGCOUNT => AppConfig::ARGCOUNT_ONE }); $config->define("dstsrv", { ARGCOUNT => AppConfig::ARGCOUNT_ONE }); $config->define("dstport", { ARGCOUNT => AppConfig::ARGCOUNT_ONE }); $config->define("dstdn", { ARGCOUNT => AppConfig::ARGCOUNT_ONE }); $config->define("dstpw", { ARGCOUNT => AppConfig::ARGCOUNT_ONE }); $config->define("tree", { ARGCOUNT => AppConfig::ARGCOUNT_ONE }); $config->define("debug", { ARGCOUNT => AppConfig::ARGCOUNT_NONE }); $config->set("srcport", "389"); $config->set("srcdn", "cn=Directory Manager"); $config->set("dstport", "389"); $config->set("dstdn", "cn=Directory Manager"); ############################################################################# # # Read in the config file # $config->file("/etc/ldapsync.conf") or die "$@"; ############################################################################# # # Abort if we don't have our minimum information # $config->get("srcsrv") ne "unset" || die "No source server set"; $config->get("srcport") ne "unset" || die "No source port set"; $config->get("srcdn") ne "unset" || die "No source dn set"; $config->get("srcpw") ne "unset" || die "No source password set"; $config->get("dstsrv") ne "unset" || die "No destination server set"; $config->get("dstport") ne "unset" || die "No destination port set"; $config->get("dstdn") ne "unset" || die "No destination dn set"; $config->get("dstpw") ne "unset" || die "No destination passowrd set"; $config->get("tree") ne "unset" || die "No LDAP tree set"; if($config->get("debug") ne "unset") { print("LDAP source: ". $config->get("srcsrv").":".$config->get("srcport")." as (". $config->get("srcdn")."/".$config->get("srcpw").")\n"); print("LDAP destination: ". $config->get("dstsrv").":".$config->get("dstport")." as (". $config->get("dstdn")."/".$config->get("dstpw").")\n"); print("LDAP Tree: ".$config->get("tree")."\n"); } ############################################################################# # # Open a connection to both LDAP servers # my $srcldap = Net::LDAP->new($config->get("srcsrv"), port=>$config->get("srcport")) or die "$@"; $srcldap->bind($config->get("srcdn"), password=>$config->get("srcpw")) or die "$@"; my $dstldap = Net::LDAP->new($config->get("dstsrv"), port=>$config->get("dstport")) or die "$@"; $dstldap->bind($config->get("dstdn"), password=>$config->get("dstpw")) or die "$@"; ############################################################################# # # Get the last time we ran # # # unlink("/var/tmp/.ldapsync.${SRCSRV}:${SRCPORT}.${DSTSRV}:${DSTPORT}"); # my $srcdate="00000101000000Z"; my $statefile="/var/tmp/.ldapsync.". $config->get("srcsrv").":". $config->get("srcport").":". $config->get("dstsrv").":". $config->get("dstport"); if(open(DATE, ${statefile})) { while() { $srcdate=$_."Z"; } close(DATE); } ############################################################################# # # Set the time for the next time we run # my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = gmtime(); ############################################################################# # # Get the records which have changed since last time we were here # my $srcresult1; if((@ARGV eq "1")&&($ARGV[0] eq "-prime")) { if($config->get("debug") ne "unset") { print("Search source LDAP server for all records\n"); } $srcresult1=$srcldap->search( base=>$config->get("tree"), filter=>"(objectclass=*)" ); } else { if($config->get("debug") ne "unset") { print("Search source LDAP server for new records\n"); } $srcresult1=$srcldap->search( base=>$config->get("tree"), filter=>"(|(modifyTimestamp>=$srcdate)(createTimestamp>=$srcdate))" ); } $srcresult1->code && die $srcresult1->error; my @srcsorted=sort { length($a->dn) <=> length($b->dn) } $srcresult1->entries; ############################################################################# # # Loop around the records, getting the dn data and then either adding a new # record, or modifying an existing record on the target server. # foreach my $srcentry (@srcsorted) { if($srcentry->dn ne $config->get("tree")) { # # Does the dn exist in the dst server ? # my $dstresult1=$dstldap->search( base=>$srcentry->dn, scope=>"base", filter=>"objectclass=*" ); # # Don't check for errors here as we'll get them if # the entry doesn't exist! # if($dstresult1->count == 1) { print("Update ".$srcentry->dn."\n"); my $dstentry = ($dstresult1->entries)[0]; foreach my $attribute ($srcentry->attributes) { my $valuearray_src = $srcentry->get_value( $attribute, asref=>1); if($dstentry->exists($attribute)) { $dstentry->replace($attribute => $valuearray_src); } else { $dstentry->add($attribute => $valuearray_src); } } foreach my $attribute ($dstentry->attributes) { if(!$srcentry->exists($attribute)) { $dstentry->delete($attribute); } } my $result = $dstentry->update($dstldap); $result->code && warn "failed to modify entry: ", $result->error ; } elsif ($dstresult1->count == 0) { print("Add ".$srcentry->dn."\n"); my $result=$dstldap->add($srcentry); $result->code && warn "failed to add entry: ", $result->error ; } else { print("Something is very wierd, ". $srcentry->dn." has ". $dstresult1->count." entries...\n"); foreach my $entry ($dstresult1->entries) { print("Entry: ".$entry->dn."\n"); } die("aborting..."); } } } ############################################################################# # # Get all the src and dst records # $srcresult1=$srcldap->search( base=>$config->get("tree"), filter=>"(objectclass=*)", scope=>"sub", attrs=>('dn') ); $srcresult1->code && die $srcresult1->error; my $dstresult1=$dstldap->search( base=>$config->get("tree"), filter=>"(objectclass=*)", scope=>"sub", attrs=>('dn') ); $dstresult1->code && die $dstresult1->error; my %srcentries; foreach my $srcentry ($srcresult1->entries) { my $dn=$srcentry->dn; $dn=~s/, */,/g; if($dn ne $config->get("tree")) { $srcentries{lc($dn)}=1; if($config->get("debug") ne "unset") { print("Source LDAP DN: ".$dn."\n"); } } } my @dstsorted=sort { length($b->dn) <=> length($a->dn) } $dstresult1->entries; ############################################################################# # # Loop around and check that all the dst entries exist on the src server, # and delete them if they don't. # foreach my $dstentry (@dstsorted) { my $dn=$dstentry->dn; $dn=~s/, */,/g; if($dn ne $config->get("tree") && $dn ne $config->get("dstdn")) { if($srcentries{lc($dn)} != 1) { print("Delete ".$dstentry->dn."\n"); my $result=$dstldap->delete($dstentry->dn); $result->code && warn "failed to delete entry: ", $result->error ; } } } ############################################################################# # # Close ldap servers # $srcldap->unbind(); $dstldap->unbind(); ############################################################################# # # Write to the statefile # if(open(DATE, ">${statefile}")) { printf(DATE "%04d%02d%02d%02d%02d%02d", $year+1900, $mon+1, $mday, $hour, $min, $sec); close(DATE); } else { printf("Unable to write to sync file\n"); }