#!/usr/bin/env perl $VERSION="1.0"; # Modified: 22/01/2008 # Written By: Lionel Bartlett (Novell AU) # <-- Change the hard-coded password here --> $PASSWORD="novell"; # <-----------------------------------------> $INITIAL_OBJECT=""; # Use commas (,) or dot (.) in the object name $INTATTRNAME=""; # Attr which holds a list of object to contact to verify reference back to initial object $REFATTRNAME=""; # Attr to return from individual users $SHOWDETAIL="N"; $USERNAME=""; # iMonitor user $CSVFILENAME=""; # Output file name $PORT="8028"; # iMonitor port $WORKINGDIR="_tmpwget"; # Working directory $LOGD=`date +"%Y%m%d_%H%M%S"`; $LOGD=~ s/\n//g; # Date and time used when specifying the -d option $HTTP="http:"; my @SERVERLIST = (); $TOTALREFERENCES=0; $HIDDENATTR="N"; #use warnings; use Getopt::Std; use File::Path; sub utilSyntax { print "\n ** Reference Checker v$VERSION**\n\n"; print " Syntax: \n"; print " -n [name_of_initial_object] the TREE name is required!/\n"; print " -a [attribute_name] on the initial object. (Case Sensitive)\n"; print " -r [referenced_attribute_name] on the objects being checked/ (Case Sensitive)\n"; print " -u [username_for_iMonitor_login]\n"; print " -w [password_for_iMonitor_login]\n"; print " -s [DNS_server_OR_ipaddress] of all servers to contact for checking. Separated by ; or ,\n"; print " -f [Filename] listing servers to contact. Place each servername or IP Addr on a new line. (Overrides -s)\n"; print " -p [iMonitor_port] Default is (8028). Must be the non-SSL port!\n"; print " -o [output_file]\n"; print " -k Report report all Values Details on the referenced object/s. Default is OFF\n"; print " -x Use this option, instead of -w, to use the password hard-coded in the script\n"; print " (Used for command-line privacy).\n"; print " -z Don't clean up the temporary working directory '$WORKINGDIR' after execution\n"; print " -d Append the date and time to the output file name to make the file unique\n"; print " -e If no errors are reported only create an empty file called 'lastrun_\'\n"; print " (There will be max one 'lastrun' file)\n\n"; print "Example: ./refchecker.pl -n \"Group1,services,acme,novell,TEST1\" -a \"Member\" -r \"Group Membership\" -u \"admin.novell\" -w \"novell\" -s \"10.10.10.1,10.20.10.50,10.30.10.70\" -o group1-refs.txt\n\n"; } #EndFunc # Check if wget dependency is available print "\n"; if ( `which wget` eq "" ) { $INS=`rpm -qa | grep wget`; print "\n ** Reference Checker v$VERSION**\n\n"; print "This utility has a dependency on wget. It can not be located"; if ( $INS eq "" ) { print " because 'wget' is not installed."; } else { print ", however 'wget' is installed."; } print " The following command should return the location of wget: 'which wget'\n\n"; exit 1; } #EndFunc %options=(); getopts("hn:a:r:u:w:s:f:p:o:kxzde",\%options) or verbose_usage(); verbose_usage() if $options{h}; # Initial Object where a list of objects to be checked will be located if (defined $options{n}) { $INITIAL_OBJECT=$options{n}; # Fix Initial Object name if ( $INITIAL_OBJECT =~ m/\./ ) { $INITIAL_OBJECT =~ s/\./,/g; } $INITIAL_DOT_OBJECTNAME=$INITIAL_OBJECT; $INITIAL_DOT_OBJECTNAME=~s/,/./g; } else { utilSyntax; print "\nSpecify the name of the initial object to be checked. This object could be a Group, GroupWise Post Office or ZEN application.\n"; exit 1; } # The name of the attribute (case sensitive) where the list of objects to be checked is listed if (defined $options{a}) { $INTATTRNAME=$options{a}; } else { print "\nSpecify the name of the attribute on the initial object which will provide a list of objects to determine if they reference is initial object.\n"; exit 1; } # The name of the attribute on the referenced objects which should contain a reference back to the initial object if (defined $options{r}) { $REFATTRNAME=$options{r}; } else { print "\nSpecify the name of the attribute on the referenced objects, where we expect to find the initial object name.\n"; exit 1; } # iMonitor Username if (defined $options{u}) { $USERNAME=$options{u}; } else { print "\nSpecify the name of the iMonitor user required for authentication.\n"; exit 1; } # iMonitor Password if (defined $options{w}) { $PASSWORD=$options{w}; } else { if ( ! $options{x} ) { print "\nSpecify the password of the iMonitor user or hard-code the password in the script and use the -x option.\n"; exit 1; } } # List of servers where objects should be located. The first server in the list must be where the inital object is located. if ( defined $options{s} || defined $options{f} ) { $SL=""; $FILENAME=$options{f}; if ( defined $options{f} ) { if ( -e $FILENAME ) { $SL=`cat $options{f}`; $SL =~ s/(\r\n|\r|\n)/;/g; } else { print "The server list file does not exist: $options{f}\n\n"; exit 1; } } else { $SL="$options{s},"; } $TOTAL=0; while ( $SL =~ m/(.[^,;]*)[,;]/g) { if ( $1 ne "" && $1 ne ";") {$SERVERLIST[$TOTAL]=$1;} $TOTAL++; } if ( $TOTAL == 0 ) { print "\nSpecify at least one IP address or DNS server name for iMonitor communication.\n"; print "When using more than one IP or server name then specify these as a comma separated list:\n"; print " Example: 10.10.1.1,10.10.2.2,10.10.3.3\n"; print "The first server MUST hold the initial object which provides a list of objects to be checked.\n"; exit 1; } } # iMonitor port. Default 8030 on eDir 8.8 and higher and 8009 on lower versions if (defined $options{p}) { $PORT=$options{p}; } else { $PORT="8028"; } # The filename where output results will be written to in CSV format if (defined $options{o}) { $SRCATTR = $INTATTRNAME; $SRCATTR =~ s!(:|/|;|\.| )!!g; $DSTATTR = $REFATTRNAME; $DSTATTR =~ s!(:|/|;|\.| )!!g; if (defined $options{d}) { $CSVFILENAME="$SRCATTR_$DSTATTR_$options{o}_$LOGD"; # Append date and time to output file name } else { $CSVFILENAME="$SRCATTR-$DSTATTR-$options{o}"; } print "$SRCATTR $DSTATTR\n"; } else { print "\nProvide a filename where the results will be written to in comma separated format.\n"; exit 1; } # Turn ON/OFF the option to report all values. ON means that all values should be reported. Default is OFF. if (defined $options{k}) { $SHOWDETAIL="Y"; } sub verbose_usage { utilSyntax; print "The purpose of this utility is to check for a reference back to a single object for each of the objects being referenced by the specified object.\n\n"; print "This is acheived by providing an initial object, like a Group. Then specifying which attribute on this object is the source list of users to check."; print "An attribute is specified for the referenced objects which should contain the name of the initial object, i.e. \"Member\" & \"Group Membership\" for example.\n\n"; print "The reference could be missing either to a real -602/3 error or using code logic (then -602! / -603!), reported in the CSV file.\n\n"; print "Lastly specify the name of the output CSV file where the results will be written\n\n"; print "When turning ON the Value Details option then all values for the specified attribute on each object are reported.\n\n"; print "Specify a list of servers to be contacted using iMonitor. The first server must hold the initial object which provides a list of the referenced objects.\n\n"; print "The rest of the servers in the list are used to check for referenced objects.\n\n"; print "TROUBLESHOOTING:\n"; print " The likely reasons for not obtaining the expected results are, incorrect:\n"; print " a) Initial Object name and/or context\n"; print " b) Initial Object attribute name\n"; print " c) Referenced object attribute name\n\n"; print " Try the following using the \"-z\" option:-\n"; print " 1. Show the available attributes on the Initial Object:\n"; print " grep \"ATTR NAME\" /tmp/_tmpwget/initial_object.txt\n\n"; print " 2. List the attribute names and user/object name values:\n"; print " grep \"/nds/\" /tmp/_tmpwget/initial_object.html\n\n"; print " 3. Instead of using the Initial Object name, use one of the referenced object names then perform point (1)\n"; print " to list available attributes on that object.\n"; print " Check that the attribute name specified for point c is correctly spelled.\n\n"; exit 1; } #EndFunc #Variables# $FILEDATA=""; $ATTRFILEDATA=""; #------------------------------------------------------------------ #Parameter Values: print "Initial object: $INITIAL_OBJECT\n"; print "Initial attribute name: $INTATTRNAME\n";; print "Referenced attribute name: $REFATTRNAME\n"; print "iMonitor User name: $USERNAME\n"; print "iMonitor user password: "; if ( $options{x} ) { print "[Using hard-coded]\n"; } else { print "[*****]\n"; } print "iMonitor port: (HTTP) $PORT "; if ( $PORT ne "8028" && $PORT ne "8008" ) { print " ** WARNING: Check port number **\n"; } else { print "\n"; } print "Report all values: $SHOWDETAIL\n"; print "Output file name: $CSVFILENAME\n"; print "Server list:\n"; foreach $SERVER(@SERVERLIST) { print "\t\t\t\t $SERVER\n"; } print "\n"; #------------------------------------------------------------------ sub makeTypeLess { $TMP=$_[0]; $TMP=~s/(CN=|OU=|O=|T=|C=)//g; return $TMP; } # EndFunc sub openFile { local $/=undef; open INFILE, "$_[0]" or die "Problem with file: $!"; $FILEDATA=; close INFILE; } # EndFunc sub openFileAttr { local $/=undef; open INFILE, "$_[0]" or die "Problem with file: $!"; $ATTRFILEDATA=; close INFILE; } # EndFunc sub isNumeric { $val = $_[0]; if ($val !~/^[0-9|.|,|-]*$/) { return 0; #Non-numeric } else { return 1; #Numeric } } #End IsNumeric sub fetchwwwpage { if (defined $options{z}) {print "In fetchwwwpage...\n";} $URL=$_[0]; $RESULTS=$_[1]; # print "wget -o $WORKINGDIR/wget_initialObject.log -O $WORKINGDIR/$RESULTS --no-check-certificate --user=$USERNAME --password=$PASSWORD $URL\n\n"; system "wget -o $WORKINGDIR/wget_initialObject.log -O $WORKINGDIR/$RESULTS --no-check-certificate --user=$USERNAME --password=$PASSWORD \"$URL\" &"; sleep 1; $TRY=0; while ( `ps -ef | grep -c wget` != 1 && $TRY < 1 ) { sleep 1; $CONNECTION=`cat $WORKINGDIR/wget_initialObject.log`; if ( $CONNECTION =~ m/(Connection refused)|Connecting to (.*:\d+)/ ) { $SRVPORT=$2; if ( $2 ne "" && $SRVPORT ne "@SERVERLIST[0]:$PORT" ) { print "************************************* FATAL ***********************************************\n"; print " Proxy server detected: $SRVPORT\n\n"; print " a. kill the running process for wget\n"; print " b. Disable the proxy in network services\n"; print " c. Close the terminal window\n"; print " d. Open a new terminal window, then try again\n"; print "*******************************************************************************************\n\n"; exit 1; } else { if ( $1 ne "" ) { print "************************************* FATAL ***********************************************\n"; print "Problem detected connecting to server:\n$CONNECTION\n"; print "*******************************************************************************************\n\n"; exit 1; } } sleep 1; $TRY++; } } # While $CONNECTION=`cat $WORKINGDIR/initial_object.html`; if ( $CONNECTION =~ m/INPUT TYPE=/ ) { print "************************************* FATAL ***********************************************\n"; print " Unable to authenticate. Check the username and password. \n"; print "*******************************************************************************************\n\n"; exit 1; } print " - Initial object data retrieved.\n"; } #EndFunc sub getHTMLdata { if (defined $options{z}) {print "In getHTMLdata...\n";} openFile "$WORKINGDIR/$_[0]"; # Remove comments $FILEDATA=~s/]*-->//g; } #EndFunc sub checkForObjError { if (defined $options{z}) {print "In checkForOBJError...\n";} if ( $FILEDATA =~ m!>Error (.*)! ) { print "\n** Problem occured accessing the initial object specified: $INITIAL_OBJECT **\n\n"; print " Error: $1\n\n"; if ( $1 =~ m/601/ && $INITIAL_OBJECT !~ m/[Tt]=/ ) { print " Check that the tree name has been used in the initial object name specified!\n\n"; } exit ; } } #EndFunc sub getTextFromHTML { # Uses the data in variable $FILEDATA if (defined $options{z}) {print "In getTextFromHTML...\n";} # Create Text File: open OUTFILE, ">$WORKINGDIR/$_[0]"; $ATTR=""; while ($FILEDATA =~ m! ([^< ].*)|(?:headers=.*\n.*_parent|alt=.*)">(.[^<]*)|class=.*\n.*/nds/schema/attribute.*">(.*)<|>Count: (\d+)<|Object Data: (.*)<|NDS Error (.[^<]*)$WORKINGDIR/$_[1]Object"; if ($FILEDATA =~ m/'$INTATTRNAME'.*(?:Value Count: `(\d+)`\n|(?:\r\n|\n|\r))(.[^~]*)/) { $VALCNT=$1; $EXTRACT=$2; while ( $EXTRACT =~ m/\.(.*)\./g ) { $USER=$1; $USER =~ s/\./,/g; processServerList $USER; } #While $TOTALREFERENCES=`grep -c "$HTTP" $WORKINGDIR/$_[1]Object`; $TOTALREFERENCES=~s/(?:\r\n|\r|\n)//g; } close OUTFILE; } #Endfunc sub objectCount { $BS=chr(8); #sleep 1; if ( -e "$WORKINGDIR/$_[0]" ) { print "$BS$BS$BS$BS"; $TOTAL=`grep -c "Object Data" $WORKINGDIR/$_[0]`; $TOTAL=~s/(?:\r\n|\r|\n)//g; printf("%04d", $TOTAL); } } #EndFunc sub getUserData { if (defined $options{z}) {print "In getUserData...\n";} $URLlist=$_[0]; $RESULTS=$_[1]; # Collect all the referenced object data using the URL file/s: will be two files if hidden attribute involved $EXTCMD=`wget -o $WORKINGDIR/wget_refObjects.log -O $WORKINGDIR/$RESULTS --no-check-certificate --user=$USERNAME --password=$PASSWORD -i $WORKINGDIR/$URLlist &`; print "There are $TOTALREFERENCES iMonitor URLs to process ... processed: "; while ( `ps -ef | grep -c wget` != 1 ) {objectCount $RESULTS; } print "\n"; } #EndFunc sub getUserTextDataII { if (defined $options{z}) {print "In getUserTextData...\n";} getHTMLdata "$_[0]"; # Working dir added in function iself getTextFromHTML "$_[1]"; # Working dir added in function iself openFile "$WORKINGDIR/$_[1]"; open OUTFILE, ">$_[2]"; #This is the final output file as requested by the user. It must NOT go into working directory. $NAME="";$ATTR=""; $FOUND="N"; $ERROR=""; # Get Field Details: assuming we don't hit a -602/3 error situation! $COMMAS=""; $FIELDNAMES=""; if ( $FILEDATA =~ m/~ ATTR NAME: '$REFATTRNAME'.*(?:\r\n|\r|\n)(.[^\d]*)\n/i ) { # Get field names $FIELDNAMES=$1; $FIELDNAMES=~s/(?:\r\n|\r|\n)/,/g; while ( $FIELDNAMES =~ m/.*/g ) { $COMMAS="$COMMAS," ;} #$COMMAS="$COMMAS," ; } if ($FIELDNAMES ne "" ) { print OUTFILE "SourceIP,RemoteServerIP,Partition,Object Name,Error,$FIELDNAMES\n"; } else { print OUTFILE "SourceIP,RemoteServerIP,Partition,Object Name,Value or Error\n"; } # If a hidden attribute is involved, then ensure that both the hidden attr and the object details form one block of data per object if ( $HIDDENATTR eq "Y" ) { $FILEDATA =~ s/� (Object Name:.*\nEntry Information)/% $1/g; # Remove the � from the second reference to the object name } while ( $FILEDATA =~ m/� Object Name: \.(.*).([^�]*)/g ) { # Split by object $OBJDATA = $2; $OBJNAME = $1; #print "Object Name: $OBJNAME $REFATTRNAME\n\n"; #print "Data: $OBJDATA\n\n"; # Debug the contents of $WORKINGDIR/userdata.tmp $PARTITIONNAME=""; if ( $OBJDATA =~ m/Partition Name\n(.[^\n]*)/i ) { $PARTITIONNAME=$1; } # Get server IP Address $CONN=""; if ( $OBJDATA =~ m!server=(?:/TCP|/UDP)'=(.[^"]*)! ) { $CONN=$1} # Make object name typeless $OBJDATA=makeTypeLess($OBJDATA); # Check for errors, missing references or write all values to file as required if ( $OBJDATA =~ m/ERROR: (.*)/ ) { $ERROR=$1; $ERROR=~s/ //g; print OUTFILE "@SERVERLIST[0],$CONN,$PARTITIONNAME,$OBJNAME,Error: $ERROR$COMMAS$INITIAL_DOT_OBJECTNAME\n"; } else { if ( $OBJDATA =~ m/~ ATTR NAME: '$REFATTRNAME'.*\n(.[^~%]*)/i ) { # Split by attributes, to cater for multiples ** $ATTRDATA="$1"; #print "Object Name: $OBJNAME ATTR NAME:$REFATTRNAME\n\n"; #print "ATTDATA: $ATTRDATA\n"; #print "Object: $INITIAL_DOT_OBJECTNAME\n"; if ( $ATTRDATA !~ m/$INITIAL_DOT_OBJECTNAME/i ) { $MISSING="Missing: $INITIAL_DOT_OBJECTNAME"; print OUTFILE "@SERVERLIST[0],$CONN,$PARTITIONNAME,$OBJNAME,Error: -602!$COMMAS$MISSING\n" } if ( $SHOWDETAIL eq "Y" ) { # Get Field Names $REGEX=""; $FIELDCOUNT =0; if ( $ATTRDATA =~ m/(.[^\d]*)/i ) { $FIELDCOUNT=($1 =~ tr/(\r\n|\r|\n)//); $FIELDNAMES=$1; $FIELDNAMES=~s/(\r\n|\r|\n)/,/g; } if ( $FIELDCOUNT == 0 ) { $REGEX="(.*\n)"; } else { $REGEX="(.*\n){$FIELDCOUNT}"; } #print "CONN: $CONN\nATTRDATA:\n$ATTRDATA\n"; # Get Field Values while ( $ATTRDATA =~ m/($REGEX)/g ) { # Get field data ** $FIELDDATA=""; if ( $1 !~ m/TimeStamp/ ) { $FIELDDATA=$1; $FIELDDATA=~s/(\r\n|\r|\n)/,/g; $FIELDDATA=~s/\.,//g; $FIELDDATA=~s/,\./,/g; if ( $FIELDDATA ne "" && $FIELDDATA ne ",") { print OUTFILE "@SERVERLIST[0],$CONN,$PARTITIONNAME,$OBJNAME,$FIELDDATA\n"; } } } # While } } else { $MISSING="No Values & Missing: $INITIAL_DOT_OBJECTNAME"; print OUTFILE "@SERVERLIST[0],$CONN,$PARTITIONNAME,$OBJNAME,Error: -603!$COMMAS$MISSING!\n"; } } } #While close OUTFILE; } #EndFunc sub getFieldNames { $TOTAL=0; while ( $_[0] =~ m/(.*)/g) { # Get field names $TOTAL++; } # While print "Total fields: $TOTAL\n"; } #EndFunc sub cleanupWorkingDir { if ( ! $options{z} ) { rmtree ($WORKINGDIR); if ( $! eq "Permission denied" ) { print "\nCan't clean up the working directory $WORKINGDIR. No permssion to remove it.\n\n"; } } } #EndFunc sub createWorkingDir { mkdir $WORKINGDIR; if ( $! eq "Permission denied" ) { print "\nUnable to create a temporary working directory.\n"; exit 2; } else { if ( $! eq "File exists" ) { rmtree ($WORKINGDIR); if ( $! eq "Permission denied" ) { print "\nNeed permission to create directory $WORKINGDIR to continue.\n"; exit 2; } else { mkdir $WORKINGDIR; } } } if ( not -e $WORKINGDIR ) { print "Directory `$WORKINGDIR` could not be created! This is fatal.\n"; exit 2; } } #EndFunc sub checkLastrun { $TOTALE=`grep -c "Error: " $CSVFILENAME`; $TOTALE=~s/(?:\r\n|\r|\n)//g; if (defined $options{e}) { if ( $TOTALE == 0 ) { open OUTFILE, ">lastrun_$LOGD"; close OUTFILE; unlink ($CSVFILENAME); print "\nThere are no errors reported in the file `$CSVFILENAME`. Creating lastrun file. All done.\n\n"; } else { print "\nTotal of $TOTALE error/s reported in the file `$CSVFILENAME`. All done.\n\n"; } } else { print "\nTotal of $TOTALE error/s reported in the file `$CSVFILENAME`. All done.\n\n"; } } #Endfunc ############################################################################################################### ############################################################################################################### # Remove lastrun file system (`rm -f lastrun_*`); # Create a working directory in as defined in the variable, default is /tmp createWorkingDir; # Get the HTML page from iMonitor for the initial object fetchwwwpage "$HTTP//@SERVERLIST[0]:$PORT/nds/object/data?dn=$INITIAL_OBJECT&attr=$INTATTRNAME","initial_object.html"; # Load the HTML page into a variable getHTMLdata "initial_object.html"; # Check if the initial object exists! checkForObjError; # Generically convert the HTML page to text for specific "tags" getTextFromHTML "initial_object.txt"; # Check if the attribute on the initial object exists, looking for -603 error check603Error; # Determine if the attribute on the referenced objects is hidden: if so we require a two pass data extraction # due to missing Entry Information when requesting specific attrs in a URL detectHiddenAttr; # Extract the specific attribute and object name values from the converted html page getUserList "initial_object.txt","urllist"; # The URL file name is prepended with Object/Attr depending on what data we need # Use iMonitor to collect the attribute data for each user referenced by the initial object getUserData "urllistObject","objectData.html"; # Extract the attribute values details and other data, like timestamps and errors getUserTextDataII "objectData.html","objectData.txt",$CSVFILENAME; # If the "-z" option is not specified, clean up the temporary working directory cleanupWorkingDir; # Check if Lastrun file is required checkLastrun; ############################################################################################################### ###############################################################################################################