#!/usr/bin/perl
#
#	clx - clx info/start/shutdown
#
# Last change by DL6RAI: Sat Dec  4 11:07:48 GMT 1999

$| = 1;                         # Kein gepufferter Output
$_ = $ARGV[0];			# parameter bernehmen

($clx_user_uid,$clx_user_gid,$home) = (getpwnam('clx_us'))[2,3,7];
$pg_lockfile = "$PGDATA/base/clx_db/pg_vlock";
$pg_tempfile = "$PGDATA/base/clx_db/temp_*";

# default settings
$interface = "AX25";
$postgres = 1; 
$job = print_info;
$status = "$home/log/clx_stat";

@clx_p = ("clx_ctl", "int_com", "snd_ctl", "usr_ctl", "iu_com",
          "icl_com", "usr_req", "udt_mng", "mb_ctl",  "usc_mng", 
          "usr_dlg", "bbs_if",  "rm_disp", "rcv_ctl", "con_ctl");

%p_info = ("clx_ctl", "811111139:Shared Memory manager:",
           "int_com", "811111104:Internal Communications Manager:",
           "snd_ctl", "811111102:Transmit Spooler:",
           "usr_ctl", "811111123:User Administration:",
           "iu_com" , "811111125:Inter-User communication:",
           "icl_com", "811111122:Inter-Node communication:",
           "usr_req", "811111124:User Database Interface:",
           "udt_mng", "811111128:User Data Table Manager:",
           "mb_ctl" , "811111126:Mailbox Controller:",
           "usc_mng", "811111127:User Commands Manager:",
           "usr_dlg", "811111129:User Dialog manager:",
           "bbs_if",  "811111130:BBS interface:",
           "rm_disp", "811111121:Received messages Dispatcher:",
           "rcv_ctl", "811111103:Received messages Spooler:",
           "con_ctl", "811111101:AX.25 interface:");

while ($_ = $ARGV[0],/^-/) {
	last if /^--$/;

	if (/^-u/) { $job = "startup_clx" }
	if (/^-r/) { $job = "restart_clx" }
	if (/^-s/) { $job = "shutdown_clx" }
	if (/^-c/) { $job = "check_clx" }
	if (/^-t/) { $job = "test_clx" }
	if (/^-x/) { $job = "clear_clx" }
	if (/^-nopostgres/) { $postgres = 0 }

	shift;
}

&chk_postgres;

chdir("$home/bin");
exit &$job();




####################### Funktionen #######################

sub chk_postgres {

	$pm_running = 0;

	open(PS, "ps auwx |") || die "Error executing ps.\n";

	while (<PS>) {

		if (/postmaster/) {
			$pm_running = 1; 
			($pg_user) = split;
			if ( $pg_user ne "postgres" ) {
        			die "Postmaster running under $pg_user. It must be running as user postgres.\n";
			}
		}
	}

	close(PS);
	return $pm_running;
}

sub restart_clx {
    &shutdown_clx;
    &startup_clx;
}

sub startup_clx {

    if ( -e $status ) {
	if ( &test_ipc  != 0 ) {
		print "clx: $status exists, is CLX already active?\n";
		&sys_log("clx: $status exists, is CLX already active?");
		exit 1;
	} else {
		print "clx: ignoring stale lock file $status\n";
	}
    }
    # touch the status file
    open(STAT,">$status");
    print STAT;
    close(STAT);

    &read_cfg();

    if ( $postgres && ! &chk_postgres ) {
        die "Postmaster must be running before clx can be started.\n";
#       print "Trying to start it...";
#       system('/bin/su','-','postgres','-c','postmaster -i &');
#       if ( &chk_postgres ) {
#           print "succeeded!\n";
#       } else {
#           die "could not start postmaster.\n";
#       }
    }

    print "Decoding callsign";
    $cl_call=`clx_ctl -e \"$code\"`;
    chop($cl_call);
    print ".\n";
    die "Cannot decode callsign - aborting.\n" if ( $cl_call =~ /\?/ );

    &sys_log ("*** Starting CLX, callsign $cl_call ***");

    &print_cfg();
    &fs_chk() if ( $postgres == 1);			# check file-system
#   &shm_clr();			# falsche shm lschen

#   unlink $pg_lockfile if ( -e $pg_lockfile ); # Postgres-Leiche

    print "Clx coming up...\n\n";

    foreach $proc (@clx_p) {
        ($id,$desc) = split(/:/,$p_info{$proc});
	&shell("$proc &");
	printf("%-8s %-32s",$proc,$desc);
        &rpc_wait($id,0);
	print " up\n";
    }

    print "\nClx is up.\n";
    &sys_log ("clx: *** CLX is up and running ***");
}




sub shutdown_clx {

    &read_cfg();
    print "Clx going down...\n\n";
    &sys_log ("clx: *** Starting shutdown ***");

    foreach $proc (reverse(@clx_p)) {
	&shell("$proc -s");
        ($id,$desc) = split(/:/,$p_info{$proc});
	printf("%-8s %-32s",$proc,$desc);
        &rpc_wait($id,1);
	print " down\n";
    }
    print "\n";

    sleep 2;
#   &shm_clr();			# falsche shm lschen

    # remove the status file
    unlink $status if ( -e $status );

    print "\nClx is down.\n";
    &sys_log ("clx: *** CLX is down ***");
}





sub clear_clx {
   if ($< != 0) {
	print "You must be root to clear rpc ports.\n";
	system "/bin/su - root -c \"~clx_us/bin/clx -x\"";
   } else {

       &kill_clx();
       &rpc_clr();
       &shm_clr();

       # remove the status file
       unlink $status if ( -e $status );
   }
}


sub test_clx {
    &print_info ();
    &sys_log ("clx: called with -t option");
    foreach $p (@clx_p) {
        ($id,$desc) = split(/:/,$p_info{$p});
	print "$p -- $id -- $desc\n";
    }
    return(13);
}


sub check_clx {
    print "Checking clx processes...\n\n";
    local $nok = 0;
    foreach $p (@clx_p) {
        ($id,$desc) = split(/:/,$p_info{$p});
        $rc = &rpc_info($id);
        if ( $rc == 0 ) {
		$ok="OK";
	} else {
		$ok="NOT OK";
		$nok = 1;
	}
        printf ("%-32s %10s:    rc = %s\n",$desc,$p,$ok);
    }
    return $nok;
}



##################### Unterprogramme ##################### 

#
# Hilfe ausgeben
#

sub print_info {
print <<"NNNN"

This script is used to startup and shutdown clx.

The following options are available:

-u               start clx
-s               shutdown clx
-r               shutdown and restart clx
-x               clear shared memories (requires root access)
-c               check if clx programs are running OK
-nopostgres      Start clx without database

NNNN
}

#
# Vacuum-Funktion der Datenbank aufrufen
#

sub db_vac {
    print "Starting vacuum... ";
    &shell("psql @_[0] -c vacuum\;");
    die "Error executing vacuum.\n" if $? != 0;
}


#
# Shared Memory prfen und ggf. lschen
#

sub shm_clr {

    print "Clearing shared memories.\n";

    open(SHML, "ipcs -m |") 
        || die "Error executing ipcs.\n";
    
    while (<SHML>) { 		# fr alle shm
	($shmid, $shmus) = (split)[1,2];
	if ($shmus eq 'clx_us') {  # eigene shm
	    &shell("ipcrm shm $shmid");
            print "+ ipcrm shm $shmid\n";
        }
    }
}

#
# Haengende RPC-Resourcen loeschen
#

sub rpc_clr {

    print "Clearing RPC ports.\n";

    open(R,"rpcinfo -p |");
    while(<R>) {
	$id = (split)[0];
        push(@found,$id);
    }
    close(R);

    foreach $p (@clx_p) {
        ($id,$desc) = split(/:/,$p_info{$p});
        if ( grep(/$id/,@found) ) {
		print("+ rpcinfo -d $id 1\n");
		&shell("rpcinfo -d $id 1");
	}
    }
}

#
#
#
sub kill_clx {

	print "Killing any hanging CLX and Postgres processes.\n";

	for $sig (15,9) {
		open(PS, "ps auwx |") || die "Error executing ps.\n";
		while (<PS>) {
#			if (/^clx_us/ || /^postgres/) {
			if (/^clx_us/) {
				chop;
				($user,$pid,$name) = (split(' ',$_,11))[0,1,10];
				$name =~ tr/()//d;
				#$name =~ s/  *$//;
				next if ( $user ne "clx_us" );
				next if ( $name =~ /-.*sh/ );
				next if ( $name =~ /clx -x/ );
				next if ( $name =~ /su - clx_us/ );
				print "+ kill -$sig $pid ($name)\n";
				kill($sig,$pid);

#
# Das funktionierte nicht gut, es gibt jetzt auch noch hngende db_sdx
# Prozesse, die so nicht erwischt werden - 15.8.98.
#
#	if (grep(/$name/,@clx_p) || $name =~ /clx -[urs]/) {
#		kill ...
#	}
			}
		}
		close(PS);
		sleep 3 if ($sig == 3);
	}
}

#
# Dateisystem prfen und ggf. verndern
#

sub fs_chk {

    print "Checking directories and file permissions\n";

    %p = ('mail',1,'iclb',1,'info',3,'batch',0,'bulletin',3);
    foreach $i (keys %p) {
        &shell("$home/tools/mk_th -q $i $p{$i}");
    }
    &shell("$home/tools/mbx_chk -q");

    @p = ('log','exec/connect','exec/command','exec/privileg','config','callb',
          'box','box/batch','box/batch/start','box/info','box/info/etc');
    foreach $i (@p) {
        opendir(DIR, "$home/$i") || mkdir("$home/$i", 0755) 
		|| die "Could not create $home/$i.\n"; 
        closedir(DIR);
    }

    &shell("$home/tools/populate");
}

#
# Konfigurationsdatei lesen
#
sub read_cfg {

$cfg="$home/config/clx_par";

die "No configuration file ($cfg) found - aborting.\n" if ( ! -e $cfg );

print "Reading $cfg.\n";

open(CFG,"grep \"^[^#].*:\" <$cfg |") || die "Error executing grep\n";
while(<CFG>) {
	chop;
	@word = split(/[\t\s]+/,$_,3);

	if (/^call:/) { $code=$word[1] }
	if (/^db_name:/) { $db_name=$word[1] }
	if (/^wampes:/) { $interface="WAMPES" }
}

#print "End read_cfg ($cfg)\n";
}

#
# Systemparameter ausgeben
#
sub print_cfg {
    print"\n*** CLX System Parameters ***\n\n";
    print "callsign:   $cl_call\n";
    print "database:   $db_name\n";
    print "interface:  $interface\n";
    print "\n"
}

#
# Warten und dabei herunterzhlen
#
sub s_sleep {
    ($time) = @_;
    $step = 5;
    print "Waiting $time seconds: ";
    for ($i = $time; $i > 0; $i = $i - $step) {
	print "$i..";
        sleep $step;
    }
    print "0.\n";
}


#
# Eintrag in System-Log
#
sub sys_log {
    local($msg) = @_;
    &shell("logger -p local5.info \"$msg\"");
    &shell("logger -p local5.debug \"$msg\"");
}


#
# RPC-Info
#
sub rpc_info {
	local($rpc_id) = @_;
	$rc = (&shell("rpcinfo -u localhost $rpc_id >/dev/null 2>&1")) / 256;
	return $rc;
}

#
# RPC-Info-Abfrage (solange warten, bis sich das Programm meldet)
#
sub rpc_wait {
	local($rpc_id,$xrc) = @_;
	$rc = &rpc_info($rpc_id);
	while ( ! $rc == $xrc ) {
		$rc = &rpc_info($rpc_id);
                print ".";
		sleep 1;
	}
	return $rc;
}

#
# Externe Programme starten
#
sub shell {
	system "@_";
        $rc = $?;
	# print "\$rc = $rc\n";
	die "\nError executing @_\nIs your PATH variable set correctly?\n" 
		if ( ( $rc % 256 ) != 0 );
	return $rc;
}

#
# Testen, ob Shared Memories eingerichtet sind
# wird verwendet, um zu prfen, ob das Status-File
# stale ist oder sich wirklich noch Reste von CLX
# im Speicher befinden. Z.B. nach Reboot wird das 
# Status-File nicht sauber gelscht.
#
sub test_ipc {
	open(SHML,"ipcs -m |") || die "Could not start ipcs.\n";

	$result=0;
	while (<SHML>) {
		if (/clx_us/) { $result++; }
	}
	close(SHML);
	return $result
}
