#!/usr/bin/perl
# 
# usage:
#		key4node MISSING_NODES
#		key4node <UNI> <HOST> <IP>
#
# ATTN! run from sudo, no external calls permitted
# return sample in case of MISSING_NODES :
#   few nodes avaiable for replacement:
#     MASTER=3.7.0 STATUS=run MISSINGNODES=2
#     UNI=F1XfkRj55j9vKNH3FnYCzA        IP=207.207.163.159      HOST=skm370
#     UNI=PyFv7xf9CFZYdkIlPML8Pw        IP=207.207.163.108      HOST=ref57
#   no avaiable nodes:
#     MASTER=3.7.0 STATUS=run MISSINGNODES=0
#   master is not running:
#     MASTER=3.7.0 STATUS=stop MISSINGNODES=UNKNOWN
#   master is not detected:
#     MASTER=NOTDETECTED STATUS=UNKNOWN MISSINGNODES=UNKNOWN
#   authorization error:
#     MASTER=NOTAUTHORIZED STATUS=UNKNOWN MISSINGNODES=UNKNOWN

use strict;

my $APL="/opt/sarch";
my $APL_VAR="/var/sarch";
my $UNI=$ARGV[0];
my $HOST=$ARGV[1];
my $IP=$ARGV[2];
my $SOS_SSH='/home/sos/.ssh';

sub ClenAll {
  unlink "$SOS_SSH/apl-$UNI.pub";
  unlink "$SOS_SSH/host-$UNI.pub";
  unlink "$SOS_SSH/authorized_keys";
}

sub ErrExit {
   my ($err,$msg)=@_;
   print "ERROR $err: $msg\n";
   ClenAll;
   exit $err;
}

sub setup_keys {
  print "UNI=$UNI HOST=$HOST IP=$IP\n";
  #----------------------------   check argument
  ErrExit( 1,"UNI  is not provided or incorrect") if $UNI!~/^\w{22}$/;
  ErrExit( 2,"HOST is not provided or incorrect") if $HOST !~/^[\w\.\-]+$/;
  ErrExit( 3,"IP   is not provided or incorrect") if $IP !~/^\d+\.\d+\.\d+\.\d+$/;
  #`echo $IP >/tmp/ip.log`;

  #ErrExit(10,"UNI=$UNI is already registered") if -f "$APL_VAR/conf/master/nodes/$UNI";
  ErrExit(11,"Server is not a master. Node cannot be registred") if not -f "$APL_VAR/conf/master/s_master";

  #---------------------------- load keys for smx 
  system("/opt/sarch/smix/bin/keymanager import $SOS_SSH/smx-$UNI.cert $UNI");
  ErrExit(18,"Failed to import node '$UNI' certificate into keystore") if ($? != 0);

  #---------------------------- read apl public key and check UNI
  open(PUB,"$SOS_SSH/apl-$UNI.pub") or ErrExit(20,"Apl key (apl-$UNI.pub) is missing");
  my $key;
  while(<PUB>) {
    $key=$_ if /$UNI/;
  }
  close PUB;
  ErrExit(25,"Key for UNI=$UNI is not found") if ! $key;

  #---------------------------- authorize key, add|replace apl's public key
  my @list;
  if(open(LIST,"$APL_VAR/home/.ssh/authorized_keys")) {
    @list= grep { not /$UNI$/ } <LIST>; 
    close LIST;
  }
  push(@list,$key);
  open(LIST,">$APL_VAR/home/.ssh/authorized_keys") or  ErrExit(30,'Cannot open for writing authorized_keys');
  foreach(@list) {
    print LIST $_;
  }
  close LIST or ErrExit(32,'Cannot close authorized_keys');
  #---------------------------- know hosts, add|replace public key
  open(PUB,"$SOS_SSH/host-$UNI.pub") or ErrExit(30,"host key is missing");
  my $host;
  while(<PUB>) {
    $host=$_ if /^ssh-/;
  }
  close PUB;
  ErrExit(35,"host key for UNI=$UNI is not found") if ! $host;

  my @hlist;
  if(open(HLIST,"$APL_VAR/home/.ssh/known_hosts")) {
    @hlist= grep { not /^$UNI/ } <HLIST>;
    close HLIST;
  }
  push(@hlist,"$UNI,$HOST,$IP $host");
  open(HLIST,">$APL_VAR/home/.ssh/known_hosts") or  ErrExit(37,'Cannot open for writing known_hosts');
  foreach(@hlist) {
    print HLIST $_;
  }
  close HLIST or ErrExit(39,'Cannot close known_hosts');
  #---------------------------- COMPLETED
  print "COMPLETED\n";
  ClenAll;
}


sub missing_nodes {
   my %master=(MASTER=>'UNKNOWN',STATUS=>'UNKNOWN',MISSINGNODES=>'UNKNOWN');
   my @nodes;
   my $DEADAFTER=60; # node is dead if no updates for 60 seconds
   if( -f "$APL_VAR/conf/master/s_master") {# ------ master version
       if(open(VER,"$APL/base/etc/VERSION")) {
        my $label=<VER>;
        close VER;
        $master{MASTER}=$1 if $label=~/-(\d+\.\d+\.\d+)-/;
      }
   }else {
      $master{MASTER}='NOTDETECTED';
   }

   if( -r "$APL_VAR/wd/status") {     # ------------ status information 
     open STATUS, "$APL_VAR/wd/status";
     $master{STATUS}=<STATUS>;
     close STATUS;
     chomp $master{STATUS};
   }

   if($master{STATUS} eq 'run') {     # ------------ collect nodes only if system is running
     if(opendir(DIR,"$APL_VAR/conf/master/nodes")) {
       my @dir=grep{/^\w{22}$/} readdir (DIR);
       close DIR;
       foreach my $id (@dir) {  # reading actual conf files
         my $confname="$APL_VAR/conf/master/nodes/$id";
         next if not -f $confname;
         my $mtime = (stat($confname))[9];
         my ($alive,$dead)=(time-$mtime>$DEADAFTER)?(0,$mtime):($mtime,0);
         if (open(CONF,$confname)) {
           my %conf=map{/(^\w+)=(.+)/} grep {/^\w+=.+/} <CONF>;
           close CONF;
           push(@nodes,\%conf) if $dead;
         }
       }
     }
     $master{MISSINGNODES}=scalar @nodes;
   }
   #                                    ------------ results
   print "MASTER=$master{MASTER} STATUS=$master{STATUS} MISSINGNODES=$master{MISSINGNODES}\n";
   foreach my $node (@nodes) {
     print "UNI=$node->{UNI}\t IP=$node->{IP}\tHOST=$node->{HOST}\n";
   }
}

if($UNI eq 'MISSING_NODES') { missing_nodes() }
else                        { setup_keys()    }

      
   


