From 8f1040df3ebd934771211369ca73710af0e4197b Mon Sep 17 00:00:00 2001
From: Mark Reynolds <mreynolds@redhat.com>
Date: Wed, 10 Sep 2014 09:36:50 -0400
Subject: [PATCH] Ticket 47548 - register-ds-admin does not register into
 remote config ds

Bug Description:  regsiter-ds-admin.pl can not register remote servers.  This
                  means you can only administer one system with the 389-console.

Fix Description:  Added ablility to register with a remote admin server, or to
                  register a remote server with the local configuration server.

                  Also added the "silent" install functionality to the script,
                  which was stated in the man page that it could do, but in
                  reality it could not.  Updated man page to fully describe
                  how to use the silent install feature.

https://fedorahosted.org/389/ticket/47548

Reviewed by: ?
---
 admserv/newinst/src/register-ds-admin.pl.in  | 704 +++++++++++++++++++++------
 admserv/newinst/src/register-ds-admin.res.in |  34 +-
 man/man8/register-ds-admin.pl.8              | 139 +++++-
 3 files changed, 713 insertions(+), 164 deletions(-)

diff --git a/admserv/newinst/src/register-ds-admin.pl.in b/admserv/newinst/src/register-ds-admin.pl.in
index 2f92411..cd0f745 100644
--- a/admserv/newinst/src/register-ds-admin.pl.in
+++ b/admserv/newinst/src/register-ds-admin.pl.in
@@ -77,11 +77,11 @@ sub reg_get_passwd
         $setup->msg(0, $key);
     }
     system("stty -echo");
-    my $ans = <STDIN>;
+    my $answer = <STDIN>;
     system("stty echo");
     print "\n";
-    chop($ans);
-    return $ans;
+    chop($answer);
+    return $answer;
 }
 
 sub reg_get_response
@@ -92,110 +92,287 @@ sub reg_get_response
 
     print("\n==============================================================================\n");
     $setup->msg(0, $key, $value);
-    my $ans = <STDIN>;
+    my $answer = <STDIN>;
     print "\n";
-    chop($ans);
-    return $ans;
+    chop($answer);
+    return $answer;
 }
 
+#
+# Return the bind dn and password for the matching instance (slapd-localhost)
+# Used by silent install
+#
+sub get_cred_from_inst
+{
+    my $mysetup = shift;
+    my $serverid = shift;
+
+    #
+    # Check the main config instance first
+    #
+    my @config_parts = split('::', $mysetup->{inf}->{register}->{configinst});
+    if ($#config_parts == 2 && $config_parts[0] eq $serverid){
+        return ( $config_parts[1], $config_parts[2] );
+    }
+
+    #
+    # Check the remaining instances
+    #
+    my @insts = $mysetup->{inf}->{register}->{instance};
+    if (@insts){
+        my $i = 0;
+        for (; $i <= $#insts; $i++){
+            my @inst_parts = split('::', $insts[$i]);
+            if($#inst_parts == 2 && $inst_parts[0] eq $serverid){
+                return ( $inst_parts[1], $inst_parts[2] );
+            }
+        }
+    }
+    return ("", "");
+}
+
+#
+# Globals
+#
+my $silent = "no";
+my $remote_host = "";
+my $remote_port = "";
+my $local_certdir = "";
+my $remote_binddn = "";
+my $remote_bindpw = "";
+my $remote_admindomain = "";
+my $localrootdn = "";
+my $localrootpw = "";
+my $destination = "";  # remote | local
+my %instances = ();
+my $instconfigdir;
+my @instconfigdirs;
+my $ans;
+my $passwd;
+my $rootdn;
+my $fqdn = hostname();
+my $dialogmgr;
+my @dialogs;
+my @silent_instances;
+my @config_parts;
+
+#
+# Continue with the setup...
+#
+
 my $res = new Resource("@propertydir@/register-ds-admin.res",
                        "@propertydir@/setup-ds-admin.res",
                        "@propertydir@/setup-ds.res");
 
+#
+# Initialize the "setup", check for silent install
+#
 my $setup = new Setup($res);
-
-$setup->msg('begin_ds_registration');
-# get existing instances
-my $instconfigdir = $setup->{configdir};
-my %instances = ();
-for my $dir (glob("$setup->{configdir}/slapd-*"))
-{
-    if (-d $dir and ($dir !~ /.removed$/))
-    {
-        my $dname = dirname($dir);
-        my $bname = basename($dir);
-        push @{$instances{$dname}}, $bname;
+if ($setup->{silent}){
+    #
+    # To be silent we must have "General" & "admin", or just "register" directives
+    #
+    if ( ($setup->{inf}->{General} && $setup->{inf}->{admin}) || $setup->{inf}->{register} ){
+        $silent = "yes";
+    } else {
+        # Missing required silent install directives
+        $setup->msg($FATAL, 'error_silent_install');
+        $setup->doExit(1);
     }
 }
 
-# in case Directory Servers are installed at the unexpected location.
-my $done = 0;
-my $ans;
-while ( !$done && ($ans =  reg_get_response($setup, 'subds_conf_prompt', "@instconfigdir@")) )
-{
-    if ( $ans eq "" || !$ans )
-    {
-        $done = 1;
+$instconfigdir = $setup->{configdir};
+
+if ($setup->{inf}->{register}){
+    #
+    # We have our remote registration silent install info
+    #
+    # [register]
+    # configinst= slapd-INSTANCE::cn=directory manager::myPassword
+    # instance= slapd-INSTANCE2::cn=directory manager::myPassword
+    # instance= slapd-INSTANCE3::cn=directory manager::myPassword
+    # remotehost= remote.server.com
+    # remoteport= 389
+    # localcertdir= /etc/dirsrv/slapd-INSTANCE
+    # remotebinddn= cn=directory manager
+    # remotebindpw= password
+    # admindn= uid=admin,ou=Administrators,ou=TopologyManagement,o=NetscapeRoot
+    # adminpw= password
+    # admindomain= server.com
+    # destination= local|remote
+    #
+
+    # 
+    # Validate the silent registration parameters
+    #
+    if ( !$setup->{inf}->{register}->{configinst} || $setup->{inf}->{register}->{configinst} eq ""){
+        $setup->msg($FATAL, 'error_missing_parameter', "configinst");
+        $setup->doExit(1);
+    }
+    if ( !$setup->{inf}->{register}->{remotehost} || $setup->{inf}->{register}->{remotehost} eq ""){
+        $setup->msg($FATAL, 'error_missing_parameter', 'remotehost');
+        $setup->doExit(1);
+    }
+    if ( !$setup->{inf}->{register}->{remoteport} ||$setup->{inf}->{register}->{remoteport} eq ""){
+        $setup->msg($FATAL, 'error_missing_parameter', 'remoteport');
+        $setup->doExit(1);
+    }
+    if ( !$setup->{inf}->{register}->{remotebinddn} || $setup->{inf}->{register}->{remotebinddn} eq ""){
+        $setup->msg($FATAL, 'error_missing_parameter', 'remotebinddn');
+        $setup->doExit(1);
+    }
+    if ( !$setup->{inf}->{register}->{remotebindpw} || $setup->{inf}->{register}->{remotebindpw} eq ""){
+        $setup->msg($FATAL, 'error_missing_parameter', 'remotebindpw');
+        $setup->doExit(1);
+    }
+    if ( !$setup->{inf}->{register}->{admindomain} || $setup->{inf}->{register}->{admindomain} eq ""){
+        $setup->msg($FATAL, 'error_missing_parameter', 'admindomain');
+        $setup->doExit(1);
+    }
+    if ( !$setup->{inf}->{register}->{admindn} || $setup->{inf}->{register}->{admindn} eq ""){
+        $setup->msg($FATAL, 'error_missing_parameter', 'admindn');
+        $setup->doExit(1);
+    }
+    if ( !$setup->{inf}->{register}->{adminpw} || $setup->{inf}->{register}->{adminpw} eq ""){
+        $setup->msg($FATAL, 'error_missing_parameter', 'adminpw');
+        $setup->doExit(1);
+    }
+    if ( !$setup->{inf}->{register}->{destination} || $setup->{inf}->{register}->{destination} eq ""){
+        $setup->msg($FATAL, 'error_missing_parameter', 'destination');
+        $setup->doExit(1);
+    }
+
+    #
+    # Add the configuration instance to the global instance hash
+    #
+    @config_parts = split('::', $setup->{inf}->{register}->{configinst});
+    if($#config_parts < 2 || $#config_parts > 2){
+        $setup->msg($FATAL, 'error_invalid_parameter', " ($#config_parts configinst: $setup->{inf}->{register}->{configinst}");
+        $setup->doExit(1);
+    }
+
+    if ( -d "$setup->{configdir}/$config_parts[0]"){
+        push @{$instances{$setup->{configdir}}}, $config_parts[0];
+    } else {
+        # config instance not found
+        $setup->msg($FATAL, 'error_invalid_parameter', "configinst: $setup->{configdir}/$config_parts[0] does not exist");
+        $setup->doExit(1);
     }
-    elsif ( ! -d $ans )
+
+    #
+    # Add the other instances (if any)
+    #
+    @silent_instances = $setup->{inf}->{register}->{instance};
+    if (@silent_instances){
+        my $i = 0;
+        for (; $i <= $#silent_instances; $i++){
+            my @inst_parts = split('::', $silent_instances[$i]);
+            if($#inst_parts < 2 || $#inst_parts > 2){
+                $setup->msg($FATAL, 'error_invalid_parameter', "instance: $silent_instances[$i]");
+                $setup->doExit(1);
+            }
+            push @{$instances{$setup->{configdir}}}, $inst_parts[0];
+        }
+    }
+
+    #
+    # Fill the setup parameters
+    #
+    $setup->{inf}->{slapd}->{RootDN} = $config_parts[1];
+    $setup->{inf}->{slapd}->{RootDNPwd} = $config_parts[2];
+
+    #
+    # Set the remote registration parameters
+    #
+    $remote_host = $setup->{inf}->{register}->{remotehost};
+    $remote_port = $setup->{inf}->{register}->{remoteport};
+    $remote_binddn = $setup->{inf}->{register}->{remotebinddn};
+    $remote_bindpw = $setup->{inf}->{register}->{remotebindpw};
+    $local_certdir = $setup->{inf}->{register}->{localcertdir};
+    $remote_admindomain = $setup->{inf}->{register}->{admindomain};
+    $destination = $setup->{inf}->{register}->{destination};
+    $localrootdn = $config_parts[1];
+    $localrootpw = $config_parts[2];
+}
+
+#
+# Get existing instances (interactive mode)
+#
+$setup->msg('begin_ds_registration');
+if($silent eq "no"){
+    for my $dir (glob("$setup->{configdir}/slapd-*"))
     {
-        ;
+        if (-d $dir and ($dir !~ /\.removed$/)){
+            my $dname = dirname($dir);
+            my $bname = basename($dir);
+            push @{$instances{$dname}}, $bname;
+        }
     }
-    elsif ( (basename($ans) =~ /^slapd-/) and ($ans !~ /.removed$/) )
+}
+
+#
+# In case Directory Servers are installed at the unexpected location. (interactive mode)
+#
+if ( $silent eq "no"){
+    my $done = 0;
+    while ( !$done && ($ans =  reg_get_response($setup, 'subds_conf_prompt', "@instconfigdir@")) )
     {
-        my $dname = dirname($ans);
-        my $bname = basename($ans);
-        if ( exists $instances{$dname} )
-        {
-            my $addit = 1;
-            foreach my $thisslapd ( @{$instances{$dname}} )
-            {
-                if ( $thisslapd eq $bname )
+        if ( $ans eq "" || !$ans ){
+            $done = 1;
+        } elsif ( ! -d $ans ){
+            ;
+        } elsif ( (basename($ans) =~ /^slapd-/) and ($ans !~ /\.removed$/) ){
+            my $dname = dirname($ans);
+            my $bname = basename($ans);
+            if ( exists $instances{$dname} ){
+                my $addit = 1;
+                foreach my $thisslapd ( @{$instances{$dname}} )
                 {
-                    $addit = 0;
-                    goto out0;
+                    if ( $thisslapd eq $bname ){
+                        $addit = 0;
+                        goto out0;
+                    }
                 }
-            }
 out0:
-            if ( $addit )
-            {
+                if ( $addit ){
+                    push @{$instances{$dname}}, $bname;
+                }
+            } else {
                 push @{$instances{$dname}}, $bname;
             }
-        }
-        else
-        {
-            push @{$instances{$dname}}, $bname;
-        }
-    }
-    else
-    {
-        $ans =~ s/^\s+//;
-        $ans =~ s/[\/\s]+$//;
-        my $rc = opendir(DIR, $ans);
-        if ( $rc )
-        {
-            my $file = "";
-            while ( defined($file = readdir(DIR)) )
-            {
-                next if ( !("$file" =~ /^slapd-/) or ($file =~ /.removed$/) );
-                if ( exists $instances{$ans} )
+        } else {
+            $ans =~ s/^\s+//;
+            $ans =~ s/[\/\s]+$//;
+            my $rc = opendir(DIR, $ans);
+            if ( $rc ){
+                my $file = "";
+                while ( defined($file = readdir(DIR)) )
                 {
-                    my $addit = 1;
-                    foreach my $thisslapd ( @{$instances{$ans}} )
-                    {
-                        if ( $thisslapd eq $file )
+                    next if ( !("$file" =~ /^slapd-/) or ($file =~ /\.removed$/) );
+                    if ( exists $instances{$ans} ){
+                        my $addit = 1;
+                        foreach my $thisslapd ( @{$instances{$ans}} )
                         {
-                            $addit = 0;
-                            goto out1;
+                            if ( $thisslapd eq $file ){
+                                $addit = 0;
+                                goto out1;
+                            }
                         }
-                    }
 out1:
-                    if ( $addit )
-                    {
+                        if ( $addit ){
+                            push @{$instances{$ans}}, $file;
+                        }
+                    } else {
                         push @{$instances{$ans}}, $file;
                     }
                 }
-                else
-                {
-                    push @{$instances{$ans}}, $file;
-                }
+                closedir(DIR);
             }
-            closedir(DIR);
         }
     }
 }
 
-my @instconfigdirs = keys %instances;
+@instconfigdirs = keys %instances;
 if ( $#instconfigdirs < 0 )
 {
     $setup->msg($FATAL, 'error_no_ds');
@@ -212,7 +389,9 @@ foreach my $c ( @instconfigdirs )
     }
 }
 
-# see if there is already a configds
+#
+# See if there is already a configds
+#
 my $admConf = AdminUtil::getAdmConf("$instconfigdir/admin-serv");
 my @admConfKeys = keys %$admConf;
 my $orig_confdsid = "";
@@ -220,9 +399,10 @@ my $new_confdsid = "";
 my $new_confdir = "";
 my $adminuid = "";
 my @errs = ();
-my $fqdn = hostname();
 
-# set defaults
+#
+# Check for an existing Admin Server, then set the defaults
+#
 if ( $#admConfKeys >= 4 ) # admserv.conf, console.conf, httpd.conf, nss.conf
 {
     # Admin Server is installed; that is Config DS exists, which may be
@@ -240,8 +420,18 @@ if ( $#admConfKeys >= 4 ) # admserv.conf, console.conf, httpd.conf, nss.conf
                                                    s/.*:\/\/(.*):[0-9]*\/.*/\1/;
     $setup->{inf}->{admin}->{SysUser} = $admConf->{sysuser};
     $adminuid = $admConf->{userdn};
+    if($silent eq "yes"){
+        if($setup->{inf}->{register}->{adminpw}){
+            $setup->{inf}->{General}->{ConfigDirectoryAdminPwd} = $setup->{inf}->{register}->{adminpw};
+        }
+    }
+    if (!$setup->{inf}->{admin}->{config_dir}){
+        $setup->{inf}->{admin}->{config_dir} = "$instconfigdir/admin-serv";
+    }
 
-    # read additional config from config DS
+    #
+    # Read additional config from config DS
+    #
     my $pset = AdminUtil::getPset($admConf);
     if ($pset && %{$pset}) {
         $setup->{inf}->{admin}->{Port} = $pset->{"configuration.nsserverport"};
@@ -253,76 +443,88 @@ if ( $#admConfKeys >= 4 ) # admserv.conf, console.conf, httpd.conf, nss.conf
         $setup->{inf}->{admin}->{ServerAdminID} = $admpw->{ServerAdminID};
         $setup->{inf}->{admin}->{ServerAdminPwd} = $admpw->{ServerAdminPwd};
     }
-    $setup->{reconfigas} = 1; # allow AS reconfig
-
-    my $dialogmgr = new DialogManager($setup, $res, $TYPICAL);
 
-    require RegDSDialogs;
-
-    my @dialogs = RegDSDialogs->getDialogs();
-
-    $dialogmgr->addDialog(@dialogs);
-
-    my $rc = $dialogmgr->run();
-    if ($rc)
-    {
-        $setup->doExit(1);
+    $setup->{reconfigas} = 1; # allow AS reconfig
+    if($silent eq "no"){
+        require RegDSDialogs;
+        my @dialogs = RegDSDialogs->getDialogs();
+        my $dialogmgr = new DialogManager($setup, $res, $TYPICAL);
+        $dialogmgr->addDialog(@dialogs);
+
+        my $rc = $dialogmgr->run();
+        if ($rc)
+        {
+            $setup->doExit(1);
+        }
     }
-
     $new_confdsid = $setup->{inf}->{slapd}->{ServerIdentifier};
     $new_confdir = $setup->{inf}->{slapd}->{config_dir};
     my $newinst = "slapd-$new_confdsid";
+
     my $inf = createInfFromConfig("$instconfigdir/$newinst", $newinst);
-    if ( ! $inf )
+    if ( !$inf )
     {
-        $setup->msg($FATAL, 'error_create_inf_from_config',
-                                "$instconfigdir/$newinst");
+        $setup->msg($FATAL, 'error_create_inf_from_config', "$instconfigdir/$newinst");
         $setup->doExit(1);
     }
-
     if ( $orig_confdsid ne $new_confdsid )
     {
+        #
         # To switch to the new Config DS, unregister the old one
+        #
         print("\n==============================================================================\n");
         $setup->msg('unregister_old_confds', $orig_confdsid);
-        # If we don't have it, prompt for the Admin password
+        #
+        # If we don't have it, prompt for the Admin password.  Silent install should have set this
+        #
         if (!$setup->{inf}->{General}->{ConfigDirectoryAdminPwd} ||
-            "" eq $setup->{inf}->{General}->{ConfigDirectoryAdminPwd})
+             $setup->{inf}->{General}->{ConfigDirectoryAdminPwd} eq "" )
         {
             $ans = reg_get_passwd($setup, 'input_admin_passwd', $adminuid);
             $setup->{inf}->{General}->{ConfigDirectoryAdminPwd} = $ans;
         }
         while (!unregisterDSWithConfigDS($orig_confdsid, \@errs, $setup->{inf}))
         {
+            if($silent eq "yes"){
+                # silent install can not recover from this error
+                $setup->msg($FATAL, 'error_unregister_ds', $orig_confdsid);
+                $setup->doExit(1);
+            }
             $setup->msg($FATAL, 'error_unregister_ds', $orig_confdsid);
             $ans = reg_get_passwd($setup, 'input_admin_passwd', $adminuid);
             $setup->{inf}->{General}->{ConfigDirectoryAdminPwd} = $ans;
             @errs = ();
         }
-        # updating the port number
+        #
+        # Updating the port number
+        #
         my $oldport = 0;
         my $newport = $inf->{slapd}->{ServerPort};
         ($oldport = $setup->{inf}->{General}->{ldapurl}) =~ s/.*:([0-9]*)\/.*/\1/;
         $setup->{inf}->{General}->{ldapurl} =~ s/$oldport/$newport/;
         $setup->{inf}->{General}->{ConfigDirectoryLdapURL} = "ldap://" . $fqdn . ":". $newport . "/o=NetscapeRoot";
     }
+    #
     # Set the new inf to $setup->{inf}
+    #
     $setup->{inf}->{slapd} = $inf->{slapd};
     $setup->{inf}->{slapd}->{config_dir} = $instconfigdir;
     $setup->{inf}->{slapd}->{Instances} = \%instances;
 }
-else
+elsif($silent eq "no")
 {
+    #
     # Admin Server is not set up.
     # %instances has more than one instance
     # note: this is orig_confdsid is just a candidate...
+    #
     my $orig_confdir = $instconfigdirs[0];
     my @orig_confdsids = @{$instances{$orig_confdir}};
     ($orig_confdsid = $orig_confdsids[0]) =~ s/slapd-(.*)/\1/;
 
     my $originst = "slapd-$orig_confdsid";
     my $inf = createInfFromConfig("$orig_confdir/$originst", $originst);
-    if ( ! $inf )
+    if ( !$inf )
     {
         $setup->msg($FATAL, 'error_create_inf_from_config',
                             "$orig_confdir/$originst");
@@ -333,22 +535,18 @@ else
     $setup->{inf}->{slapd}->{config_dir} = $orig_confdir;
     $setup->{inf}->{slapd}->{Instances} = \%instances;
 
-    my $dialogmgr = new DialogManager($setup, $res, $TYPICAL);
-
+    $dialogmgr = new DialogManager($setup, $res, $TYPICAL);
     require RegDSDialogs;
     require SetupDialogs;
     require ConfigDSDialogs;
     require ASDialogs;
-
-    my @dialogs = RegDSDialogs->getDialogs();
-
+    @dialogs = RegDSDialogs->getDialogs();
     $dialogmgr->addDialog(@dialogs);
-
     my $rc = $dialogmgr->run();
-    if ( $rc )
-    {
+    if ( $rc ){
         $setup->doExit(1);
     }
+
     $new_confdsid = $setup->{inf}->{slapd}->{ServerIdentifier};
     $new_confdir = $setup->{inf}->{slapd}->{config_dir};
     if ( $orig_confdsid ne $new_confdsid )
@@ -357,43 +555,65 @@ else
         $inf = createInfFromConfig("$instconfigdir/$newinst", $newinst);
         if ( ! $inf )
         {
-            $setup->msg($FATAL, 'error_create_inf_from_config',
-                                "$instconfigdir/$newinst");
+            $setup->msg($FATAL, 'error_create_inf_from_config', "$instconfigdir/$newinst");
             $setup->doExit(1);
         }
         $setup->{inf}->{slapd} = $inf->{slapd};
         $setup->{inf}->{slapd}->{Instances} = \%instances;
     }
-    $setup->{inf}->{General}->{ConfigDirectoryLdapURL} = "ldap://" . $fqdn . ":". $setup->{inf}->{slapd}->{ServerPort} . "/o=NetscapeRoot";
+    $setup->{inf}->{General}->{ConfigDirectoryLdapURL} = "ldap://" . $fqdn . ":". 
+        $setup->{inf}->{slapd}->{ServerPort} . "/o=NetscapeRoot";
 
     $dialogmgr->resetDialog();
     @dialogs = SetupDialogs->getRegDialogs();
     push @dialogs, ConfigDSDialogs->getRegDialogs();
     push @dialogs, ASDialogs->getDialogs();
-
     $dialogmgr->addDialog(@dialogs);
-
-    $rc = $dialogmgr->run();
-    if ( $rc )
-    {
+    my $rc = $dialogmgr->run();
+    if ( $rc )    {
         $setup->doExit(1);
     }
+
     $adminuid = $setup->{inf}->{General}->{ConfigDirectoryAdminID};
 }
+else
+{
+    #
+    # This is a silent install, fill in any remaining missing values
+    #
+    my $orig_confdir = $new_confdir = $instconfigdirs[0];
+    my @orig_confdsids = @{$instances{$orig_confdir}};
+    ($new_confdsid = $orig_confdsids[0]) =~ s/slapd-(.*)/\1/;
+    my $originst = "slapd-$new_confdsid";
+    my $inf = createInfFromConfig("$orig_confdir/$originst", $originst);
 
+    if ( !$inf )
+    {
+        $setup->msg($FATAL, 'error_create_inf_from_config',
+                            "$orig_confdir/$originst");
+        $setup->doExit(1);
+    }
+    $setup->{inf}->{slapd} = $inf->{slapd};
+    $setup->{inf}->{slapd}->{Instances} = \%instances;
+}
 
-
-# Get the ConfigDS's rootDN password
+#
+# Add "o=netscaperoot" to the config DS
+#
 print("\n==============================================================================\n");
 $setup->msg('register_new_confds', $new_confdsid);
-$setup->{inf}->{slapd}->{RootDNPwd} =
-    reg_get_passwd($setup, 'input_rootdn_passwd', $new_confdsid);
+
+if ($silent eq "no"){
+    $setup->{inf}->{slapd}->{RootDNPwd} = reg_get_passwd($setup, 'input_rootdn_passwd', $new_confdsid);
+}
 
 if ( ($#admConfKeys >= 0 && ($orig_confdsid ne $new_confdsid)) ||
       $#admConfKeys < 0 )
 {
     @errs = ();
     # First, let's register the Configuration Directory itself
+
+    $setup->{inf}->{slapd}->{RootDNPwd} = $localrootpw;
     while (!createConfigDS($setup->{inf}, \@errs))
     {
         foreach my $err (@errs)
@@ -403,17 +623,22 @@ if ( ($#admConfKeys >= 0 && ($orig_confdsid ne $new_confdsid)) ||
                 goto out;
             }
         }
-        $setup->{inf}->{slapd}->{RootDNPwd} =
-                reg_get_passwd($setup, 'input_rootdn_passwd', $new_confdsid);
+        if(!$setup->{inf}->{slapd}->{RootDNPwd} || $setup->{inf}->{slapd}->{RootDNPwd} eq ""){
+            # silent install should have set this
+            $setup->{inf}->{slapd}->{RootDNPwd} = reg_get_passwd($setup, 'input_rootdn_passwd', $new_confdsid);
+        }
         @errs = ();
     }
 out:
 }
 
+#
 # If we don't have it, prompt for the Admin password
+#
 if (!$setup->{inf}->{General}->{ConfigDirectoryAdminPwd} ||
-    "" eq $setup->{inf}->{General}->{ConfigDirectoryAdminPwd})
+     $setup->{inf}->{General}->{ConfigDirectoryAdminPwd} eq "")
 {
+    # silent install should have set this
     $ans = reg_get_passwd($setup, 'input_admin_passwd', $adminuid);
     $setup->{inf}->{General}->{ConfigDirectoryAdminPwd} = $ans;
 }
@@ -421,6 +646,11 @@ if (!$setup->{inf}->{General}->{ConfigDirectoryAdminPwd} ||
 @errs = ();
 while (!registerDSWithConfigDS($new_confdsid, \@errs, $setup->{inf}))
 {
+    if($silent eq "yes"){
+        # silent install can not recover
+        $setup->msg($FATAL, 'error_register_configds', $new_confdsid);
+        $setup->doExit(1);
+    }
     $setup->msg($WARN, 'error_register_configds', $new_confdsid);
     $ans = reg_get_passwd($setup, 'input_admin_passwd', $adminuid);
     $setup->{inf}->{General}->{ConfigDirectoryAdminPwd} = $ans;
@@ -428,7 +658,10 @@ while (!registerDSWithConfigDS($new_confdsid, \@errs, $setup->{inf}))
 }
 
 my $hassubinst = 0;
+
+#
 # Then, register the rest of the Directory Servers, if any
+#
 my %subinstances = ();    # hash without the Config DS
 %instances = %{$setup->{inf}->{slapd}->{Instances}};
 foreach my $subconfdir (keys %instances)
@@ -439,28 +672,37 @@ foreach my $subconfdir (keys %instances)
         if ( ("$subinst" ne "slapd-" . $new_confdsid) ||
              ($subconfdir ne $new_confdir) )
         {
-            if ( 0 == $hassubinst )
-            {
+            if ( 0 == $hassubinst){
                 $hassubinst = 1;
                 print("\n==============================================================================\n");
                 $setup->msg('register_subds');
             }
             my $subid = $subinst;
             $subid =~ s/slapd-//;
-            my $passwd =
-                reg_get_passwd($setup, 'input_rootdn_passwd_sub', $subid, $subid);
+
+            if ($silent eq "yes"){
+                # Get the password from silent install config
+                ($rootdn, $passwd) = get_cred_from_inst($setup, $subinst);
+                if($rootdn ne ""){
+                    $setup->{inf}->{slapd}->{RootDN} = $rootdn;
+                }
+            } else {
+                $passwd = reg_get_passwd($setup, 'input_rootdn_passwd_sub', $subid, $subid);
+            }
+
             # if the password is not given, we don't register the server
             next if ( "" eq $passwd || !$passwd );
+
             my $subinf = createInfFromConfig("$subconfdir/$subinst", $subinst);
-            if ( ! $subinf )
-            {
-                $setup->msg($FATAL, 'error_create_inf_from_config',
-                                    "$subconfdir/$subinst");
+            if ( !$subinf ){
+                $setup->msg($FATAL, 'error_create_inf_from_config', "$subconfdir/$subinst");
             }
             else
             {
+                #
                 # If we're switching the config DS, we want to force updating the
                 # PTA plug-in since it's configured for the old config DS.
+                #
                 my $force_pta = 0;
                 if ( $orig_confdsid ne $new_confdsid ) {
                     $force_pta = 1;
@@ -469,19 +711,20 @@ foreach my $subconfdir (keys %instances)
                 $setup->{inf}->{slapd} = $subinf->{slapd};
                 $setup->{inf}->{slapd}->{RootDNPwd} = $passwd;
                 push @{$subinstances{$subconfdir}}, $subinst;
-                $done = 0;
+                my $done = 0;
                 while ( !$done && !createSubDS($setup->{inf}, \@errs, $force_pta) )
                 {
                     $setup->msg($FATAL, @errs);
+                    if($silent eq "yes"){
+                        # silent install can not recover
+                        $setup->doExit(1);
+                    }
                     $passwd = reg_get_passwd($setup, 'input_rootdn_passwd_sub',
                                                         $subid, $subid);
-                    if ( "" eq $passwd || !$passwd )
-                    {
+                    if ( "" eq $passwd || !$passwd ){
                         $done = 1;
                         pop @{$subinstances{$subconfdir}};
-                    }
-                    else
-                    {
+                    } else {
                         $setup->{inf}->{slapd}->{RootDNPwd} = $passwd;
                     }
                 }
@@ -502,49 +745,208 @@ foreach my $subconfdir (keys %instances)
 }
 
 my @subkeys = keys %subinstances;
-if ( $#subkeys >= 0 )
-{
+if ( $#subkeys >= 0 ){
     @errs = ();
-    if ( !registerScatteredDSWithConfigDS($setup->{inf}, \@errs, \%subinstances) )
-    {
+    if ( !registerScatteredDSWithConfigDS($setup->{inf}, \@errs, \%subinstances) ){
         $setup->msg($FATAL, @errs);
         $setup->doExit(1);
     }
 }
 
+#
 # Configure and register the admin server instance.
 # Generate a new inf for the config DS and override
 # the old slapd data from the last instance we registered.
+#
+print("\n==============================================================================\n");
 $new_confdir = $setup->{inf}->{slapd}->{config_dir};
 my $newinst = "slapd-$new_confdsid";
 my $inf = createInfFromConfig("$instconfigdir/$newinst", $newinst);
-if ( ! $inf )
+if ( !$inf )
 {
     $setup->msg($FATAL, 'error_create_inf_from_config', "$instconfigdir/$newinst");
     $setup->doExit(1);
 }
 $setup->{inf}->{slapd} = $inf->{slapd};
 
-# need these manually set these 2 parameters
+# need to manually set these 2 parameters
 $setup->{inf}->{slapd}->{UseExistingMC} = "yes";
 $setup->{inf}->{slapd}->{SlapdConfigForMC} = "yes";
 if ( !$setup->{reconfigas} )
 {
-    if ( !createAdminServer($setup) )
-    {
+    if ( !createAdminServer($setup) ){
         $setup->msg($FATAL, 'error_create_adminserver');
         $setup->doExit(1);
     }
+} else {
+    if ( !reconfigAdminServer($setup) ){
+        $setup->msg($FATAL, 'error_reconfig_adminserver');
+        $setup->doExit(1);
+    }
 }
-else
+
+#
+# Check if we are registering to a remote config, or adding a instance to our local config
+#
+if ($silent eq "no")
 {
-    if ( !reconfigAdminServer($setup) )
+    print("\n==============================================================================\n");
+    $setup->msg(0, 'remote_register_local_prompt');
+    my $answer;
+    chomp($answer = <>);
+    if ($answer eq "y"){
+        $destination = "local";
+    } else {
+        print("\n==============================================================================\n");
+        $setup->msg(0, 'remote_register_remote_prompt');
+        chomp($answer = <>);
+        if ($answer eq "y")
+        {
+            $destination = "remote";
+        }
+    }
+    if($destination ne "")
     {
-        $setup->msg($FATAL, 'error_reconfig_adminserver');
+        print "\n";
+        $setup->msg(0, 'remote_prompt', "hostname");
+        while($remote_host eq ""){
+            chomp($remote_host = <>);
+        }
+
+        $setup->msg(0, 'remote_prompt', "port");
+        while($remote_port eq ""){
+            chomp($remote_port = <>);
+        }
+
+        $setup->msg(0, 'remote_prompt', "bind DN");
+        while($remote_binddn eq ""){
+            chomp($remote_binddn = <>);
+        }
+
+        $setup->msg(0, 'remote_prompt', "bind password");
+        while($remote_bindpw eq ""){
+            chomp($remote_bindpw = <>);
+        }
+
+        $setup->msg(0, 'remote_conn_use_ssl');
+        chomp($local_certdir = <>);
+
+        $setup->msg(0, 'remote_prompt', "admin domain[$setup->{inf}->{General}->{AdminDomain}]");
+        chomp($remote_admindomain = <>);
+        if ($remote_admindomain eq ""){
+            $remote_admindomain = $setup->{inf}->{General}->{AdminDomain};
+        }
+    }
+}
+
+#
+# We are registering with a remote server(locally or remotely)
+#
+# So we search the origin o=netscaperoot (admin domain), and simply add
+# those entries to the destination config server.
+#
+if($remote_host)
+{
+    #
+    # Open connections to remote and local servers
+    #
+    my $connRemote = new Mozilla::LDAP::Conn($remote_host,
+                                             $remote_port,
+                                             $remote_binddn,
+                                             $remote_bindpw,
+                                             $local_certdir);
+    my $errstr = "Success";
+    if ($connRemote) {
+        $errstr = $connRemote->getErrorString();
+    }
+    if (!$connRemote or ($errstr ne "Success")) {
+        # error
+        if ($errstr eq "Success"){
+            $errstr = "";
+        }
+        $setup->msg($FATAL, 'error_connection', $remote_host, $errstr);
+        $setup->msg($FATAL, 'remote_register_error');
+        $setup->doExit(1);
+    }
+
+    #
+    # Open a connection to the local server
+    #
+    my $connLocal = new Mozilla::LDAP::Conn($fqdn, $inf->{slapd}->{ServerPort}, $localrootdn, $localrootpw);
+    if ($connLocal) {
+        $errstr = $connLocal->getErrorString();
+    }
+    if (!$connLocal or ($errstr ne "Success")) {
+        # error
+        if ($errstr eq "Success"){
+            $errstr = "";
+        }
+        $setup->msg($FATAL, 'error_connection', $fqdn, $errstr);
+        $setup->msg($FATAL, 'remote_register_error');
         $setup->doExit(1);
     }
+
+    if ($destination eq "local"){
+        #
+        # Register a remote server with our local configuration server
+        #
+        print "Searching local configuration server...\n";
+        my $entry = $connRemote->search("ou=$remote_admindomain,o=netscaperoot", "sub", "(objectclass=*)");
+        if (!$entry){
+            $setup->msg($FATAL, 'error_operation', "searching" ,"ou=$remote_admindomain,o=netscaperoot", $connLocal->getErrorString());
+            $connLocal->close();
+            $connRemote->close();
+            $setup->doExit(1);
+        } else {
+            print "Adding remote configuration entries to local configuration server...\n";
+            while ($entry){
+                $connLocal->add($entry);
+                if($connLocal->getErrorCode() != 0 && $connLocal->getErrorCode() != 68){
+                    $setup->msg($FATAL, 'error_operation', 'adding', $entry->getDN(), $connLocal->getErrorString());
+                    $setup->msg($FATAL, 'remote_register_error');
+                    $connLocal->close();
+                    $connRemote->close();
+                    $setup->doExit(1);
+                }
+                $entry = $connRemote->nextEntry();
+            }
+        }
+        $setup->msg(0, 'remote_register_completed', $remote_host, $fqdn );
+    } else {
+        #
+        # Register our local server with a remote configuration server
+        #
+        print "Searching remote configuration server...\n";
+        my $entry = $connLocal->search("ou=$setup->{inf}->{General}->{AdminDomain},o=netscaperoot", "sub", "(objectclass=*)");
+        if (!$entry){
+            $setup->msg($FATAL, 'error_operation', "searching", $entry->getDN(), $connLocal->getErrorString());
+            $setup->msg($FATAL, 'remote_register_error');
+            $connLocal->close();
+            $connRemote->close();
+            $setup->doExit(1);
+        } else {
+            print "Adding local configuration entries to remote configuration server...\n";
+            while ($entry){
+                $connRemote->add($entry);
+                if($connRemote->getErrorCode() != 0 && $connRemote->getErrorCode() != 68){
+                    $setup->msg($FATAL, 'error_connection', 'adding', $entry->getDN(), $connRemote->getErrorString());
+                    $setup->msg($FATAL, 'remote_register_error');
+                    $connLocal->close();
+                    $connRemote->close();
+                    $setup->doExit(1);
+                }
+                $entry = $connLocal->nextEntry();
+            }
+        }
+        $setup->msg(0, 'remote_register_completed', $setup->{inf}->{General}->{FullMachineName}, $remote_host );
+    }
+
+    # Close up our connections
+    $connLocal->close();
+    $connRemote->close();
 }
-$setup->msg('end_ds_registration');
 
+print("\n==============================================================================\n");
+$setup->msg('end_ds_registration');
 $setup->doExit(0);
 
diff --git a/admserv/newinst/src/register-ds-admin.res.in b/admserv/newinst/src/register-ds-admin.res.in
index df022bf..99cb3e3 100644
--- a/admserv/newinst/src/register-ds-admin.res.in
+++ b/admserv/newinst/src/register-ds-admin.res.in
@@ -1,6 +1,6 @@
-begin_ds_registration = Beginning registration of the Directory Server
+begin_ds_registration = Beginning registration of the Directory Server\n
 
-use_existing_configds_txt = Do you want to use this server as Configuration Directory Server?\n\n
+use_existing_configds_txt = Do you want to use this server as Configuration Directory Server? \n\n
 
 subds_conf_prompt = The Directory Server locates its configuration file (dse.ldif) at %s/slapd-ID, by default.  If you have Directory Server(s) which configuration file is put at the other location, you need to input it to register the server.\n\nIf you have such Directory Server, type the full path that stores the configuration file.\n\nIf you don't, type return.\n[configuration directory path or return]: 
 
@@ -10,9 +10,9 @@ unregister_old_confds = Cleaning up old Config DS: %s\n
 
 register_new_confds = Registering new Config DS: %s\n
 
-register_subds = Registering Sub DSes:
+register_subds = Registering Sub Directory Servers:\n
 
-end_ds_registration = Finished registration of the Directory Server
+end_ds_registration = Finished registration of the Directory Server\n
 
 input_rootdn_passwd = Input the Directory Manager password on the server %s: 
 
@@ -40,4 +40,28 @@ error_register_subds = Error: failed to register the additional server info to t
 
 error_unregister_ds = Error: failed to clean up the configuration info from the old Configuration\nDirectory Server %s.\n
 
-post_create_subds = Instance '%s' was registered.\n\nYou must restart '%s' in order to complete console registration.\n
+post_create_subds = Instance '%s' was successfully registered.\n
+
+error_missing_parameter = Silent install file missing required parameter '%s'\n
+
+error_invalid_parameter = Silent install parameter '%s' is invalid\n
+
+error_connection = Error connecting to host '%s' - %s\n
+
+error_operation = Error while %s entry '%s' - %s\n
+
+error_silent_install = Silent setup misconfiguration.  Must have 'General' and 'admin', or 'register' directives\n
+
+remote_register_error = The remote registration process failed!\n
+
+remote_register_local_prompt = Do you want to register a remote server with the local configuration server (y/n)? 
+
+remote_register_remote_prompt = Do you want to register with a remote configuration server (y/n)? 
+
+remote_register_direction = Register local servers to 
+
+remote_prompt = Enter Remote %s: 
+
+remote_conn_use_ssl = If using SSL, enter certificate db directory: 
+
+remote_register_completed = Successfully registered host '%s' with the configuration server on '%s'.\n
diff --git a/man/man8/register-ds-admin.pl.8 b/man/man8/register-ds-admin.pl.8
index a54537c..eb85bba 100644
--- a/man/man8/register-ds-admin.pl.8
+++ b/man/man8/register-ds-admin.pl.8
@@ -21,11 +21,15 @@ register\-ds\-admin.pl \- Registers Directory Server instances with an Admin Ser
 .B register-ds-admin.pl
 [\fI--options\fR] \fI-- \fR[\fIargs\fR]
 .SH DESCRIPTION
-Registers existing Directory Server instances with an existing Admin Server.
-This command does the set up necessary for the use of the Console to manage
-the Directory Server instances you are registering.
+Registers existing Directory Server instances with an existing Admin Server. 
+This command does the set up necessary for the use of the Console to manage 
+the Directory Server instances you are registering.  You can register remote 
+Directory Server instances to a local Admin Server, as well as register local 
+Directory Server instances with a remote Admin Server - this allows a single 
+console/Admin Server to manage all your Directory Servers on your network.
 
-Use this command with the \fB--update\fR option after an upgrade to refresh the server information (version, build number, etc.) in the Console.
+Use this command with the \fB--update\fR option after an upgrade to refresh
+ the server information (version, build number, etc.) in the Console.
 
 Can be run in interactive mode with different levels of verbosity, or
 in silent mode with parameters supplied in a .inf format file or
@@ -64,8 +68,9 @@ Update an existing installation (e.g. after upgrading packages)
 .B \fB\-\-continue
 (update only) keep going despite errors (also --force)
 .PP
-For all options, you can also use the short name e.g. \fB\-h\fR, \fB\-d\fR, etc.  For the \fB\-d\fR argument,
-specifying it more than once will increase the debug level e.g. \fB\-ddddd\fR
+For all options, you can also use the short name e.g. \fB\-h\fR, \fB\-d\fR, etc.  
+For the \fB\-d\fR argument, specifying it more than once will increase the debug 
+level e.g. \fB\-ddddd\fR
 .PP
 args:
 You can supply default .inf data in this format:
@@ -78,10 +83,128 @@ General.FullMachineName=foo.example.com
 .PP
 or
 .IP
-"slapd.Suffix=dc=example, dc=com"
+"slapd.Suffix=dc=example,dc=com"
 .PP
-Values passed in this manner will override values in an .inf file given with the \fB\-f\fR argument.
+Values passed in this manner will override values in an .inf file given with 
+the \fB\-f\fR argument.
+
+.SH Silent Mode (.inf file)
+.PP
+Here is an example of an .inf file that registers a local Directory Server instance with a remote server over SSL:
+.IP
+[General]
+.br
+FullMachineName= localhost.localdomain
+.br
+SuiteSpotUserID= nobody
+.br
+SuiteSpotGroup= nobody
+.br
+AdminDomain= redhat.com
+.br
+ConfigDirectoryAdminID= admin
+.br
+ConfigDirectoryAdminPwd= admin
+.br
+ConfigDirectoryLdapURL= ldap://localhost.localdomain:389/o=NetscapeRoot
+.br
+
+.br
+[admin]
+.br
+Port= 9830
+.br
+ServerIpAddress= 127.0.0.1
+.br
+ServerAdminID= admin
+.br
+ServerAdminPwd= password
+.br
+
+.br
+[register]
+.br
+configinst= slapd-localhost::cn=directory manager::password
+.br
+instance= slapd-replica::cn=directory manager::password
+.br
+remotehost= ldap.redhat.com
+.br
+remoteport= 636
+.br
+remotebinddn= cn=directory manager
+.br
+remotebindpw= password
+.br
+localcertdir= /etc/dirsrv/slapd-localhost
+.br
+admindomain= redhat.com
+.br
+admindn= uid=admin,ou=Administrators,ou=TopologyManagement,o=NetscapeRoot
+.br
+adminpw= password
+.br
+destination= remote
+.br
+
+.br
+The above .inf will install a local admin server and then store its configuration (o=netscaperoot) 
+in the "config" instance.  The second local Directory Server instance, slapd-replica, is also 
+added the configuration server.  Then this server's configuration to added to the 
+remote configuration server on ldap.redhat.com
+.PP
+The \fB[register]\fR directive parameter descriptions:
+.TP
+.B configinst\fR = INSTANCE::BIND_DN::PASSWORD\fR
+.br
+.IP
+.I INSTANCE\fR = The server where the existing configuration exists, or the server where you want 
+to add the configuration to. The value takes the form of slapd-INSTANCE.
+.br
+.I BIND_DN\fR = The root DN, usually \*(lqcn=directory manager\*(rq, for the local configuration server.
+.br
+.I PASSWORD\fR = the root DN password.
+.TP
+.B instance\fR = INSTANCE::BIND_DN::PASSWORD\fR
+.br
+.IP
+.I INSTANCE\fR = Additional local Directory Server instance that should be added to the 
+configuration server. The value takes the form of slapd-INSTANCE.
+.br
+.I BIND_DN\fR = The root DN, usually \*(lqcn=directory manager\*(rq, for the local server.
+.br
+.I PASSWORD\fR = the root DN password.
+.br
+
+This is an optional parameter for specifying other local Directory Server instances to register with the
+configuration server.
+.br 
+.TP
+.B remotehost\fR = The FQDN of the remote server.  This is either the rmeote server we 
+registering with, or the remote server that is being registered with the local configuration 
+server.
+.TP
+.B remoteport\fR = The port of the remote server.
+.TP
+.B remotebinddn\fR = The bind DN, preferrably the root DN, to connect to the remote server.
+.TP
+.B remotebinddn\fR = The password for the remotebinddn entry.
+.TP
+.B localcertdir\fR = The directory of the certificate database files (e.g. cert8.db, key3.db). This 
+optional parameter is only used if connecting to the remote server over SSL.
+.TP
+.B admindomain\fR = The admin domain of the remote configuration server.
+.TP
+.B admindn\fR = The local administrator DN for o=netscaperoot
+.TP 
+.B adminpw\fR = The local administrator password.
+.TP
+.B destination\fR = \fBlocal\fR, or \fBremote\fR
+.br
+.IP
+.I local\fR = Registers the remote server with the local configuration server.
 .br
+.I remote\fR = Registers the local server with the remote configuration server.
 .SH AUTHOR
 register-ds-admin.pl was written by the 389 Project.
 .SH "REPORTING BUGS"
-- 
1.9.3

