#!/usr/bin/perl -w
###############################################################################
# $Id$
###############################################################################
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
###############################################################################

=head1 NAME

vclclientd

=head1 SYNOPSIS

 perl vclclientd

=head1 DESCRIPTION

 This is the executable module for running the VCL client daemon on Linux lab
 machines.

=cut

##############################################################################

use strict;
use Getopt::Long;
use diagnostics;
use Symbol;
use POSIX;

$| = 1;    # turning off autoflush

# -- DEVELOPMENT testing
#my $PIDFILE = "/var/run/vcldev.pid";
#our $LOG = "/var/log/vcldev.log";

# GLOBALS
our $HOME       = "/home/vclstaff";
our $VCLFLAG    = "$HOME/flag";
our $CLIENTDATA = "$HOME/clientdata";
our $PIDFILE    = "/var/run/vclclientd.pid";
our %children   = ();                                                                                                   # keys are current child process IDs
our $children   = 0;                                                                                                    # current number of children
our $LOG        = "/var/log/vclclientd.log";
our %ERRORS     = ('DEPENDENT' => 4, 'UNKNOWN' => 3, 'OK' => 0, 'WARNING' => 1, 'CRITICAL' => 2, 'MAILMASTERS' => 5);
our $opt_d      = '';

Getopt::Long::Configure('bundling', 'no_ignore_case');
GetOptions('d|debug' => \$opt_d);
if (!$opt_d) {
	#daemonize
	&daemonize;
}


sub daemonize {
	chdir '/' or die "Can't chdir to /: $!";
	defined(my $pid = fork) or die "Can't fork $!";
	exit if $pid;
	#production
	$0 = "vclclientd";
	print "Created process $$ renamed to $0 ...\n";
	setsid or die "Can't start a new session: $!";
	open STDIN,  '/dev/null' or die "Can't read /dev/null $!";
	open STDOUT, '>>$LOG'    or die "Can't write $LOG $!";
	open STDERR, '>>$LOG'    or die "Can't write $LOG $!";
	umask 0;
	open(PIDFILE, ">$PIDFILE");    # so I can kill myself easily
	print PIDFILE $$;
	close(PIDFILE);

} ## end sub daemonize


#------- Subroutine declarations -------
sub main();                       # main calls primary subroutines
sub flag;
sub processdata;
sub startsshd;
sub makedatestring;
sub reboot;
sub fetch;
sub store;
sub sshdstatus;
sub createnewssh_config_vcl;
sub restartsshd;

sub REAPER {                      # takes care of dead children
	$SIG{CHLD} = \&REAPER;
	my $pid = wait;
	if (exists $children{$pid}) {
		$children--;
		notify($ERRORS{'OK'}, "$pid -- child process exiting, deleting $pid ");
		delete $children{$pid};
	}
	else {
		notify($ERRORS{'OK'}, "$pid -- sub process exiting");
	}

} ## end sub REAPER

sub HUNTSMAN {    # signal handler for SIGINT
	local ($SIG{CHLD}) = 'IGNORE';    # we're going to kill our child processes
	kill 'INT' => keys %children;
	notify($ERRORS{'OK'}, "$$ -- process exiting");
	exit;                             # clean up with dignity
}

# Install signal handlers.
$SIG{CHLD} = \&REAPER;
$SIG{INT}  = \&HUNTSMAN;
$SIG{QUIT} = \&HUNTSMAN;
$SIG{HUP}  = \&HUNTSMAN;
$SIG{TERM} = \&HUNTSMAN;

main();

sub main () {
	#preplogfile;
	#my @hostinfo = hostname;
	#make sure vclstaff owns authorized_keys and log file
	if (open(AUTHFILE, "chown vclstaff:root /home/vclstaff/authorized_keys 2>&1 |")) {
		notify($ERRORS{'OK'}, "main: setting vclstaff ownership of /home/vclstaff/authorized_keys");
		close(AUTHFILE);
	}
	if (open(LOGFILE, "chown vclstaff:root /var/log/vclclientd.log 2>&1 |")) {
		notify($ERRORS{'OK'}, "main: setting vclstaff ownership of /var/log/vclclientd.log");
		close(LOGFILE);
	}
	if (!(-r "/etc/users.local.admin")) {
		notify($ERRORS{'OK'}, "main: /etc/users.local.admin does not exist creating");
		if (open(COPY, "/bin/cp /etc/users.local /etc/users.local.admin |")) {
			close(COPY);
			if (-r "/etc/users.local.admin") {
				notify($ERRORS{'OK'}, "main: /etc/users.local.admin exist now");
			}
		}

	} ## end if (!(-r "/etc/users.local.admin"))
	#on startup check to see if someone has rebooted us. this is a hack
	#we just need to figure out if this a reboot or a restart
	#for now we are just going to look at the output of last -- there has
	#to be a better way
	if (open(LAST, "/usr/bin/last 2>&1 |")) {
		my @last = <LAST>;
		close(LAST);
		if ($last[0] =~ /reboot/ || $last[1] =~ /reboot/) {
			if (-r "$CLIENTDATA") {
				if (open(CLD, "$CLIENTDATA")) {
					my @file = <CLD>;
					close(CLD);
					if ($file[0] =~ /new/) {
						if (open(FLAG, ">$VCLFLAG")) {
							print FLAG 1;
							close(FLAG);
							notify($ERRORS{'OK'}, "main: possibly a reboot setting flag to 1 for reinitializing");
						}    #flag
					}    #new
				}    #CLD
				else {
					notify($ERRORS{'OK'}, "main: could not open $CLIENTDATA");
				}
			}    # readable
		}    # last -- reboot
	} ## end if (open(LAST, "/usr/bin/last 2>&1 |"))


	while (1) {
		if (flag) {
			notify($ERRORS{'OK'}, "main: flag is set proceed to process");
			#make sure clientdata is readable
			if (-r $CLIENTDATA) {
				#process data
				if (open(CLIENTDATA, "$CLIENTDATA")) {
					my %request = ();
					my @lines   = <CLIENTDATA>;
					close(CLIENTDATA);
					$request{"state"}    = $lines[0];
					$request{"unityid"}  = $lines[1];
					$request{"remoteIP"} = $lines[2];
					chomp($request{"state"});
					chomp($request{"unityid"});
					chomp($request{"remoteIP"});

					make_new_child(%request) if ($request{"state"} =~ /new|timeout|delete/);
					reboot()                 if ($request{"state"} =~ /reboot/);
					fetch()                  if ($request{"state"} =~ /fetch/);
					store()                  if ($request{"state"} =~ /store/);
				} ## end if (open(CLIENTDATA, "$CLIENTDATA"))
				else {
					notify($ERRORS{'OK'}, "main: could not open $CLIENTDATA: $!");

				}
			}    #if -r
		}    #if flag
		else {
			#check for any hung children
			#kill fork process and reset flag? does this create a race condition
			#could keep track of killed processes in children hash when number exceeds X (reboot machine?)
			#notify($ERRORS{'OK'},"main: number of children  $children");
			foreach my $p (keys %children) {
				next if ($p =~ /hung/);
				notify($ERRORS{'OK'}, "main: pid = $p");
				$children{"hungtries"}{"count"} += 1;
				if (open(KILL, "kill -9 $p 2>&1 |")) {
					notify($ERRORS{'OK'}, "main: stopping forked process in an attempt to reset");
					my $k = <KILL>;
					close(KILL);
					if ($k =~ /No such process/) {
						#not found maybe I was too guick to judge
						#let it ride
					}
					else {
						if ($children{"hungtries"}{"count"} > 4) {
							notify($ERRORS{'OK'}, "main: hung process attempts are greater than 4 rebooting");
							reboot();
						}
						if (open(ECHO, "echo 1 > /home/vclstaff/flag |")) {
							notify($ERRORS{'OK'}, "main: attempt to reset initiated");
							close(ECHO);
						}
					} ## end else [ if ($k =~ /No such process/)
				} ## end if (open(KILL, "kill -9 $p 2>&1 |"))
			} ## end foreach my $p (keys %children)

		} ## end else [ if (flag)
		sleep 5;
	}    #while
} ## end sub main ()
sub flag {
	if (!(-e $VCLFLAG)) {
		# warning flag does not exist
		#create it and continue
		if (open(FLAG, ">$VCLFLAG")) {
			print FLAG 0;
			notify($ERRORS{'OK'}, "had to create $VCLFLAG");
			close(FLAG);
			if (open(LOGFILE, "chown vclstaff:root /home/vclstaff/flag 2>&1 |")) {
				notify($ERRORS{'OK'}, "main: setting vclstaff ownership of /home/vclstaff/flag");
				close(LOGFILE);
			}
			if (open(LOGFILE, "chmod 640 /home/vclstaff/flag 2>&1 |")) {
				notify($ERRORS{'OK'}, "main: setting 640 perms /home/vclstaff/flag");
				close(LOGFILE);
			}
		} ## end if (open(FLAG, ">$VCLFLAG"))
		else {
			notify($ERRORS{'OK'}, "could not create $VCLFLAG $! will try to delete");
			unlink $VCLFLAG;
			return 0;
		}
	} ## end if (!(-e $VCLFLAG))
	my @lines;
	# VCLFLAG file exists, check contents
	if (open(FLAG, "$VCLFLAG")) {
		@lines = <FLAG>;
		close(FLAG);
		# clear flag
		if (open(FLAG, ">$VCLFLAG")) {
			print FLAG 0;
			close(FLAG);
		}
		else {
			unlink $VCLFLAG;
		}
		return $lines[0];
	} ## end if (open(FLAG, "$VCLFLAG"))
	else {
		notify($ERRORS{'OK'}, "flag: could not open $VCLFLAG $!");
		return 0;
	}
} ## end sub flag
sub make_new_child {
	my (%request_data) = @_;
	my $pid;
	my $sigset;

	# block signal for fork
	$sigset = POSIX::SigSet->new(SIGINT);
	sigprocmask(SIG_BLOCK, $sigset) or die "Can't block SIGINT for fork: $!\n";
	#die "fork: $!" unless defined ($pid = fork);
	FORK: {
		if ($pid = fork) {
			# Parent records the child's birth
			# and returns.
			sigprocmask(SIG_UNBLOCK, $sigset) or die "Can't unblock SIGINT for fork: $!\n";

			$children{$pid} = 1;
			$children++;
			notify($ERRORS{'OK'}, "vclclientd current number of forked kids: $children");
			return;
		} ## end if ($pid = fork)
		elsif (defined $pid) {
			# Child can *not* return from this subroutine.
			$SIG{INT} = 'DEFAULT';
			# make SIGINT kill us as it did before unblock signals
			sigprocmask(SIG_UNBLOCK, $sigset) or die "Can't unblock SIGINT for fork: $!\n";

			notify($ERRORS{'OK'}, "processdata: new request child process $request_data{state} $request_data{unityid},$request_data{remoteIP}   ");
			#do something that may take a long time or needs to be monitored
			#based on the case lets do something
			if ($request_data{state} =~ /new/) {
				notify($ERRORS{'OK'}, "processdata: new request  $request_data{unityid},$request_data{remoteIP}   ");
				if (new_state($request_data{unityid}, $request_data{remoteIP})) {
					notify($ERRORS{'OK'}, "processdata: connection for $request_data{unityid}\@$request_data{remoteIP} successfully opened");
				}
			}
			elsif ($request_data{"state"} =~ /timeout|deleted/) {
				if (timeout_state($request_data{unityid}, $request_data{remoteIP})) {
					notify($ERRORS{'OK'}, "vclclientd: connection for $request_data{unityid}\@$request_data{remoteIP} successfully terminated");
				}
			}

			exit;
		} ## end elsif (defined $pid)  [ if ($pid = fork)
		elsif ($! =~ /No more process/) {
			sleep 5;
			redo FORK;
		}
		else {
			# strange error
			die "Can't fork: $!\n";
		}
	} ## end FORK:
} ## end sub make_new_child
sub new_state {
	my ($user, $remoteIP) = @_;
	# assumuption user and IP are valid
	# add user to users.local, sshd_config_vcl
	# on acknowledgemment turn on sshd on port 22
	my @file;
	my $line;
	my ($userset, $remoteIPset) = 0;
	#test for sshd_config_vcl
	if (!(-r "$HOME/sshd_config_vcl")) {
		#hrmm. were did sshd_config_vcl go
		#let try to create another from the orignal
		if (createnewssh_config_vcl) {
			notify($ERRORS{'OK'}, "new_state: sshd_config_vcl missing created a new one");
		}
		else {
			notify($ERRORS{'OK'}, "new_state: sshd_config_vcl missing failed to create a new one");
			return 0;
		}

	} ## end if (!(-r "$HOME/sshd_config_vcl"))
	if (open(CONFIG, "$HOME/sshd_config_vcl")) {
		@file = <CONFIG>;
		close(CONFIG);
		foreach $line (@file) {
			if ($line =~ /AllowUsers/) {
				$line    = "AllowUsers $user\n";
				$userset = 1;
				notify($ERRORS{'OK'}, "new_state: adding AllowUsers $user to sshd_config_vcl");
			}
		}
		if (!$userset) {
			push @file, "AllowUsers $user\n";
			notify($ERRORS{'OK'}, "new_state: hrmm, had to add AllowUsers $user to sshd_config_vcl");
		}
		if (open(CONFIG, ">$HOME/sshd_config_vcl")) {
			print CONFIG @file;
			close(CONFIG);
		}
	} ## end if (open(CONFIG, "$HOME/sshd_config_vcl"))
	# append to users.local
	if (open(USERSLOCAL, "/etc/users.local")) {
		my @users = <USERSLOCAL>;
		close(USERSLOCAL);
		push @users, "\n$user\n";
		if (open(USERSLOCAL, ">/etc/users.local")) {
			print USERSLOCAL @users;
			notify($ERRORS{'OK'}, "new_state: adding $user to users.local");
			close(USERSLOCAL);
		}
	} ## end if (open(USERSLOCAL, "/etc/users.local"))
	else {
		notify($ERRORS{'WARNING'}, "new_state: could not open /etc/users.local $!");
		return 0;
	}
	#start sshd
	if (startsshd) {
		notify($ERRORS{'OK'}, "new_state: startsshd returned and successful");
		return 1;
	}
	return 0;
} ## end sub new_state
sub timeout_state {
	# time to close non-admin ssh sessions and clean up users.local,
	# sshd_config_vcl
	my ($user, $remoteIP) = @_;
	my $os = lc($^O);
	#notify($ERRORS{'OK'},"timeout_state: OSname is $os");
	my @file;
	my $l;
	my $sshd_admin_pid = 0;
	my ($pgrep, $pkill);

	# get admin pid
	if ($os eq "solaris") {
		$pgrep = "/bin/pgrep";
		$pkill = "/bin/pkill";

		if (open(SSH, "/local/openssh/etc/sshd.admin.pid")) {
			@file = <SSH>;
			close(SSH);
			$sshd_admin_pid = $file[0];
			notify($ERRORS{'OK'}, "timeout_state: sshd_admin_pid set $sshd_admin_pid");
		}
		else {
			notify($ERRORS{'OK'}, "timeout_state: could not open /local/openssh/etc/sshd.admin.pid $!");
		}
	} ## end if ($os eq "solaris")
	elsif ($os eq "linux") {
		$pgrep = "/usr/bin/pgrep";
		$pkill = "/usr/bin/pkill";
		if (open(SSH, "ps -ef \| grep /usr/sbin/sshd |")) {
			@file = <SSH>;
			close(SSH);
			$sshd_admin_pid = $file[0];
			foreach $l (@file) {
				chomp($l);
				next if ($l =~ /grep/);
				if ($l =~ /(\/usr\/sbin\/sshd$)/) {
					my $blah;
					($blah, $sshd_admin_pid, $blah) = split(/\s+/, $l, 3);
					notify($ERRORS{'OK'}, "timeout_state: sshd_admin_pid set $sshd_admin_pid");
				}
			}
		} ## end if (open(SSH, "ps -ef \| grep /usr/sbin/sshd |"...
		else {
			notify($ERRORS{'OK'}, "timeout_state: execute ps -ef $!");
		}
	} ## end elsif ($os eq "linux")  [ if ($os eq "solaris")
	else {
		notify($ERRORS{'OK'}, "timeout_state: $os not supported");
		# we'll just let this ride and get a restart
	}
	# clean up users.local
	# collect members of users.admin,users.base, and users.cluster
	my @users_admin;
	my @users_base;
	my @users_cluster;
	my $u;
	#this one should exist
	if (open(USERSLOCAL, "cat /etc/users.local.admin > /etc/users.local |")) {
		close(USERSLOCAL);
		notify($ERRORS{'OK'}, "timeout_state: dumped contents of /etc/users.local.admin /etc/users.local");
	}
	if (-r "/etc/users.local.base") {
		if (open(USERSLOCAL, "cat /etc/users.local.base >> /etc/users.local |")) {
			close(USERSLOCAL);
			notify($ERRORS{'OK'}, "timeout_state: dumped contents of /etc/users.local.base /etc/users.local");
		}
	}
	if (-r "/etc/users.local.cluster") {
		if (open(USERSLOCAL, "cat /etc/users.local.cluster >> /etc/users.local |")) {
			close(USERSLOCAL);
			notify($ERRORS{'OK'}, "timeout_state: dumped contents of /etc/users.local.cluster /etc/users.local");
		}
	}


	if (open(USERSLOCAL, "/etc/users.local")) {
		@users_admin = <USERSLOCAL>;
		close(USERSLOCAL);
	}
	# check users.local add vclstaff if is does not exist
	my $vclstaff = 0;
	my $i;
	for $i (@users_admin) {
		$vclstaff = 1 if ($i =~ "vclstaff");

	}

	if (!$vclstaff) {
		push @users_admin, "\nvclstaff\n";
		if (open(USERSLOCAL, "> /etc/users.local")) {
			print USERSLOCAL @users_admin;
			close(USERSLOCAL);
		}
	}
	# clean up our sshd_config_vcl
	my @SSH;
	my $s;
	if (open(SSHDCONFIG, "$HOME/sshd_config_vcl")) {
		@SSH = <SSHDCONFIG>;
		close(SSHDCONFIG);
		foreach $s (@SSH) {
			if ($s =~ s/AllowUsers $user/AllowUsers/g) {
				notify($ERRORS{'OK'}, "timeout_state: $user\@$remoteIP removed from sshd_config_vcl");
			}
		}
		# write back out to sshd_config_vcl
		if (open(SSHDCONFIG, ">$HOME/sshd_config_vcl")) {
			print SSHDCONFIG @SSH;
			close(SSHDCONFIG);
		}
		else {
			notify($ERRORS{'OK'}, "timeout_state: could not open $HOME/sshd_config_vcl for writing");
		}
	} ## end if (open(SSHDCONFIG, "$HOME/sshd_config_vcl"...

	#kill off any user processes
	if (open(PKILL, "$pkill -9 -U $user 2>&1 |")) {
		my @pkill = <PKILL>;
		close(PKILL);
		notify($ERRORS{'OK'}, "timeout_state: stopped user processes");
		#check for user
		notify($ERRORS{'OK'}, "timeout_state: confirming user processes are stopped");
		if (open(PGREP, "ps -ef \| grep $user|")) {
			my @pgrep = <PGREP>;
			close(PGREP);
			foreach my $pid (@pgrep) {
				next if ($pid =~ /grep/);
				my ($userblah, $userpid) = split(/\s+/, $pid, 3);
				if ($userpid) {
					if (open(KILL, "kill -9 $userpid |")) {
						notify($ERRORS{'OK'}, "timeout_state: killed user process $userpid");
						close(KILL);
					}
				}
			} ## end foreach my $pid (@pgrep)
		} ## end if (open(PGREP, "ps -ef \| grep $user|"))
	} ## end if (open(PKILL, "$pkill -9 -U $user 2>&1 |"...
	notify($ERRORS{'OK'}, "timeout_state: checking for all sshd processes");
	# use pgrep to get all sshd pids
	if (open(PGREP, "ps -ef \|grep sshd 2>&1|")) {
		my @pfile = <PGREP>;
		close(PGREP);
		foreach $l (@pfile) {
			next if ($l =~ /grep/);
			next if ($l =~ /ps -ef/);
			notify($ERRORS{'OK'}, "timeout_state: pgrep sshd = $l");

			my ($b, $sshpid);
			($b, $b, $sshpid, $b) = split(/\s+/, $l, 4) if ($os eq "solaris");
			($b, $sshpid) = split(/\s+/, $l, 3) if ($os eq "linux");
			next if ($sshpid == $sshd_admin_pid);
			if (open(KILL, "kill -9 $sshpid |")) {
				notify($ERRORS{'OK'}, "timeout_state: killed sshd process $sshpid");
				close(KILL);
			}
		} ## end foreach $l (@pfile)
		notify($ERRORS{'OK'}, "timeout_state: checking if I accidentially killed all sshd processes");
		# did we kill all sshd sessions?
		if (open(PGREP, "ps -ef \|grep sshd \|grep -v grep |")) {
			notify($ERRORS{'OK'}, "timeout_state: executed ps -ef \|grep sshd \|grep -v grep");
			@file = <PGREP>;
			notify($ERRORS{'OK'}, "timeout_state: @file");
			close(PGREP);
			if (!($file[0])) {
				notify($ERRORS{'OK'}, "timeout_state: killed all sshd processes, will try to restart");
				if (open(SSHD, "/etc/inet.d/sshd start |")) {
					@file = <SSHD>;
					close(SSHD);
					notify($ERRORS{'OK'}, "timeout_state: sshd admin restarted @file");
				}
			}
		} ## end if (open(PGREP, "ps -ef \|grep sshd \|grep -v grep |"...
	} ## end if (open(PGREP, "ps -ef \|grep sshd 2>&1|"...
	else {
		notify($ERRORS{'WARNING'}, "timeout_state: could not execute /usr/bin/pgrep sshd");

	}
	notify($ERRORS{'OK'}, "timeout_state: looking for sshd_config_vcl");
	#look for sshd_config_vcl in case we killed the sshd_admin pid
	if (open(SSH, "ps -ef \| grep /usr/sbin/sshd |")) {
		my @sshfile = <SSH>;
		close(SSH);
		foreach $l (@sshfile) {
			if ($l =~ /(\/home\/vclstaff\/sshd_config_vcl\/)/) {
				# for some reason sshd with the vcl config file did not get stopped
				#initiate a restart/reload
				notify($ERRORS{'OK'}, "timeout_state: sshd_config_vcl not stopped for some reason, prhaps the wrong sshd pid");
				my ($b, $sshpid) = split(/\s+/, $l, 3);
				if ($sshpid == $sshd_admin_pid) {
					notify($ERRORS{'OK'}, "timeout_state: killed the wrong sshd pid");
					#kill this pid
					if (open(KILL, "kill -9 $sshpid |")) {
						notify($ERRORS{'OK'}, "timeout_state: killed sshd process $l");
						close(KILL);
					}

					#stop and start sshd service.
					if (open(SSHSTOP, "/etc/inet.d/sshd stop |")) {
						@file = <SSHSTOP>;
						close(SSHSTOP);
						notify($ERRORS{'OK'}, "timeout_state: sshd admin stopped @file");
						if (open(SSHSTART, "/etc/inet.d/sshd start |")) {
							@file = <SSHSTART>;
							close(SSHSTART);
							notify($ERRORS{'OK'}, "timeout_state: sshd admin started @file");
						}
					} ## end if (open(SSHSTOP, "/etc/inet.d/sshd stop |"...
				} ## end if ($sshpid == $sshd_admin_pid)
			} ## end if ($l =~ /(\/home\/vclstaff\/sshd_config_vcl\/)/)
		} ## end foreach $l (@sshfile)
	} ## end if (open(SSH, "ps -ef \| grep /usr/sbin/sshd |"...

	if (sshdstatus) {
		notify($ERRORS{'OK'}, "timeout_state: sshd core process is running");
	}
	else {
		notify($ERRORS{'CRITICAL'}, "timeout_state: sshd is not running or could not be restarted");

	}
	return 1;
} ## end sub timeout_state
sub reboot {
	#simply reboot the client when called
	my $os = lc($^O);
	my $reboot;
	if ($os eq "solaris") {
		$reboot = "/usr/sbin/shutdown -y -g 0 -i 6";
	}
	else {
		$reboot = "/sbin/shutdown -r now";
	}
	notify($ERRORS{'OK'}, "reboot: starting reboot sequence");
	if (open(REBOOT, "$reboot 2>&1 |")) {
		my @reboot = <REBOOT>;
		close(REBOOT);
		notify($ERRORS{'OK'}, "reboot: @reboot");
		return 1;
	}

} ## end sub reboot
sub fetch {
	#collect host ssh keys and save for MN to pick up
	notify($ERRORS{'OK'}, "fetch: copying ssh keys to $HOME");
	my $os = lc($^O);
	my $sshdir;
	if ($os eq "solaris") {
		$sshdir = "/local/openssh/etc";
	}
	else {
		$sshdir = "/etc/ssh/";
	}

	if (open(CP, "/bin/cp $sshdir/ssh_host\* $HOME 2>&1 |")) {
		my @cp = <CP>;
		close(CP);
		if (@cp) {
			notify($ERRORS{'OK'}, "fetch: copy problems - @cp");
		}

		if (open(CHOWN, "/bin/chown vclstaff $HOME/ssh_host\* 2>&1 |")) {
			my @chown = <CHOWN>;
			close(CHOWN);
			if (@chown) {
				notify($ERRORS{'OK'}, "fetch: chown problems - @cp");
			}
		}
	} ## end if (open(CP, "/bin/cp $sshdir/ssh_host\* $HOME 2>&1 |"...
	notify($ERRORS{'OK'}, "fetch: fetch complete");
	return 1;
} ## end sub fetch
sub store {
	# take host ssh keys stored in my home directory and place them into the /etc/sshd directory
	#create an orig directory in $sshdir
	#copy original keys to orig dir
	#cp given keys to proper location
	#set correct ownership and premissions on keys
	#unlink/remove locally stored keys from vclstaff dir
	my $os = lc($^O);
	my $sshdir;
	if ($os eq "solaris") {
		$sshdir = "/local/openssh/etc";
	}
	else {
		$sshdir = "/etc/ssh";
	}
	notify($ERRORS{'OK'}, "store: copying ssh keys to $sshdir");

	my %filelist;
	$filelist{"dsa"}    = "ssh_host_dsa_key";
	$filelist{"dsapub"} = "ssh_host_dsa_key.pub";
	$filelist{"rsa"}    = "ssh_host_rsa_key";
	$filelist{"rsapub"} = "ssh_host_rsa_key.pub";
	$filelist{"key"}    = "ssh_host_key";
	$filelist{"keypub"} = "ssh_host_key.pub";

	if (!(-d "$sshdir/origkeys")) {
		if (mkdir("$sshdir/origkeys", 755)) {
			notify($ERRORS{'OK'}, "store: mkdir successfully created $sshdir/origkeys");
		}
		else {
			notify($ERRORS{'OK'}, "store: mkdir $sshdir/origkeys $! ");
		}
	}
	else {
		#hrmm $sshdir/origkeys already exists
	}
	#copy system generated keys to orig dir
	#copy stored keys to ssh dir
	#set perms,ownership,unlink local copy
	foreach my $f (sort keys %filelist) {
		if (!(-f "$HOME/$filelist{$f}")) {
			notify($ERRORS{'OK'}, "store: does not exist $HOME/$filelist{$f}");
			next;
		}
		if (open(CP, "/bin/cp $sshdir/$filelist{$f} $sshdir/origkeys 2>&1 |")) {
			my @cp = <CP>;
			close(CP);
			if (@cp) {
				notify($ERRORS{'OK'}, "store: copy orig keys problem on $filelist{$f} - @cp");
			}
		}
		#copy given keys to ssh dir
		if (open(CP, "/bin/cp $HOME/$filelist{$f} $sshdir/$filelist{$f} 2>&1 |")) {
			my @cp = <CP>;
			close(CP);
			if (@cp) {
				notify($ERRORS{'OK'}, "store: copy given keys problem on $filelist{$f} - @cp");
			}
			else {
				notify($ERRORS{'OK'}, "store: copied $filelist{$f} to $sshdir");
				if (open(CHOWN, "/bin/chown root:root $sshdir/$filelist{$f} 2>&1 |")) {
					close(CHOWN);
				}
				my $p;
				if ($f =~ /pub/) {
					$p = 644;
				}
				else {
					$p = 600;
				}
				if (open(CHMOD, "/bin/chmod $p $sshdir/$filelist{$f} 2>&1|")) {
					my @chmod = <CHMOD>;
					close(CHMOD);
					if (@chmod) {
						notify($ERRORS{'OK'}, "store: chmod problem on $filelist{$f} - @chmod");
					}
				}    #chmod
			}    #else no cp problems
		}    #CP
		     #unlink
		if (unlink "$HOME/$filelist{$f}") {
			notify($ERRORS{'OK'}, "store: deleted $HOME/$filelist{$f}");
		}
		else {
			notify($ERRORS{'OK'}, "store: unable to delete $HOME/$filelist{$f}");
		}
	}    #foreach
	     #restart sshd
	if (restartsshd) {
		notify($ERRORS{'OK'}, "store: sshd restarted");
		return 1;
	}
	else {
		notify($ERRORS{'OK'}, "store: sshd restart failed");
		return 0;
	}
	return 1;
} ## end sub store

sub restartsshd {
	my $os = lc($^O);
	notify($ERRORS{'OK'}, "restartsshd: attempting to restart sshd on $os");
	if ($os eq "solaris") {
		if (open(STOP, "/bin/pkill -f sshd_admin.cfg  2>&1 |")) {
			my @stop = <STOP>;
			close(STOP);
			foreach my $r (@stop) {
				if ($r =~ /failed/i) {
					notify($ERRORS{'WARNING'}, "restartsshd: sshd stop failed @stop");
				}
			}
			if (open(START, "/etc/init.d/sshd start 2>&1 |")) {
				my @start = <START>;
				close(START);
				foreach my $r (@start) {
					#notify($ERRORS{'OK'},"restartsshd: output $r");
					if ($r =~ /failed/i) {
						notify($ERRORS{'WARNING'}, "restartsshd: sshd start failed @start");
					}
					return 1 if ($r =~ /ok/i);
				}
			}    #if start
		}    # pkill
	} ## end if ($os eq "solaris")
	elsif ($os =~ /linux/) {
		if (open(RESTART, "/etc/init.d/sshd restart 2>&1 |")) {
			my @restart = <RESTART>;
			close(RESTART);
			foreach my $r (@restart) {
				if ($r =~ /failed/i) {
					notify($ERRORS{'WARNING'}, "restartsshd: sshd restart failed $r @restart");
				}
				if ($r =~ /Starting/) {
					return 1 if ($r =~ /ok/i);
				}
			}
		} ## end if (open(RESTART, "/etc/init.d/sshd restart 2>&1 |"...
	} ## end elsif ($os =~ /linux/)  [ if ($os eq "solaris")
	return 1;
} ## end sub restartsshd
sub startsshd {
	my @lines;
	#figure out OS solaris or linux
	my $os = lc($^O);
	my @output;
	my $l;
	if ($os eq "solaris") {
		if (open(SSHD, "/local/openssh/sbin/sshd -f $HOME/sshd_config_vcl 2>&1 |")) {
			notify($ERRORS{'OK'}, "startsshd: starting sshd");
			@output = <SSHD>;
			close(SSHD);
			foreach $l (@output) {
				notify($ERRORS{'OK'}, "startsshd output: $l");
			}
			return 1;
		}
		else {
			notify($ERRORS{'OK'}, "startsshd: could not execute /local/openssh/sbin/sshd -f $HOME/sshd_config_vcl $!");
			return 0;
		}
	} ## end if ($os eq "solaris")
	elsif ($os eq "linux") {
		if (open(SSHD, "/usr/sbin/sshd -f $HOME/sshd_config_vcl |")) {
			notify($ERRORS{'OK'}, "startsshd: starting sshd");
			@output = <SSHD>;
			close(SSHD);
			foreach $l (@output) {
				notify($ERRORS{'OK'}, "startsshd output: $l");
			}
			return 1;
		}
		else {
			notify($ERRORS{'OK'}, "startsshd: could not execute /usr/sbin/sshd -f $HOME/sshd_config_vcl $!");
			return 0;
		}
	} ## end elsif ($os eq "linux")  [ if ($os eq "solaris")
} ## end sub startsshd
sub notify {
	my ($error, $string) = @_;
	my $currenttime = makedatestring;
	if (open(LOGIT, ">>$LOG")) {
		if (!$error) {
			print LOGIT "$currenttime - $$: $string\n";
			close(LOGIT);
			return;
		}
		if ($error == 2) {    #CRITICAL something bad happened, exiting
			print LOGIT "\n$string\n";
			print LOGIT "exiting\n";
			close(LOGIT);
			exit;
		}
		elsif ($error == 1) {
			# WARNING should prompt admin to
			# continue or exit
			# need to disable for cron
			print LOGIT "\n---- WARNING ---- \n$string\n";
			close(LOGIT);
		}
	} ## end if (open(LOGIT, ">>$LOG"))
} ## end sub notify
sub makedatestring {
	my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime();
	$year += 1900;
	$mon++;
	my $datestring = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $year, $mon, $mday, $hour, $min, $sec);
	return $datestring;
}
sub sshdstatus {
	my $os = lc($^O);
	if ($os eq "solaris") {
		my $sshd_admin_pid;
		if (open(SSH, "/local/openssh/etc/sshd.admin.pid")) {
			my @file = <SSH>;
			close(SSH);
			chomp($file[0]);
			$sshd_admin_pid = $file[0];
		}
		else {
			notify($ERRORS{'OK'}, "sshdstatus: could not open /local/openssh/etc/sshd.admin.pid $!");
			return 0;
		}

		if (open(STAT, "/bin/pgrep -f sshd  2>&1 |")) {
			my @stat = <STAT>;
			close(STAT);
			foreach my $r (@stat) {
				if ($r =~ /$sshd_admin_pid/) {
					#notify($ERRORS{'OK'},"sshdstatus: sshd is running");
					return 1;
				}
			}
			notify($ERRORS{'OK'}, "sshdstatus: sshd is NOT running trying to restart");
			if (open(START, "/etc/init.d/sshd start 2>&1 |")) {
				my @start = <START>;
				close(START);
				foreach my $r (@start) {
					if ($r =~ /failed/i) {
						notify($ERRORS{'WARNING'}, "store: sshd start failed @start");
						return 0;
					}
				}
			}    #if start
		}    #STAT
		return 1;
	}    #os=solaris
	elsif ($os =~ /linux/) {
		my $sshd_admin_pid;
		if (open(SSH, "/var/run/sshd.pid")) {
			my @file = <SSH>;
			close(SSH);
			chomp($file[0]);
			$sshd_admin_pid = $file[0];
		}
		else {
			notify($ERRORS{'WARNING'}, "sshdstatus: could not open /var/run/sshd.pid $!");
			return 0;
		}
		my $running = 0;
		if (open(SSH, "pgrep -f sshd 2>&1|")) {
			my @lines = <SSH>;
			close(SSH);
			foreach my $l (@lines) {
				if ($l =~ /$sshd_admin_pid/) {
					#ok it's running
					$running = 1;    #not that this matters
					return 1;
				}
			}
		}    #if pgrep
		if (!$running) {
			#start sshd
			notify($ERRORS{'WARNING'}, "sshdstatus: not running trying to restart");
			if (open(STAT, "/etc/init.d/sshd start 2>&1 |")) {
				my @stat = <STAT>;
				close(STAT);
				foreach my $s (@stat) {
					if ($s =~ /ok/i) {
						notify($ERRORS{'OK'}, "sshdstatus: restarted core sshd process, @stat");
						return 1;
					}
				}
				#in case I don't return in above check
				notify($ERRORS{'OK'}, "sshdstatus: restart attempt may of had issues, @stat");
				return 0;
			}    #if sshd start
		}    #if ! running
	}    #elsif linux
} ## end sub sshdstatus
sub createnewssh_config_vcl {
	#check for .orig from /etc/ssh dir
	#if orig then just need to add port 22 and AllowUsers directive
	my ($port22, $AU, $port24) = 0;
	my @file;
	if (-e "/etc/sshd/sshd_config.orig") {
		#good slurp it in
		if (open(CONFIG, "$HOME/sshd_config_vcl")) {
			@file = <CONFIG>;
			close(CONFIG);
			foreach my $line (@file) {
				#check for port 22 and AllowUsers
				if ($line =~ /^Port 22/) {
					$port22 = 1;
				}
				if ($line =~ /^AllowUsers/) {
					$AU = 1;
				}
				if ($line =~ s/^Port 24/Port 22/g) {
					$port22 = 1;
				}
			} ## end foreach my $line (@file)
		} ## end if (open(CONFIG, "$HOME/sshd_config_vcl"))
	} ## end if (-e "/etc/sshd/sshd_config.orig")
	else {
		#ok /etc/sshd/sshd_config.orig does not exist
		#try to create from the /etc/sshd/sshd_config

	}
	#write out to $HOME/sshd_config_vcl
	if (open(SC, ">$HOME/sshd_config_vcl")) {
		print SC @file;
		close(SC);
	}
	else {
		notify($ERRORS{'CRITICAL'}, "createnewssh_config_vcl: sshd_config_vcl was reported to not exists, in repairing I failed to create a new $HOME/sshd_config_vcl $!");
		return 0;
	}
} ## end sub createnewssh_config_vcl

#/////////////////////////////////////////////////////////////////////////////

1;
__END__

=head1 SEE ALSO

L<http://cwiki.apache.org/VCL/>

=cut
