#!/usr/bin/perl -w
#
# This script is Copyright (C) 2002 by Mike Gleason, NcFTP Software.
# All Rights Reserved.
#
# This script tries to use the "lsof" utility if the package included it.
# Thanks to Vic Abell <abe@purdue.edu> of the Purdue University
# Computing Center for providing "lsof".
#
use strict;
use Socket;
use File::Copy;
use POSIX qw(strftime);

# Porting checklist:
#   df behavior
#   du -s -k
#   ps behavior
#   echo -n / echo \c
#   rc scripts
#   lsof
#   /var/log preference
#   adduser program
#   uninstall_ncftpd.pl
#

my ($verbose) 				= 1;
my ($force_install)			= 0;
my ($prefix) 				= "";
my ($eprefix) 				= "";
my ($bindir) 				= "";
my ($sbindir) 				= "";
my ($datadir)				= "";
my ($sysconfdir)			= "";
my ($runtimedatadir)			= "/var/run";
my ($logdir)				= "/var/log";
my ($libdir)				= "";
my ($includedir)			= "";
my ($mandir)				= "";
my ($ftpuser_preferred)			= "ftp";
my ($ftpgroup_preferred)		= "ftp";
my ($ftpuser)				= $ftpuser_preferred;
my ($ftpgroup)				= $ftpgroup_preferred;
my ($ftpuid)				= -1;
my ($ftpgid)				= -1;
my ($webuser)				= "";
my ($webgroup)				= "";
my ($webuid)				= -1;
my ($webgid)				= -1;
my ($host)				= "";
my ($OS)				= "";
my ($SYS)				= "";
my ($linux_libc)			= "";
my ($ftphome)				= "";
my ($ncftpd_running_at_time_of_install)	= 0;
my ($xinetd_running_at_time_of_install)	= 0;
my ($inetd_running_at_time_of_install)	= 0;
my ($lsof)				= "";
my ($perl)				= "";
my ($ftpport)				= 21;
my ($ftpservicename)			= "ftp";
my ($install_version)			= "";
my ($existing_version)			= "";
my ($existing_ncftpd)			= "";
my ($existing_general_cf)		= "";
my ($existing_domain_cf)		= "";
my ($new_general_cf)			= "";
my ($new_domain_cf)			= "";
my ($new_ncftpd)			= "";
my ($confdir)				= "";
my ($rptbindir)				= "";
my ($install_src_root)			= "";
my ($extra_paths)			= "";
my ($create_new_configs)		= 0;
my ($inittab_preferred)			= 0;
my ($run_ncftpd_verbose) 		= 0;
my ($startup_error_log)			= "";
my ($rc_startup_script)			= "";
my ($inittab_startup_script)		= "";
my ($linux_distro)			= "";
my ($install_log)			= "";
my ($pager) 				= "";
my ($pager_pid)				= 0;
my (@uninstall_items)			= ();
my ($uninstaller)			= "";
my ($binary_compat_err)			= "";



sub Usage
{
	print <<EOF;
Usage: $0 [options]
Options: [defaults in brackets after descriptions]
Invocation:
  --help                  Print this message
  --quiet, --silent       Do not print `checking...' messages
  --debug                 Print debugging messages
Directory and file names:
  --prefix=PREFIX         Install architecture-independent files in PREFIX
                          [/usr/local]
  --exec-prefix=EPREFIX   Install architecture-dependent files in EPREFIX
                          [same as prefix]
  --bindir=DIR            User executables in DIR [EPREFIX/bin]
  --sbindir=DIR           System admin executables in DIR [EPREFIX/sbin]
  --sysconfdir=DIR        Read-only single-machine data in DIR [PREFIX/etc]
  --runtimedatadir=DIR    Dynamic runtime state information DIR [$runtimedatadir]
  --logdir=DIR            Create log files in DIR [$logdir]
  --lsof=FILE             Use FILE as path to list-open-files program
Features and packages:
  --port=NUMBER           Install on port NUMBER [21]
  --create-new-configs    Do not try to reuse existing configs, if present
  --inittab               Run NcFTPd from /etc/inittab rather than an RC script
EOF
#
# Not using these yet:
#
#  --libexecdir=DIR        Program executables in DIR [EPREFIX/libexec]
#  --datadir=DIR           Read-only architecture-independent data in DIR
#                          [PREFIX/share]
#  --libdir=DIR            Object code libraries in DIR [EPREFIX/lib]
#  --includedir=DIR        C header files in DIR [PREFIX/include]
#  --mandir=DIR            Man documentation in DIR [PREFIX/man]
	Exit(2);
}	# Usage



sub StripExtraSlashes
{
	my ($dir) = @_;
	$dir =~ s/\/{2,}/\//g;
	return ($dir) if ($dir eq "/");
	$dir =~ s/\/+$//g;
	return ($dir);
}	# StripExtraSlashes	




sub Getopts
{
	my($i, $arg, $val);

	for ($i = 0; $i < scalar(@ARGV); $i++) {
		$arg = $ARGV[$i];
		if ($arg eq "--help") {
			Usage();
		} elsif ($arg eq "--force") {
			$force_install = 1;
		} elsif ($arg eq "--create-new-configs") {
			$create_new_configs = 1;
		} elsif ($arg eq "--inittab") {
			if (! -f "/etc/inittab") {
				Exit(1, "This system does not use /etc/inittab.\n");
			}
			$inittab_preferred = 1;
		} elsif ($arg eq "--silent") {
			$verbose = 0;
		} elsif ($arg eq "--quiet") {
			$verbose = 0;
		} elsif ($arg eq "--debug") {
			$verbose = 2;
		} elsif ($arg =~ /^--debug=(.*)/) {
			$val = int($1);
			Exit(1, "$val is not a valid debugging level.\n")
				if (($val < 0) || ($val > 10));
			$verbose = $val;
		} elsif ($arg =~ /^--port=(.*)/) {
			$val = int($1);
			Exit(1, "$val is not a valid port number.\n")
				if (($val < 1) || ($val >= 65535));
			$ftpport = $val;
		} elsif ($arg =~ /^--lsof=(.*)/) {
			$val = StripExtraSlashes($1);
			Exit(1, "$val is not an absolute pathname.\n")
				if ($val !~ /^\//);
			$lsof = $val;
		} elsif ($arg =~ /^--prefix=(.*)/) {
			$val = StripExtraSlashes($1);
			Exit(1, "$val is not an absolute pathname.\n")
				if ($val !~ /^\//);
			$prefix = $val;
		} elsif ($arg =~ /^--exec-prefix=(.*)/) {
			$val = StripExtraSlashes($1);
			Exit(1, "$val is not an absolute pathname.\n")
				if ($val !~ /^\//);
			$eprefix = $val;
		} elsif ($arg =~ /^--bindir=(.*)/) {
			$val = StripExtraSlashes($1);
			Exit(1, "$val is not an absolute pathname.\n")
				if ($val !~ /^\//);
			$bindir = $val;
		} elsif ($arg =~ /^--sbindir=(.*)/) {
			$val = StripExtraSlashes($1);
			Exit(1, "$val is not an absolute pathname.\n")
				if ($val !~ /^\//);
			$sbindir = $val;
		} elsif ($arg =~ /^--datadir=(.*)/) {
			$val = StripExtraSlashes($1);
			Exit(1, "$val is not an absolute pathname.\n")
				if ($val !~ /^\//);
			$datadir = $val;
		} elsif ($arg =~ /^--sysconfdir=(.*)/) {
			$val = StripExtraSlashes($1);
			Exit(1, "$val is not an absolute pathname.\n")
				if ($val !~ /^\//);
			$sysconfdir = $val;
		} elsif ($arg =~ /^--runtimedatadir=(.*)/) {
			$val = StripExtraSlashes($1);
			Exit(1, "$val is not an absolute pathname.\n")
				if ($val !~ /^\//);
			$runtimedatadir = $val;
		} elsif ($arg =~ /^--logdir=(.*)/) {
			$val = StripExtraSlashes($1);
			Exit(1, "$val is not an absolute pathname.\n")
				if ($val !~ /^\//);
			$logdir = $val;
		} elsif ($arg =~ /^--libdir=(.*)/) {
			$val = StripExtraSlashes($1);
			Exit(1, "$val is not an absolute pathname.\n")
				if ($val !~ /^\//);
			$libdir = $val;
		} elsif ($arg =~ /^--includedir=(.*)/) {
			$val = StripExtraSlashes($1);
			Exit(1, "$val is not an absolute pathname.\n")
				if ($val !~ /^\//);
			$includedir = $val;
		} elsif ($arg =~ /^--mandir=(.*)/) {
			$val = StripExtraSlashes($1);
			Exit(1, "$val is not an absolute pathname.\n")
				if ($val !~ /^\//);
			$mandir = $val;
		} else {
			printf ("Argument not recognized: \"%s\".\nTry \"%s --help\" command line options.\n", $arg, $0);
			Exit(3);
		}
	}

	$prefix = "/usr/local" if (! $prefix);
	$eprefix = $prefix if (! $eprefix);
	$bindir = $eprefix . "/bin" if (! $bindir);
	$sbindir = $eprefix . "/sbin" if (! $sbindir);
	$datadir = $prefix . "/share" if (! $datadir);
	$sysconfdir = $prefix . "/etc" if (! $sysconfdir);
	$runtimedatadir = "/var" if (! $runtimedatadir);
	$libdir = $eprefix . "/lib" if (! $libdir);
	$includedir = $prefix . "/include" if (! $includedir);
	$mandir = $prefix . "/man" if (! $mandir);

	$confdir = "$sysconfdir/ncftpd";
	$rptbindir = "$sysconfdir/ncftpd/rptbin";
	$new_general_cf = "$confdir/general.cf";
	$new_domain_cf = "$confdir/domain.cf";
	$new_ncftpd = "$sbindir/ncftpd";

	if ($verbose > 1) {
		$run_ncftpd_verbose = 1;

		Log(1, "prefix=$prefix\n");
		Log(1, "eprefix=$eprefix\n");
		Log(1, "bindir=$bindir\n");
		Log(1, "sbindir=$sbindir\n");
		Log(1, "datadir=$datadir\n");
		Log(1, "sysconfdir=$sysconfdir\n");
		Log(1, "runtimedatadir=$runtimedatadir\n");
		Log(1, "logdir=$logdir\n");
	#	Log(1, "libdir=$libdir\n");
	#	Log(1, "includedir=$includedir\n");
	#	Log(1, "mandir=$mandir\n");
		Log(1, "port=$ftpport\n");
		Log(1, "run_ncftpd_verbose=$run_ncftpd_verbose\n");
		Log(1, "\n");
	}
}	# Getopts




sub Mkdirs
{
	my ($path, $mode, $uid, $gid) = @_;
	my ($subpath);
	my ($i, $n, $e);
	my (@nodes);
	my($absolute) = 0;

	if (!defined($mode)) { $mode = 00755; }		# Umask will NOT affect.
	@nodes = split(/\/+/, $path);

	$n = scalar(@nodes);
	return 1 if ($n <= 0);		# It was just the root directory

	if ($nodes[0] eq "") {
		#
		# Absolute paths will leave us with an
		# empty first node.
		#
		$absolute = 1;
		shift(@nodes);
		$n--;
	}

	#
	# Find the greatest part of the path that
	# already exists.  We will then create the
	# remaining nodes.
	#
	while (1) {
		$subpath = "";
		if ($absolute) { $subpath = "/"; }
		for ($i = 0; $i < $n; $i++) {
			$subpath .= $nodes[$i] . "/";
		}
		chop($subpath);
		last if (-d "$subpath");
		if (--$n <= 0) {
			$subpath = "";
			$n = 0;
			last;
		}
	}

	#
	# We now have in $subpath the portion of $path
	# that already exists.  We now just need to
	# create the remaining node(s).  We have in $n
	# the number of successive nodes which exist.
	#
	$e = $n;
	$n = scalar(@nodes);
	return 1 if ($e == $n);			# Entire path already exists

	for ($i = $e ; $i < $n; $i++) {
		if (($subpath eq "") && (! $absolute)) {
			$subpath = $nodes[$i];
		} else {
			$subpath .= "/" . $nodes[$i];
		}
		return 0 if (! mkdir($subpath,$mode));
		chown($uid, $gid, $subpath) if (($> == 0) && (defined($uid)) && (defined($gid)));
		chmod($mode, $subpath);
		UninstallLog("Rmdir(\"$subpath\");\n");
	}

	return 1;				# Finished
}	# Mkdirs



sub DiskFreeBytes
{
	my ($path) = @_;
	my ($dfresult) = "";	
	my (@dflines);
	my (@column_headers);
	my (@column_values);
	my ($blocksize) = 0;

	$path = "\'$path\'";
	if ($^O eq "sco3.2v5.0") {
		$dfresult = `df -P -k $path 2>/dev/null`;
	} elsif ($^O eq "sunos") {
		$dfresult = `/usr/bin/df $path 2>/dev/null`;
	} else {
		$dfresult = `df -k $path 2>/dev/null`;
	}

	#
	# This is an ugly hack, but it's pick your poison:
	# Parse "df" output which varies per platform,
	# or try to use even more variants of statfs
	# and friends from within Perl.
	#
	# The "df" option also implies that the user must
	# be running in English for this to work, but this
	# is still the most feasible option.
	#
	@dflines = split(/\r?\n/, $dfresult);
	return (-1) if (scalar(@dflines) < 2);

# aix> df -k /usr/local/bin
# Filesystem    1024-blocks      Free %Used    Iused %Iused Mounted on
# /dev/hd2           749568     20136   98%    28087    15% /usr

# solaris> df -k /usr/local/bin
# Filesystem            kbytes    used   avail capacity  Mounted on
# /dev/dsk/c0t0d0s3    3527406 1175661 2316471    34%    /opt

# sunos> /usr/bin/df /usr/local
# Filesystem            kbytes    used   avail capacity  Mounted on
# /dev/sd0h             665662  380527  218569    64%    /home

# unixware7> df -k /usr/local/bin
# filesystem          kbytes   used     avail    capacity  mounted on
# /dev/root           2369587  729785   1639802  31%       /

# openunix8> df -k /usr/local/bin
# filesystem          kbytes   used     avail    capacity  mounted on
# /dev/root           8209215  1601741  6607474  20%       /

# linux> df -k /usr/local/bin
# Filesystem           1k-blocks      Used Available Use% Mounted on
# /dev/sdb5             31245640  15312240  14346180  52% /usr

# openbsd> df -k /usr/local/bin
# Filesystem  1K-blocks     Used    Avail Capacity  Mounted on
# /dev/sd0a     1770994   233886  1448559    14%    /

# bsd/os> df -k /usr/local/bin
# Filesystem  1024-blocks     Used    Avail Capacity  Mounted on
# /dev/sd0h       3998700   921434  2877331    24%    /usr

# irix> df -k /usr/local/bin
# Filesystem             Type  kbytes     use     avail  %use Mounted on
# /dev/root               xfs  1962860  1568024   394836  80  /

# tru64unix> df -k /usr/local/bin
# Filesystem     1024-blocks        Used   Available Capacity  Mounted on
# usr_domain#usr     2150400      782697     1307000    38%    /usr
#

# scosv> df -P -k /usr/local/bin
# Filesystem         1024-blocks     Used Available Capacity Mounted on
# /dev/root              4893578   909216   3984362      19% /usr/local

	@column_headers = split(' ', $dflines[0]);
	@column_values = split(' ', $dflines[1]);

	if ((scalar(@column_headers) >= 5) && (scalar(@column_values) >= 5)) {
		if ($column_headers[1] eq "1k-blocks") {
			$blocksize = 1024;
		} elsif ($column_headers[1] eq "1024-blocks") {
			$blocksize = 1024;
		} elsif ($column_headers[1] eq "1K-blocks") {
			$blocksize = 1024;
		} elsif (($column_headers[1] eq "kbytes") && ($column_headers[2] eq "used")) {
			$blocksize = 1024;
		} elsif (($column_headers[2] eq "kbytes") && ($column_headers[3] eq "use")) {
			$blocksize = 1024;
		}

		if ($blocksize == 1024) {
			if ($column_headers[2] eq "Free") {
				return ($blocksize * $column_values[2]);
			} elsif ($column_headers[3] eq "avail") {
				return ($blocksize * $column_values[3]);
			} elsif ($column_headers[3] eq "Avail") {
				return ($blocksize * $column_values[3]);
			} elsif ($column_headers[3] eq "Available") {
				return ($blocksize * $column_values[3]);
			} elsif ($column_headers[4] eq "avail") {
				return ($blocksize * $column_values[4]);
			}
		}
	}

# hp-ux> > df -k /usr/local/bin
# /software/common       (/dev/vg00/lvol14      ) :  2235975 total allocated Kb
#                                                     408093 free allocated Kb
#                                                    1827882 used allocated Kb
#                                                         81 % allocation used
	if ($dfresult =~ /(\d+).free.allocated.Kb/s) {
		return (int($1) * 1024);
	}

	return (-1);
}	# DiskFreeBytes




sub PsGrep
{
	my ($pattern, $match_mode, $output, $pscmd) = @_;
	my (@column_headers);
	my (@column_values);
	my ($column_number);
	my ($line, $col);
	my ($pid_column) = 0;
	my ($command_pos) = 0;
	my ($command) = 0;
	my ($two_column_format) = 0;
	my (@pids) = ();

	return () unless (defined($pattern));
	$match_mode = "program" unless (defined($match_mode));
	$output = "pids" unless (defined($output));

	if ((! defined($pscmd)) || (! $pscmd)) {
		if ($^O =~ /^(solaris|aix|dec_osf|sco3.2v5.0|svr4)$/) {
			$pscmd = "/bin/ps -e -o pid,args";
		} elsif ($^O =~ /^(linux)$/) {
			$pscmd = "/bin/ps -w -e -o pid,args";
			if (open(PSCOMMAND, "/bin/ps --version |")) {
				$line = <PSCOMMAND>;
				close(PSCOMMAND);
				$pscmd = "/bin/ps auxw" if ($line =~ /version\s1/);
			}
		} elsif ($^O =~ /^(hpux)$/) {
			$pscmd = "/bin/ps -ef";
		} elsif ($^O =~ /^(unixware|openunix)$/) {
			$pscmd = "/usr/bin/ps -ef";
		} elsif ($^O =~ /^(freebsd|netbsd|openbsd|bsdos)$/) {
			$pscmd = "/bin/ps -axw -o pid,command";
		} elsif ($^O =~ /^(darwin|rhapsody)$/) {
			$pscmd = "/bin/ps -auxw";
		} elsif ($^O eq "irix") {
			$pscmd = "/sbin/ps -e -o pid,args";
		} elsif ($^O eq "sunos") {
			$pscmd = "/bin/ps -axw";
		} else {
			$pscmd = "ps -ef";
		}
	}
	$two_column_format = 1 if ($pscmd =~ /pid,/);

	if (open(PSCOMMAND, "$pscmd 2>/dev/null |")) {
		unless (defined($line = <PSCOMMAND>)) {
			close(PSCOMMAND);
			return ();
		}
		if ($two_column_format) {
			$pid_column = 0;
			if ($match_mode eq "program") {
				$pattern = '^' . $pattern . '$'; 
			}
			while (defined($line = <PSCOMMAND>)) {
				chomp($line);
				@column_values = split(' ', $line, 2);
				$command = $column_values[1];
				$command =~ s/://;
				next unless defined($command);

				if ($match_mode ne "full") {
					my (@a) = split(' ', $command);
					$command = $a[0];
					next unless defined($command);
				}
				if ($match_mode eq "program") {
					my (@a) = split(/\//, $command);
					$command = $a[-1];
					next unless defined($command);
					if ($command =~ /^[\(\[](.*)[\)\]]$/) {
						$command = $1;
						next unless defined($command);
					}
				}
				if ($command =~ /${pattern}/) {
					if ($output eq "long") {
						push(@pids, $line);
					} else {
						push(@pids, $column_values[$pid_column]);
					}
				}
			}
		} else {
			$command_pos = index($line, "COMMAND");
			$command_pos = index($line, "CMD") if ($command_pos <= 0);
			unless ($command_pos > 0) {
				close(PSCOMMAND);
				return ();
			}
			@column_headers = split(' ', $line);
			unless (scalar(@column_headers) > 0) {
				close(PSCOMMAND);
				return ();
			}
			$column_number = 0;
			for $col (@column_headers) {
				$pid_column = $column_number if ($col eq "PID");
				$column_number++;
			}
			unless ($pid_column >= 0) {
				close(PSCOMMAND);
				return ();
			}
			if ($match_mode eq "program") {
				$pattern = '^' . $pattern . '$'; 
			}
			while (defined($line = <PSCOMMAND>)) {
				chomp($line);
				@column_values = split(' ', $line);
				$command = substr($line, $command_pos);
				$command =~ s/://;
				if ($match_mode ne "full") {
					my (@a) = split(' ', $command);
					$command = $a[0];
				}
				if ($match_mode eq "program") {
					my (@a) = split(/\//, $command);
					$command = $a[-1];
					if ($command =~ /^[\(\[](.*)[\)\]]$/) {
						$command = $1;
					}
				}
				if ($command =~ /${pattern}/) {
					if ($output eq "long") {
						push(@pids, $line);
					} else {
						push(@pids, $column_values[$pid_column]);
					}
				}
			}
		}
		close(PSCOMMAND);
	}

	return (@pids);
}	# PsGrep




sub SearchPath 
{
	my ($prog, $pathstotryfirst, $pathstoignore) = @_;
	return ("") if ((! defined($prog)) || (! $prog));

	my (@ignore_paths) = ();
	my ($PATH) = $ENV{"PATH"};
	$PATH = "/bin:/usr/bin" if ((! defined($PATH)) || (! $PATH));
	if ((defined($pathstotryfirst)) && ($pathstotryfirst ne "")) {
		$PATH = "$pathstotryfirst:$PATH";
	}
	if ((defined($pathstoignore)) && ($pathstoignore ne "")) {
		@ignore_paths = split(/:/, $pathstoignore);
	}

	my (@path) = split(/:/, $PATH);
	my ($dir, $pathprog, $idir, $ignore);
	for $dir (@path) {
		$ignore = 0;
		for $idir (@ignore_paths) {
			if ($idir eq $dir) {
				$ignore = 1;
			}
		}
		next if ($ignore);
		$pathprog = "$dir/$prog";
		return ($pathprog) if (-x $pathprog);
	}
	return ("");
}	# SearchPath




sub UninstallLog
{
	my (@more_uninst_items) = @_;
	push(@uninstall_items, @more_uninst_items);
}	# UninstallLog




sub Log
{
	my ($v, @args) = @_;
	my ($arg);

	return if (! defined($v));
	if ($v !~ /\d+/) {
		$arg = $v;
		print LOG $arg if ($install_log ne "");
		print $arg;
		$v = 0;
		print $arg if ($verbose > $v);
	}

	if (scalar(@args)) {
		for $arg (@args) {
			print LOG $arg if ($install_log ne "");
			print $arg if ($verbose > $v);
		}
	}
}	# Log




sub CreateCustomUninstaller
{
	my ($line);
	my (@uninstaller) = ();
	my ($uninstall_item);
	my (@reversed_uninstall_items);
	my ($ltime) = strftime("%Y-%m-%d %H:%M:%S %z", localtime(time()));
	my ($skip) = 0;

	if (($uninstaller ne "") && (open(UNINST, "<$uninstaller"))) {
		@uninstaller = <UNINST>;
		close(UNINST);

		foreach $line (@uninstaller) {
			if ($line eq "#---prologue---#\n") {
				$line .= "#---begin prologue set by $0 on $ltime---#\n";
				$line .= "\t\$OS = \"$OS\";\n";
				$line .= "\t\$SYS = \"$SYS\";\n";
				$line .= "\t\$linux_distro = \"$linux_distro\";\n";
				$line .= "\t\$ftpport = $ftpport;\n";
				$line .= "#---end prologue set by $0 on $ltime---#\n";
			} elsif ($line eq "#---host-specific---#\n") {
				$line .= "#---begin host-specifics set by $0 on $ltime---#\n";
				$line .= "\tIsThereAnFTPServerServing();\n";
				@reversed_uninstall_items = reverse(@uninstall_items);
				for $uninstall_item (@reversed_uninstall_items) {
					chomp($uninstall_item);
					$line .= "\t$uninstall_item\n";
				}
				$line .= "\tIsThereAnFTPServerServing();\n";
				$line .= "\tExit(0);\n";
				$line .= "#---end host-specifics set by $0 on $ltime---#\n";
			} elsif ($line =~ /^#---begin/) {
				#
				# Omit existing #---begin / #---end blocks if they exist.
				#
				$line = "";
				$skip = 1;
			} elsif ($line =~ /^#---end/) {
				$line = "";
				$skip = 0;
			} elsif ($skip == 1) {
				$line = "";
			}
		}

		if (open(UNINST, ">$uninstaller")) {
			print UNINST @uninstaller;
			close(UNINST);
			chmod(0700, $uninstaller);
		}
	}
}	# CreateCustomUninstaller




sub Exit
{
	my ($es, @reason) = @_;
	if (scalar(@reason)) {
		Log(0, @reason);
	}
	if ($install_log ne "") {
		my ($ltime) = strftime("%Y-%m-%d %H:%M:%S %z", localtime(time()));
		print LOG "--- Log closed at $ltime ---\n";
		close(LOG);
		if ($confdir ne "") {
			unlink("$confdir/install_ncftpd.log");
			if (move($install_log, "$confdir/install_ncftpd.log")) {
				$install_log = "$confdir/install_ncftpd.log";
			}
		}
		print "\nInstallation log saved as $install_log.\n";
		UninstallLog("Remove(\"$install_log\");\n");
		$install_log = "";
		CreateCustomUninstaller();
	}
	exit($es);
}	# Exit




sub OpenLog
{
	if (open(LOG, ">/etc/install_ncftpd.log")) {
		my ($ltime) = strftime("%Y-%m-%d %H:%M:%S %z", localtime(time()));
		$install_log = "/etc/install_ncftpd.log";
		print LOG "--- Log created at $ltime ---\n";
	}
}	# OpenLog




sub OpenPager
{
	if (-t STDOUT) {
		if ((defined($ENV{"PAGER"})) && (-x $ENV{"PAGER"})) {
			$pager = $ENV{"PAGER"};
		} elsif (-x "/usr/bin/less") {
			$pager = "/usr/bin/less";
		} elsif (-x "/usr/bin/more") {
			$pager = "/usr/bin/more";
		} elsif (-x "/bin/more") {
			$pager = "/bin/more";
		} elsif (-x "/usr/bin/pg") {
			$pager = "/usr/bin/pg";
		}
		if ($pager ne "") {
			$pager_pid = open(STDOUT, "| $pager");
			if ($pager_pid > 0) {
				$SIG{PIPE} = 'IGNORE';
				$| = 1;
			}
		}
	} else {
		$| = 1;
	}
}	# OpenPager




sub SetOSVar
{
	my ($os)				= "";
	my ($os_v)				= "";
	my ($os_r)				= "";
	my ($archp)				= "";
	my (@os_r)				= (0,0,0);
	my (@os_v)				= (0,0,0);
	my ($arch)				= "";
	my ($line);

	$host = lc( `uname -n 2>/dev/null` );
	chomp($host);
	Log(2, "host = $host\n");

	$os = lc( `uname -s 2>/dev/null` );
	chomp($os);
	Log(2, "os = $os\n");

	$os_v = lc( `uname -v 2>/dev/null` );
	chomp($os_v);
	if ($os_v =~ /(\d+(\.\d+)*)/) {
		# Get just the version digit string
		$os_v = $1;
		@os_v = split(/\./, $os_v);
	}
	Log(2, "os_v = $os_v\n");

	$os_r = lc( `uname -r 2>/dev/null` );
	chomp($os_r);
	if ($os_r =~ /(\d+(\.\d+)*)/) {
		# Get just the version digit string
		$os_r = $1;
		@os_r = split(/\./, $os_r);
	}
	Log(2, "os_r = $os_r\n");

	$arch = lc( `uname -m 2>/dev/null` );
	chomp($arch);
	Log(2, "arch = $arch\n");

	$archp = lc( `uname -p 2>/dev/null` );
	chomp($archp);
	Log(2, "archp = $archp\n");

	if ($os eq "osf1") {
		if (($os_r[0] == 3) || ($os_r[0] == 4)) {
			$OS = "digitalunix${os_r}-$arch";
			$SYS = "digitalunix";
		} else {
			$OS = "tru64unix${os_r}-$arch";
			$SYS = "tru64unix";
		}
	} elsif ($os eq "aix") {
		$OS = "aix${os_v}.${os_r}";
		$SYS = "aix";
	} elsif ($os eq "irix") {
		$OS = "irix${os_r}";
		$SYS = "irix";
	} elsif ($os eq "irix64") {
		$OS = "irix64_${os_r}";
		$SYS = "irix64";
	} elsif ($os eq "hp-ux") {
		$OS = "hpux${os_r}";
		$SYS = "hpux";
	} elsif ($os eq "freebsd") {
		$OS = "freebsd${os_r}-$arch";
		$SYS = "freebsd";
	} elsif ($os eq "netbsd") {
		$OS = "netbsd${os_r}-$arch";
		$SYS = "netbsd";
	} elsif ($os eq "openbsd") {
		$OS = "openbsd${os_r}-$arch";
		$SYS = "openbsd";
	} elsif ($os =~ "^sco") {
		$OS = "scosv";
		$SYS = "sco";
	} elsif ($os =~ "^dynix") {
		$OS = "dynixptx${os_v}";
		$SYS = "dynixptx";
	} elsif ($os eq "linux") {
		my ($libc) = "";

		$arch = "x86" if ($arch =~ /86$/);
	
		my ($tmpfile, $rnd);
		$rnd = int(rand(10000));
		$tmpfile = $0;
		$tmpfile =~ s/^.*\///;
		$tmpfile = "$tmpfile.$$.$rnd";
		if ((-d "/root") && ($> == 0)) {
			$tmpfile = "/root/$tmpfile";
		} else {
			# FIXME: /tmp race
			#
			$tmpfile = "/tmp/$tmpfile";
		}
		Log(2, "$tmpfile\n");
		unlink("$tmpfile", "$tmpfile.c", "$tmpfile.o");
		Exit(1, "Could not open $tmpfile.c: $!\n") unless open(TMPCFILE, "> $tmpfile.c");
		print TMPCFILE <<EOF;
#include <stdio.h>
#ifdef HAVE_GNU_LIBC_VERSION_H
#	include <gnu/libc-version.h>
#endif

main()
{
	const char *ver = gnu_get_libc_version();
	const char *rel = gnu_get_libc_release();

	fprintf(stdout, "glibc%s\\n", ver);
	exit(0);
}
EOF
		Log(2, "created $tmpfile.c\n");
		close(TMPCFILE);

		my ($CC);
		if (-f "/usr/bin/gcc") {
			$CC = "/usr/bin/gcc";
		} elsif (-f "/usr/bin/cc") {
			$CC = "/usr/bin/cc";
		} else {
			$CC = "cc";
		}

		if (-f "/usr/include/gnu/libc-version.h") {
			system("$CC", "-DHAVE_GNU_LIBC_VERSION_H", "$tmpfile.c", "-o", "$tmpfile") if ($CC ne "cc");
		} else {
			system("$CC", "$tmpfile.c", "-o", "$tmpfile") if ($CC ne "cc");
		}
		if (-f "$tmpfile") {
			Log(2, "compiled $tmpfile.c to $tmpfile\n");
			$libc=`$tmpfile`;
			chomp($libc);
			Log(2, "libc = $libc\n");
		}
		unlink("$tmpfile", "$tmpfile.c", "$tmpfile.o");

		$OS = "linux-$arch";
		$SYS = "linux";

		if ($libc =~ /^glibc/) {
			if ($libc =~ /^(glibc\d+\.\d+)/) {
				$libc = $1;
			}
		} elsif (-f "/lib/libc-2.2.9.so") {
			$libc = "glibc2.2";
		} elsif (-f "/lib/libc-2.2.8.so") {
			$libc = "glibc2.2";
		} elsif (-f "/lib/libc-2.2.7.so") {
			$libc = "glibc2.2";
		} elsif (-f "/lib/libc-2.2.6.so") {
			$libc = "glibc2.2";
		} elsif (-f "/lib/libc-2.2.5.so") {
			$libc = "glibc2.2";
		} elsif (-f "/lib/libc-2.2.4.so") {
			$libc = "glibc2.2";
		} elsif (-f "/lib/libc-2.2.3.so") {
			$libc = "glibc2.2";
		} elsif (-f "/lib/libc-2.2.2.so") {
			$libc = "glibc2.2";
		} elsif (-f "/lib/libc-2.2.1.so") {
			$libc = "glibc2.2";
		} elsif (-f "/lib/libc-2.2.0.so") {
			$libc = "glibc2.2";
		} elsif (-f "/lib/libc-2.1.3.so") {
			$libc = "glibc2.1";
		} elsif (-f "/lib/libc-2.1.2.so") {
			$libc = "glibc2.1";
		} elsif (-f "/lib/libc-2.1.1.so") {
			$libc = "glibc2.1";
		} elsif (-f "/lib/libc-2.1.0.so") {
			$libc = "glibc2.1";
		} elsif (-f "/lib/libc-2.1.so") {
			$libc = "glibc2.1";
		} elsif (-f "/lib/libc-2.0.0.so") {
			$libc = "glibc2.0";
		} elsif (-f "/lib/libc-2.0.1.so") {
			$libc = "glibc2.0";
		} elsif (-f "/lib/libc-2.0.2.so") {
			$libc = "glibc2.0";
		} elsif (-f "/lib/libc-2.0.3.so") {
			$libc = "glibc2.0";
		} elsif (-f "/lib/libc-2.0.4.so") {
			$libc = "glibc2.0";
		} elsif (-f "/lib/libc-2.0.5.so") {
			$libc = "glibc2.0";
		} elsif (-f "/lib/libc-2.0.6.so") {
			$libc = "glibc2.0";
		} elsif (-f "/lib/libc-2.0.7.so") {
			$libc = "glibc2.0";
		} elsif (-f "/lib/libc-2.0.8.so") {
			$libc = "glibc2.0";
		} elsif (-f "/lib/libc-2.0.9.so") {
			$libc = "glibc2.0";
		} elsif (-f "/lib/libc.so.6") {
			$libc = "glibc2.0";
		} elsif (-f "/lib/libc.so.6.1") {
			$libc = "glibc2.0";
		} else {
			$libc = "libc5";
		}
		$linux_libc = $libc;

		$linux_distro = "unknown";
		if (-f "/etc/SuSE-release") {
			$linux_distro = "SuSE";
		} elsif (-f "/etc/turbolinux-release") {
			$linux_distro = "TurboLinux";
		} elsif ((-f "/etc/slackware-version") || (-f "/etc/slackware-release")) {
			$linux_distro = "Slackware";
		} elsif (-f "/etc/mandrake-release") {
			$linux_distro = "Mandrake";
		} elsif (-f "/etc/cobalt-release") {
			$linux_distro = "Cobalt";
		} elsif (-f "/etc/debian_version") {
			$linux_distro = "Debian";
		} elsif (-f "/etc/conectiva-release") {
			$linux_distro = "Conectiva";
		} elsif (open(RH, "</etc/redhat-release")) {
			$line = <RH>;
			close(RH);
			$linux_distro = "Red Hat";
			if ($line =~ /^Red\ Hat\ Linux/) {
				$linux_distro = "Red Hat";
			} elsif ($line =~ /^LinuxPPC/) {
				$linux_distro = "LinuxPPC";
			}
		} elsif (open(ETCISSUE, "</etc/issue")) {
			while (defined($line = <ETCISSUE>)) {
				if ($line =~ /(Caldera|Cobalt|Debian|LinuxPPC|SuSE|TurboLinux|Slackware|Mandrake|Conectiva|Red\ Hat)/) {
					$linux_distro = $1;
					last;
				} elsif ($line =~ /S\.u\.S\.E/i) {
					$linux_distro = "SuSE";
				}
			}
			close(ETCISSUE);
		}
		if ($linux_distro eq "unknown") {
			my (@unknown_releases) = glob("/etc/*-release");
			if (scalar(@unknown_releases) > 0) {
				$linux_distro = $unknown_releases[0];
				$linux_distro =~ s/\-release$//;
				$linux_distro =~ s-^.*/--;
				Log(1, "Detected unknown linux_distro=$linux_distro.\n");
			}
		}
		Log(1, "linux_distro=$linux_distro ; linux_libc=$linux_libc\n");
	} elsif ($os eq "bsd/os") {
		$OS= "bsdos${os_r}";
		$SYS = "bsdos";
	} elsif ($os eq "ultrix") {
		$OS= "ultrix-${arch}";
		$SYS = "ultrix";
	} elsif ($os eq "openunix") {
		$OS= "openunix${os_v}";
		$SYS = "openunix";
	} elsif ($os eq "unixware") {
		$OS= "unixware${os_v}";
		$SYS = "unixware";
	} elsif (($os eq "darwin") || ($os eq "rhapsody") || ($os =~ /^macos/))  {
		$OS= "macosx";
		$SYS = "macosx";
	} elsif ($os eq "sunos") {
		$arch = "sparc" if (! $arch);
		$archp = "$arch" if (! $archp);
		if (($os_r[0] == 5) && ($os_r[1] >= 7)) {
			$os_r =~ s/^\d+\.//;
			$OS = "solaris${os_r}-$archp";
			$SYS = "solaris";
		} elsif (($os_r[0] == 5) && ($os_r[1] < 7)) {
			$os_r =~ s/^5/2/;
			$OS = "solaris${os_r}-$archp";
			$SYS = "solaris";
		} elsif ($os_r[0] == 4) {
			$OS = "sunos${os_r}-$archp";
			$SYS = "sunos";
		} else {
			$OS = "solaris${os_r}-$archp";
			$SYS = "solaris";
		}
	} else {
		$OS = "$os";
		$SYS = "$os";
	}

	Log(1, "OS = $OS ; SYS = $SYS\n");
}	# SetOSVar




sub SetOSDefaults
{
	# Set log dir to OS defaults, unless we already have a /var/log.
	#
	if ((! -d $logdir) || (-l $logdir)) {
		if ($SYS eq "aix") {
			$logdir = "/var/adm";
		} elsif ($SYS =~ /^irix/) {
			$logdir = "/var/adm";
		} elsif ($SYS eq "hpux") {
			$logdir = "/var/spool/log";
		} elsif ($SYS eq "sco") {
			$logdir = "/var/adm/log";
		} elsif ($SYS eq "unixware") {
			$logdir = "/var/adm/log";
		} elsif ($SYS eq "openunix") {
			$logdir = "/var/adm/log";
		}
	}
	$startup_error_log = "$logdir/ncftpd/startup_errors";
}	# SetOSDefaults




sub VerifyOSPrerequisites
{
	if (($SYS eq "macosx") && (! -x "/usr/bin/ftp")) {
		Exit(1, "NcFTPd for Mac OS X requires the BSD Subsystem.  This is located on the Mac OS X installation disc.\n");
	}
}	# VerifyOSPrerequisites




sub SeeIfNcFTPdIsBinaryCompatible
{
	my ($f) = @_;
	my ($osdir, $ver, $n);

	$osdir = $f;
	$osdir =~ s/\/[^\/]+$//;
	$n = "$osdir/ncftpd";
	Log(1, "Try to run \"$n -b\".\n");
	$ver = "";
	$ver = `$n -b 2>&1`;
	chomp($ver);
	# i.e: "NcFTPd 2.7.1/849"
	if ($ver =~ /^NcFTPd\s([1-9]\.\d+\.\d+)\/\d+$/) {
		Log(2, "  result = [$ver]\n");
		$ver = $1;
		$install_version = $ver;
		$install_src_root = $osdir;
		$binary_compat_err = "";
		return (1);
	} else {
		Log(1, "  result = [$ver]\n");
		$binary_compat_err = $ver;
	}
	return (0);
}	# SeeIfNcFTPdIsBinaryCompatible




sub VerifyLocation
{
	my ($wd) = $0;
	my (@os_ncftpd);
	my ($f);

	$wd =~ s/\/*install_ncftpd.pl$//;	
	$wd = "." if (! $wd);
	if ($wd ne ".") {
		chdir($wd) or Exit(1, "Could not change directory to \"$wd\": $!\n");
	}
	$wd = `pwd 2>/dev/null`;
	chomp($wd);
	$wd = "." if (! $wd);
	Exit(1, "Could not find install_ncftpd.pl in directory \"$wd\"!\n") if (! -f "install_ncftpd.pl");
	$install_src_root = $wd;

	$install_version = "";
	@os_ncftpd = glob("$wd/*/ncftpd");
	if (scalar(@os_ncftpd) > 0) {
		foreach $f (@os_ncftpd) {
			$f =~ s-/ncftpd$--;
			$f =~ s-^.*/--;
		}
		if ($SYS eq "linux") {
			@os_ncftpd = sort {
				if ($a eq $linux_libc) {
					return (-1);
				} elsif ($b eq $linux_libc) {
					return (1);
				} else {
					my ($aver, $bver);

					$aver = $a;
					$aver =~ s/libc5/0/;
					$aver =~ s/[^\d]//g;
					$aver .= substr("00000", length($aver));

					$bver = $b;
					$bver =~ s/libc5/0/;
					$bver =~ s/[^\d]//g;
					$bver .= substr("00000", length($bver));

					$bver <=> $aver;
				}
			} @os_ncftpd;
		} else {
			@os_ncftpd = sort {
				if ($a eq $OS) {
					return (-1);
				} elsif ($b eq $OS) {
					return (1);
				} else {
					my ($aver, $bver);

					$aver = $a;
					$aver =~ s/[^\d]//g;
					$aver .= substr("00000", length($aver));

					$bver = $b;
					$bver =~ s/[^\d]//g;
					$bver .= substr("00000", length($bver));

					$bver <=> $aver;
				}
			} @os_ncftpd;
		}
		Log(2, "List of platform subdirectories found:\n");
		for $f (@os_ncftpd) {
			Log(2, "  " . $f . "\n");
		}
		for $f (@os_ncftpd) {
			last if (SeeIfNcFTPdIsBinaryCompatible("$wd/$f/ncftpd"));
		}
		if ($install_version eq "") {
			Log(0, "\nERROR: The package you downloaded will not run on this platform.\n");
			Log(0, "       Check http://www.ncftp.com/download/ for a package for $OS.\n");
			Exit(1);
		}
	} elsif (! -f "$wd/ncftpd") {
		Log(0, "\nERROR: There is no \"ncftpd\" program in the same directory as this script,\n$wd/install_ncftpd.pl.\n");
		Exit(1);
	} else {
		if (! SeeIfNcFTPdIsBinaryCompatible("$wd/ncftpd")) {
			if ($SYS eq "openbsd") {
				my ($libc_so_link);
				my (@libc_sos, $libc_so);

				if ($binary_compat_err =~ /(libc\.so\.[^:]+): No\ such\ file\ or\ directory/) {
					$libc_so_link = "/usr/lib/" . $1;
					@libc_sos = glob("/usr/lib/libc.so.*");
					$libc_so = $libc_sos[0];
					Symlink($libc_so, $libc_so_link, "do not overwrite");
				}
				if (! SeeIfNcFTPdIsBinaryCompatible("$wd/ncftpd")) {
					Log(0, "\nERROR: The package you downloaded will not run on this platform.\n");
					Log(0, "       Check http://www.ncftp.com/download/ for a package for $OS.\n");
					Exit(1);
				}
			} else {
				Log(0, "\nERROR: The package you downloaded will not run on this platform.\n");
				Log(0, "       Check http://www.ncftp.com/download/ for a package for $OS.\n");
				Exit(1);
			}
		}
	}

	Log(1, "Install source root is $install_src_root.\n");
	if ($install_src_root eq $wd) {
		return "$install_src_root/extra:$install_src_root";
	} else {
		return "$install_src_root/extra:$install_src_root:$wd";
	}
}	# VerifyLocation




sub VerifyPackageContents
{
	my ($f, $path);
	my (@required_files) = (
		"LICENSE",
		"conf/general.cf-dist",
		"conf/domain.cf-dist",
		"ncftpd",
		"ncftpd_edquota",
		"ncftpd_passwd",
		"ncftpd_repquota",
		"ncftpd_spy",
		"extra/ncftpd.init",
		"extra/ncftpd.sh",
	);
	my (@required_dirs) = (
		"extra",
		"rptbin",
	);
	for $f (@required_files) {
		$path = "$install_src_root/$f";
		if (! -f $path) {
			Log(0, "Missing required file for installation: $path.\n") if ($force_install);
			Exit(1, "Missing required file for installation: $path.\n") if (! $force_install);
		}
	}
	for $f (@required_dirs) {
		$path = "$install_src_root/$f";
		if (! -d $path) {
			Log(0, "Missing required directory for installation: $path.\n") if ($force_install);
			Exit(1, "Missing required directory for installation: $path.\n") if (! $force_install);
		}
	}
}	# VerifyPackageContents




sub VerifyFreeSpace
{
	my ($bytesfree, $kbytesfree, $duresult, $kbytesneeded);

	$bytesfree = DiskFreeBytes($eprefix);
	if ($bytesfree < 0) {
		Log(0, "Could not determine free space on $prefix.\n");
		return;
	}
	$kbytesfree = $bytesfree / 1024;
	Log(1, sprintf("$eprefix kbytes free: %.2f\n", $kbytesfree));

	if ($^O eq "sunos") {
		$duresult = `cd $install_src_root && du -s $install_src_root 2>&1`;
	} else {
		$duresult = `cd $install_src_root && du -s -k $install_src_root 2>&1`;
	}
	chomp($duresult);
	if ($duresult !~ /^(\d+)/) {
		Log(0, "Could not determine disk usage for $install_src_root.\n");
		Log(0, "Result was [$duresult]\n");
		return;
	}
	$kbytesneeded = int($1) + 100;
	Log(1, sprintf("$install_src_root kbytes usage: %.2f\n", $kbytesneeded));
	if ($kbytesneeded > $kbytesfree) {
		Log(0, sprintf("ERROR: %u kB of free space is required on $prefix,\n", $kbytesneeded));
		Log(0, sprintf("       but only %.2f kB available.\n", $kbytesfree));
		Exit(1);
	}
}	# VerifyFreeSpace




sub SetPATHEnvironmentVar
{
	my ($path_prefix) = @_;
	my (@path_prefix_list) = ();
	my ($HOME) = $ENV{"HOME"};
	my ($newPATH) = "";
	my (@origPATHs) = split(/:/, $ENV{"PATH"});
	my (@paths) = ();
	my (@pre_paths);
	my ($dir);
	my (%found_paths);

	$HOME = "/" unless (defined($HOME));
	$ENV{"PATH"} = "/bin:/usr/bin" if (! defined($ENV{"PATH"}));
	@origPATHs = split(/:/, $ENV{"PATH"});
	$extra_paths = $path_prefix if (defined($path_prefix));
	@path_prefix_list = split(/:/, $path_prefix) if defined($path_prefix);
	if ($> != 0) {
		#
		# PATH for unprivileged user.
		#
		@paths = (
			@path_prefix_list,
			".",
			"$HOME/$OS/bin",
			"$HOME/bin/powerpc-apple-macos",
			"$HOME/bin",
			"$HOME/sh",
			"/opt/SUNWspro/bin",
			"/opt/ansic/bin",
			"/usr/vac/bin",
			"/usr/ccs/bin",
			"/udk/usr/ccs/bin",
			"/usr/local/bin",
			"/usr/local/sbin",
			"/bin",
			"/usr/bin",
			"/usr/contrib/bin",
			"/usr/X11R6/bin",
			"/usr/bin/X11R6",
			"/usr/X11/bin",
			"/usr/bin/X11",
			"/usr/sbin",
			"/sbin",
			"/etc",
			"/usr/ucb",
			"/usr/bsd",
			"/usr/freeware/bin",
			"/usr/openwin/bin",
			@origPATHs
		);
	} else {
		#
		# PATH for Superuser.
		#

		my (@pre_paths) = ();
		if ($HOME ne "/") {
			@pre_paths = (
				"$HOME/bin/powerpc-apple-macos",
				"$HOME/bin",
				"$HOME/sh",
				);
		}

		@paths = (
			@path_prefix_list,
			@pre_paths,
			"/bin",
			"/usr/bin",
			"/usr/sbin",
			"/sbin",
			"/usr/local/sbin",
			"/etc",
			"/usr/local/bin",
			"/usr/ucb",
			"/usr/bsd",
			"/opt/SUNWspro/bin",
			"/opt/ansic/bin",
			"/usr/vac/bin",
			"/usr/ccs/bin",
			"/udk/usr/ccs/bin",
			"/usr/contrib/bin",
			"/usr/X11R6/bin",
			"/usr/bin/X11R6",
			"/usr/X11/bin",
			"/usr/bin/X11",
			"/usr/freeware/bin",
			"/usr/openwin/bin",
			@origPATHs
		);
	}

	for $dir (@paths) {
		if (-d $dir) {
			if (! defined($found_paths{$dir})) {
				$found_paths{$dir} = 1;
				if ($newPATH eq "") {
					$newPATH = $dir;
				} else {
					$newPATH .= ":" . $dir;
				}
			}
		}
	}

	Log(1, "Setting PATH to \"$newPATH\".\n");
	$ENV{"PATH"} = $newPATH;
}	# SetPATHEnvironmentVar




sub LookupFTPServiceName
{
	return if ($ftpport == 21);
	return if ((defined($ftpservicename)) && ($ftpservicename ne "") && ($ftpservicename ne "ftp"));

	my ($ftpdataport) = $ftpport - 1;
	my ($name,$aliases,$port,$proto) = getservbyport($ftpport, "tcp");
	if (defined($name)) {
		$ftpservicename = $name;
	} else {
		($name,$aliases,$port,$proto) = getservbyname("ftp-$port", "tcp");
		if (defined($port) && defined($name) && ($port == $ftpport)) {
			$ftpservicename = $name;
		} elsif ((-f "/etc/services") && (open(ETCSERVICES, ">>/etc/services"))) {
			print ETCSERVICES "\n#\n# Added by install_ncftpd.pl\n#\n";
			print ETCSERVICES "ftp-$ftpport-data\t$ftpdataport/tcp\n";
			print ETCSERVICES "ftp-$ftpport\t$ftpport/tcp\t\t\t# Alternate FTP Service port\n";
			Log(0, "Added \"ftp-$ftpport\" to /etc/services.\n");
			close(ETCSERVICES);
			UninstallLog("RemoveFromEtcServices(\"ftp-$ftpport-data\", \"ftp-$ftpport\");\n");
		}
	}
}	# LookupFTPServiceName




sub FindLsof
{
	return (1) if (($lsof ne "") && (-x $lsof));
	$lsof = SearchPath("lsof", "/sbin:/usr/sbin:/usr/local/sbin:/usr/bin:/bin");
	return (0) if ($lsof eq "");
	return (1);
}	# FindLsof




sub FindPerl
{
	return (1) if (($perl ne "") && (-x $perl));
	$perl = SearchPath("perl");
	$perl = "/usr/bin/perl" if ($perl eq "");
	return (1);
}	# FindPerl




sub AbortIfSomethingWeDontKnowHowToDisableIsUsingPort21()
{
	my (@lsof_result);

	if (! FindLsof()) {
		Log(1, "Could not find lsof.\n");
	} elsif (open(LSOF, "$lsof -w -i tcp:$ftpport 2>/dev/null |")) {
		@lsof_result = <LSOF>;
		close(LSOF);
		if ((defined($lsof_result[0])) && ($lsof_result[0] =~ /^COMMAND/)) {
			my ($lines_printed) = 0;
			my ($line);
			for ($line = 1; $line < scalar(@lsof_result); $line++) {
				my (@junk) = split(' ', $lsof_result[$line], 2);
				my ($program) = $junk[0];
				if ($program !~ /(ncftpd|xinetd|inetd|ftpd)/) {
					if (! $lines_printed) {
						Log(0, "\nERROR: You must first uninstall your current FTP server software.\n");
						Log(0, "The following processes are using the FTP service port ($ftpport):\n\n");
						Log(0, "  ", $lsof_result[0]);
					}
					Log(0, "  ", $lsof_result[$line]);
					$lines_printed++;
				} elsif ($program =~ /ncftpd/) {
					$ncftpd_running_at_time_of_install++;
				} elsif ($program =~ /xinetd/) {
					$xinetd_running_at_time_of_install++;
				} elsif ($program =~ /inetd/) {
					$inetd_running_at_time_of_install++;
				}
			}
			if ($lines_printed) {
				Log(0, "\n");
				Log(0, "You can try killing these processes and restarting the installation,\n");
				Log(0, "but you must disable the other software's startup script. Otherwise,\n");
				Log(0, "the next time you reboot the machine NcFTPd will not be able to start.\n");
				Exit(1);
			}
		} else {
			Log(1, "No processes are currently handling FTP.\n");
		}
		Log(0, "NcFTPd is currently handling FTP.\n") if ($ncftpd_running_at_time_of_install > 0);
		Log(0, "Inetd is currently handling FTP.\n") if ($inetd_running_at_time_of_install > 0);
		Log(0,"Xinetd is currently handling FTP.\n") if ($xinetd_running_at_time_of_install > 0);
	} else {
		Log(1, "Could not run lsof.\n");
	}
}	# AbortIfSomethingWeDontKnowHowToDisableIsUsingPort21




sub CreateEtcShells
{
	return if (-f "/etc/shells");

	my (@shells_found) = ();
	my ($shell);
	my ($path);
	my ($aix_shells_found) = 0;

	if (open(AIXSHELLS, "/etc/security/login.cfg")) {
		#
		# AIX has information about its shells in this file.
		#
		# Example:
		#	shells = /bin/sh,/bin/bsh,/bin/csh,/bin/ksh,/bin/tsh,/usr/bin/sh,/usr/bin/bsh,/usr/bin/csh,/usr/bin/ksh,/usr/bin/tsh,/usr/sbin/sliplogin
		#
		my (@logincfgdata) = <AIXSHELLS>;
		close(AIXSHELLS);
		my (@shells_line) = grep(/^\s*shells\s*=/, @logincfgdata);
		if (scalar(@shells_line) > 0) {
			if ($shells_line[0] =~ /\=\s*(\/.*)/) {
				$shells_line[0] = $1;
				@shells_found = split(/,/, $shells_line[0]);
				$aix_shells_found = scalar(@shells_found);
			}
		}
	}

	my (@paths) = ("/sbin", "/bin", "/bin/posix", "/usr/bin", "/usr/local/bin");
	my (@shells) = ("sh", "ksh", "csh", "bash", "tcsh", "zsh", "pdksh", "rksh", "passwd");

	for $path (@paths) {
		for $shell (@shells) {
			if (-x "$path/$shell") {
				my ($a);
				my ($b) = 0;
				for $a (@shells_found) {
					if ($a eq "$path/$shell") {
						$b = 1;
						last;
					}
				}
				push(@shells_found, "$path/$shell") if ($b == 0);
			}
		}
	}

	return if (scalar(@shells_found) == 0);
	if (open(ETCSHELLS, ">/etc/shells")) {
		print ETCSHELLS "# NcFTPd will not allow users to connect who are not using one of\n# these shells.\n";

		if ($aix_shells_found) {
			print ETCSHELLS "#\n";
			print ETCSHELLS "# This file was generated from /etc/security/login.cfg.\n";
			print ETCSHELLS "# If you change this file, also change /etc/security/login.cfg.\n";
		}

		print ETCSHELLS "#\n";
		for $shell (@shells_found) {
			print ETCSHELLS $shell, "\n";
		}	
		close(ETCSHELLS);
		chmod(0644, "/etc/shells");
		Log(0, "Created /etc/shells.\n");
		UninstallLog("Remove(\"/etc/shells\");\n");
	}
}	# CreateEtcShells




sub CreateEtcFtpusers
{
	my ($user);
	my ($t0, $t1);
	my ($uid);
	my (@users) = (	"root", "kroot", "broot", "croot", "toor",
			"4Dgifts",
			"adabas",
			"adm",
			"Administrator",
			"administrator",
			"amanda",
			"anon",
			"at",
			"auditor",
			"auth",
			"bin",
			"bind",
			"cmwlogin",
			"cron",
			"cyrus",
			"daemon",
			"db2",
			"db2as",
			"db2fenc1",
			"db2inst1",
			"dbadmin",
			"dbmaker",
			"demo",
			"demos",
			"diag",
			"dpbox",
			"empress",
			"EZsetup",
			"fax",
			"fib",
			"firewall",
			"fixadm",
			"fixlohn",
			"flexlm",
			"fnet",
			"games",
			"gdm",
			"gnats",
			"guest",
			"halt",
			"hpdb",
			"imap",
			"informix",
			"ingres",
			"irc",
			"ixess",
			"kmem",
			"listen",
			"lnx",
			"lp",
			"lpd",
			"mail",
			"man",
			"mdom",
			"mosaic",
			"mysql",
			"named",
			"netscape",
			"news",
			"noaccess",
			"nobody",
			"nobody4",
			"nobodyV",
			"nonroot",
			"nonuser",
			"notes",
			"nouser",
			"nps",
			"ntp",
			"nuucp",
			"operator",
			"oracle",
			"OutOfBox",
			"pop",
			"postfix",
			"postgres",
			"quota",
			"radius",
			"reboot",
			"rfindd",
			"ris",
			"seti",
			"sgiweb",
			"shutdown",
			"skyrix",
			"spooladm",
			"sql",
			"squid",
			"sync",
			"sys",
			"sysadm",
			"tacacs",
			"tcb",
			"tty",
			"unknown",
			"uucp",
			"uucpa",
			"virtuoso",
			"vmware",
			"web",
			"wnn",
			"www",
			"wwwrun",
			"xfs",
			"xten",
			"yard",
			);
	my (@users_found) = ();
	my ($number_of_ftpusers_loaded) = 0;

	if (open(ETCFTPUSERS, "</etc/ftpusers")) {
		while (defined($user = <ETCFTPUSERS>)) {
			chomp($user);
			next unless ($user =~ /^[A-Za-z]/);
			push (@users_found, $user);
		}
		close(ETCFTPUSERS);
	}
	$number_of_ftpusers_loaded = scalar(@users_found);

	$t0 = time();
	Log(1,"Checking system user database for /etc/ftpusers suggestions.\n");
	for $user (@users) {
		my (@found) = grep(/$user/, @users_found);
		next if (scalar(@found) > 0);
		$uid = getpwnam($user);
		push (@users_found, $user) if (defined($uid));
		$t1 = time();
		if (($t1 - $t0) > 15) {
			Log(0, "Warning: It is taking too long to lookup usernames.  Perhaps you're using NIS or LDAP?\n");
			Log(0, "         The /etc/ftpusers checks will be skipped.\n");
			return;
		}
	}

	return if (scalar(@users_found) == 0);

	if ($number_of_ftpusers_loaded == 0) {
		if (open(ETCFTPUSERS, ">/etc/ftpusers")) {
			for $user (@users_found) {
				print ETCFTPUSERS $user, "\n";
			}	
			close(ETCFTPUSERS);
			chmod(0644, "/etc/ftpusers");
			Log(0, "Created /etc/ftpusers.\n");
			UninstallLog("Remove(\"/etc/ftpusers\");\n");
		}
	} elsif ($number_of_ftpusers_loaded < scalar(@users_found)) {
		# Don't munge the system's existing file,
		# but warn about users we think should be
		# in there.
		#
		Log(0, "\n");
		Log(0, "Warning: your /etc/ftpusers file may be missing entries.\n");
		Log(0, "The /etc/ftpusers file is a list of users who should NOT\n");
		Log(0, "be allowed to login by FTP.  The following users were found\n");
		Log(0, "on your system, and are NOT in /etc/ftpusers.  Please review\n");
		Log(0, "the list below, and if one or more of the users below should\n");
		Log(0, "not be logging in by FTP, add them to /etc/ftpusers using\n");
		Log(0, "a text editor (such as ", defined($ENV{EDITOR}) ? $ENV{EDITOR} : "vi", ").\n\n");
		my ($i);
		my ($j) = 0;
		my ($n) = scalar(@users_found);
		for ($i = $number_of_ftpusers_loaded; $i < $n; $i++, $j++) {
			$user = $users_found[$i];
			Log(0, "    ") if (($j % 4) == 0);
			Log(0, sprintf("%-12s", $user));
			Log(0, "\n") if ((($j % 4) == 3) || ($i == ($n - 1)));
		}
		Log(0, "\n");
	}
}	# CreateEtcFtpusers




sub AddFTPGroup
{
	my ($i, $grp);
	my (@altgroups) = ("www", "web", "daemon", "nobody");

	$ftpgid = getgrnam($ftpgroup);
	if ((defined($ftpgid)) && ($ftpgid > 0)) {
		Log(1, "Using group $ftpgroup (UID $ftpgid)\n");
		return;
	}

	#
	# The "ftp" group does not exist.
	# Try to add one.
	#
	Log(1, "$ftpgroup group does not exist\n");

	#
	# Pick a random GID.
	#
	for ($ftpgid = 50; $ftpgid < 400; $ftpgid += 10) {
		last if (! defined(getgrgid($ftpgid)));
	}

	if ($SYS eq "macosx") {
		system("niutil", "-create",     "/", "/groups/$ftpgroup");
		system("niutil", "-createprop", "/", "/groups/$ftpgroup", "passwd", "*");
		system("niutil", "-createprop", "/", "/groups/$ftpgroup", "gid", "$ftpgid");
	} elsif ($SYS eq "bsdos") {
		system("/usr/sbin/addgroup", "-g", $ftpgid, $ftpgroup);
	} elsif ($SYS eq "aix") {
		system("/usr/bin/mkgroup", "id=$ftpgid", $ftpgroup);
	} elsif ($SYS eq "sco") {
		system("/etc/groupadd", "-g", $ftpgid, $ftpgroup);
	} elsif (($SYS =~ /^(openbsd|netbsd|solaris|linux|tru64unix|digitalunix|unixware|openunix|hpux)$/) && (-x "/usr/sbin/groupadd")) {
		system("/usr/sbin/groupadd", "-g", $ftpgid, $ftpgroup);
	}

	$ftpgid = getgrnam($ftpgroup);
	if ((defined($ftpgid)) && ($ftpgid > 0)) {
		Log(0, "Created group $ftpgroup (UID $ftpgid)\n");
		UninstallLog("RemoveGroup(\"$ftpgroup\");\n");
		return;
	}

	# We'd prefer to be able to add the group, but we
	# can live without it since we can borrow commonly
	# available groupnames.
	#
	for $grp (@altgroups) {
		$ftpgid = getgrnam($grp);
		if ((defined($ftpgid)) && ($ftpgid > 0)) {
			$ftpgroup = $grp;
			Log(1, "Using alternate choice for group = $ftpgroup (UID $ftpgid)\n");
			return;
		}
	}
}	# AddFTPGroup




sub AddFTPUser
{
	my ($i, $usr);
	my (@altusers) = ("www", "web", "daemon", "nobody");
	my ($noshell) = "";	# Most systems just leave the shell field blank.
	my (@shells) = ();
	my ($modified_etc_shells) = 0;

	$ftpuid = getpwnam($ftpuser);
	if ((defined($ftpuid)) && ($ftpuid > 0)) {
		Log(1, "Using user $ftpuser (UID $ftpuid)\n");
		return;
	}

	#
	# The "ftp" user does not exist.
	# Try to add one.
	#
	Log(1, "$ftpuser user does not exist\n");

	$noshell = "/sbin/nologin" if (-x "/sbin/nologin");
	$noshell = "/dev/null" if ($SYS eq "macosx");
	$noshell = "/dev/null" if ($SYS =~ /^irix/);
	$noshell = "/nonexistent" if ($SYS eq "freebsd");
	$noshell = "/bin/false" if ($SYS eq "tru64unix");	# Or else userdel fails
	$noshell = "/bin/false" if ($SYS eq "solaris");		# Or else useradd fails
	$noshell = "/bin/false" if ($SYS eq "unixware");	# Or else useradd fails
	$noshell = "/bin/false" if ($SYS eq "openunix");	# Or else useradd fails
	$noshell = "/bin/false" if ($SYS eq "hpux");		# Or else useradd fails

	if (($SYS =~ /^(forfutureuse)$/) && (-f "/etc/shells")) {
		#
		# Some systems won't let you add a user unless
		# you specify a valid shell program.  We don't
		# want to add the "ftp" user with a real shell,
		# so we add /bin/false to /etc/shells which
		# is harmless if actually logged in with.
		#
		if (open(ETCSHELLS, "</etc/shells")) {
			@shells = <ETCSHELLS>;
			Log(1, "Loading /etc/shells.\n");
			close(ETCSHELLS);
			if (scalar(grep(/$noshell/, @shells)) <= 0) {
				#
				# Our no-shell wasn't already in there,
				# add it temporarily.
				#
				Log(1, "$noshell was not in /etc/shells.\n");
				if (open(ETCSHELLS, ">>/etc/shells")) {
					print ETCSHELLS $noshell, "\n";
					close(ETCSHELLS);
					$modified_etc_shells = 1;
					Log(1, "Added $noshell to /etc/shells.\n");
				}
			}
		}
	}

	#
	# Pick a random UID.
	#
	$ftpuid = 14;
	$ftpuid = 214 if ($SYS eq "sco");	# Otherwise SCO useradd fails
	for ($ftpuid = 14; $ftpuid < (65534 - 42); $ftpuid += 41) {
		last if (! defined(getpwuid($ftpuid)));
	}

	if ($SYS eq "macosx") {
		system("niutil", "-create",     "/", "/users/$ftpuser");
		system("niutil", "-createprop", "/", "/users/$ftpuser", "shell", "$noshell");
		system("niutil", "-createprop", "/", "/users/$ftpuser", "passwd", "*");
		system("niutil", "-createprop", "/", "/users/$ftpuser", "realname", "FTP Server");
		system("niutil", "-createprop", "/", "/users/$ftpuser", "uid", $ftpuid);
		system("niutil", "-createprop", "/", "/users/$ftpuser", "gid", $ftpgid);
		$ftphome = "/Library/FTPServer/Documents";
		system("niutil", "-createprop", "/", "/users/$ftpuser", "home", $ftphome);
		# system("niutil", "-createprop", "/", "/users/$ftpuser", "_shadow_passwd", "");
		system("niutil", "-createprop", "/", "/users/$ftpuser", "expire", "0");
		system("niutil", "-createprop", "/", "/users/$ftpuser", "change", "0");
	} elsif ($SYS =~ /^irix/) {
		# There doesn't seem to be a corresponding groupmgmt.
		system("/usr/sbin/passmgmt", "-a", "-u", $ftpuid, "-g", $ftpgid, "-c", "FTP User", "-s", $noshell, $ftpuser);
	} elsif ($SYS eq "bsdos") {
		system("/usr/sbin/adduser", "-u", $ftpuid, "-g", $ftpgid, "-G", "FTP User", "-s", $noshell, "-H", "/usr/home/$ftpuser", "-P", "*", $ftpuser);
	} elsif ($SYS eq "aix") {
		# User will get created with a regular shell, 
		# but they can't login in since we set su,rlogin,login=false.
		#
		system("/usr/bin/mkuser", "su=false", "daemon=false", "expires=0", "gecos=FTP User", "id=$ftpuid", "login=false", "pgrp=$ftpgroup", "rlogin=false", "su=false", "umask=022", $ftpuser);
	} elsif ($SYS eq "sco") {
		system("/etc/useradd", "-u", $ftpuid, "-g", $ftpgid, "-c", "FTP User", "-s", $noshell, $ftpuser);
	} elsif (($SYS =~ /^(openbsd|netbsd|linux|tru64unix|digitalunix|solaris|unixware|openunix|hpux)$/) && (-x "/usr/sbin/useradd")) {
		system("/usr/sbin/useradd", "-u", $ftpuid, "-g", $ftpgid, "-c", "FTP User", "-s", $noshell, $ftpuser);
	}
	
	if ($modified_etc_shells) {
		if (open(ETCSHELLS, ">/etc/shells")) {
			print ETCSHELLS @shells;
			close(ETCSHELLS);
			$modified_etc_shells = 0;
			Log(1, "Restored /etc/shells.\n");
			chmod(0644, "/etc/shells");
		}
	}

	$ftpuid = getpwnam($ftpuser);
	if ((defined($ftpuid)) && ($ftpuid > 0)) {
		Log(0, "Created user $ftpuser (UID $ftpuid)\n");
		UninstallLog("RemoveUser(\"$ftpuser\");\n");
		return;
	}

	# We'd prefer to be able to add the user, but we
	# can live without it since we can borrow commonly
	# available usernames.
	#
	for $usr (@altusers) {
		$ftpuid = getpwnam($usr);
		if ((defined($ftpuid)) && ($ftpuid > 0)) {
			$ftpuser = $usr;
			Log(1, "Using alternate choice for user = $ftpuser (UID $ftpuid)\n");
			return;
		}
	}
}	# AddFTPUser




sub CreateFTPHome
{
	my ($name,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell,$expire);

	($name,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell,$expire) = 
		getpwnam($ftpuser);
	if (defined($dir)) {
		if (($ftpuser eq "www") && ($dir eq "/Library/WebServer")) {
			#
			# For Mac OS X, we'll try to use "www", and the
			# htdocs directory (/Library/WebServer/Documents)
			# for ftp-home.
			#
			$ftphome = "/Library/WebServer/Documents";
			Log(0, "Using $ftphome for ftp-home\n");
		} elsif ($ftpuser ne $ftpuser_preferred) {
			#
			# If we couldn't add "ftp", NcFTPd will use one of
			# the alternate users.  In this case, NcFTPd will
			# also not use that alternate user's home directory
			# as ftp-home.
			#
			if (-d "/export/home") {
				$ftphome = "/export/home/ftp";
			} elsif (-d "/home") {
				$ftphome = "/home/ftp";
			} elsif (-d "/usr/local") {
				$ftphome = "/usr/local/ftp";
			} else {
				$ftphome = "/usr/ftp";
			}
			Log(0, "Using $ftphome for ftp-home\n");
		} else {
			$ftphome = $dir;
			Log(0, "Using $ftphome for ftp-home\n");
		}

		#
		# Note that NcFTPd can run if ftp-home doesn't exist.
		#
		if ((! -d $ftphome) && (Mkdirs($ftphome, 00755))) {
			Log(0, "Created ftp-home $ftphome\n");
			if (open(README, ">$ftphome/README")) {
				print README "This is the anonymous FTP area on $host.\n";
				print README "Files for public access to the world can be placed here.\n";
				close(README);
				chmod(0644, "$ftphome/README");
				UninstallLog("Remove(\"$ftphome/README\");\n");
			}
		}

		#
		# Make sure ~ftp is NOT owned by ftp!
		#
		chmod(0755, $ftphome);
		chown(0, 1, $ftphome);
	}
}	# CreateFTPHome




sub IsThereAnFTPServerServing
{
	my ($v) = @_;
	my ($hardway) = 0;
	my ($serveraddr);

	$v = 0 if (! defined($v));
	$serveraddr = inet_aton("localhost") || inet_aton("127.0.0.1");
	$hardway = 1 if (! defined($serveraddr));

	$hardway = 1 if (! socket(FTPSOCKET, PF_INET, SOCK_STREAM, getprotobyname('tcp')));

	if ($hardway) {
		#
		# This older way doesn't work as well.
		#
		Log(2, "Using alternate method to tell if FTP server is running.\n");
		my ($ftp) = SearchPath("ftp", "/usr/bin");
		my ($echo) = SearchPath("echo", "/bin:/usr/bin");
		if ($ftp eq "") {
			Log(0, "/usr/bin/ftp is not present.\n");
			Log(0, "Cannot tell if FTP server is running.\n");
			return (0);
		}
		if ($echo eq "") {
			Log(0, "/bin/echo is not present.\n");
			$echo = "echo";		# Try anyway
		}
		if (open(FTP, "$echo quit | $ftp -d -n 127.0.0.1 2>/dev/null |")) {
			my (@ftpresult) = <FTP>;
			close(FTP);
			# Connected to localhost.
			# 220 Hurricane NcFTPd Server (free personal license) ready.
			# ftp> quit
			# ---> QUIT
			# 221 Goodbye.
			my (@quitresult) = grep(@ftpresult, "QUIT");
			if (scalar(@quitresult) > 0) {
				Log($v, "An FTP server is running.\n");
				return (1);
			}
		}
		Log($v, "No FTP server is currently running.\n");
		return (0);
	}

	$serveraddr = sockaddr_in($ftpport, $serveraddr);
	if (connect(FTPSOCKET, $serveraddr)) {
		print FTPSOCKET "QUIT\r\n";
		close(FTPSOCKET);
		Log($v, "An FTP server is running.\n");
		return (1);
	}

	close(FTPSOCKET);
	Log($v, "No FTP server is running.\n");
	return (0);
}	# IsThereAnFTPServerServing




sub CommentOutFTPLineInInetdConf
{
	my ($inetd_conf) = "/etc/inetd.conf";
	$inetd_conf = "/etc/inet/inetd.conf" if ($SYS eq "solaris");

	my ($inetd_conf_new) = "$inetd_conf.new";
	my ($line);
	my ($disabled, $disableprefix);

	return unless (-f $inetd_conf);

	$disabled = 0;
	if (open(INETDCONF, "<$inetd_conf")) {
		if (! open(INETDCONFNEW, ">$inetd_conf_new")) {
			Log(0, "Could not create the file $inetd_conf_new: $!\n");
			Log(0, "The \"inetd\" process will not be able to be configured.\n");
			close(INETDCONF);
			return (0);
		}

		$disableprefix = "";
		while (defined($line = <INETDCONF>)) {
			if ($line =~ /^$ftpservicename\b/) {
				$disableprefix = "#DISABLED by install_ncftpd.pl# ";
				$disabled++;
			}
			print INETDCONFNEW $disableprefix, $line;

			# Also disable continuation lines.
			$disableprefix = "" unless ($line =~ /\\$/);
		}
		close(INETDCONF);
		close(INETDCONFNEW);
	}

	if ($disabled) {
		chmod(0644, $inetd_conf);
		unlink($inetd_conf);
		rename($inetd_conf_new, $inetd_conf) or Exit(1, "Could not rename $inetd_conf_new to $inetd_conf: $!\n");
		chmod(0644, $inetd_conf);
		Log(0, "Disabled the \"$ftpservicename\" service in the $inetd_conf file.\n");
		UninstallLog("UncommentOutFTPLineInInetdConf(\"$inetd_conf\");\n");
		return (1);
	} else {
		Log(0, "$inetd_conf does not appear to be handling the \"$ftpservicename\" service.\n");
		unlink($inetd_conf_new);
		return (0);
	}
}	# CommentOutFTPLineInInetdConf




sub CommentOutFTPLineInXinetdConf
{
	my ($inetd_conf) = "/etc/xinetd.conf";
	my ($inetd_conf_new) = "$inetd_conf.new";
	my ($line);
	my ($disabled, $disableprefix);

	return unless (-f $inetd_conf);

	$disabled = 0;
	if (open(INETDCONF, "<$inetd_conf")) {
		if (! open(INETDCONFNEW, ">$inetd_conf_new")) {
			Log(0, "Could not create the file $inetd_conf_new: $!\n");
			Log(0, "The \"xinetd\" process will not be able to be configured.\n");
			close(INETDCONF);
			return (0);
		}

		$disableprefix = "";
		while (defined($line = <INETDCONF>)) {
			if ($line =~ /^[^#]*service\s+$ftpservicename\b/) {
				$disableprefix = "#DISABLED by install_ncftpd.pl# ";
				$disabled++;
			}
			print INETDCONFNEW $disableprefix, $line;

			# Disable rest of block.
			$disableprefix = "" if ($line =~ /^[^#]*\}/);
		}
		close(INETDCONF);
		close(INETDCONFNEW);
	}

	if ($disabled) {
		chmod(0644, $inetd_conf);
		unlink($inetd_conf);
		rename($inetd_conf_new, $inetd_conf) or Exit(1, "Could not rename $inetd_conf_new to $inetd_conf: $!\n");
		chmod(0644, $inetd_conf);
		Log(0, "Disabled the \"$ftpservicename\" service in the $inetd_conf file.\n");
		UninstallLog("UncommentOutFTPLineInXinetdConf(\"$inetd_conf\");\n");
		return (1);
	} else {
		Log(0, "$inetd_conf does not appear to be handling the \"$ftpservicename\" service.\n");
		unlink($inetd_conf_new);
		return (0);
	}
}	# CommentOutFTPLineInXinetdConf




sub CommentOutFTPLineInXinetdD
{
	my ($inetd_d) = "/etc/xinetd.d";
	my ($inetd_conf);
	my ($line);
	my ($disabled, $disableprefix, $tdisabled);

	return unless (-d $inetd_d);

	my (@inetd_d_files) = glob("$inetd_d/*");

	$tdisabled = 0;
	for $inetd_conf (@inetd_d_files) {
		next if (-d $inetd_conf);
		if (open(INETDCONF, "<$inetd_conf")) {
			my ($inetd_conf_new) = "$inetd_conf.new";
			if (! open(INETDCONFNEW, ">$inetd_conf_new")) {
				Log(0, "Could not create the file $inetd_conf_new: $!\n");
				Log(0, "The \"xinetd\" process will not be able to be configured.\n");
				close(INETDCONF);
				return (0);
			}

			$disableprefix = "";
			$disabled = 0;
			while (defined($line = <INETDCONF>)) {
				if ($line =~ /^[^#]*service\s+$ftpservicename\b/) {
					$disableprefix = "#DISABLED by install_ncftpd.pl# ";
					$disabled++;
				}
				print INETDCONFNEW $disableprefix, $line;

				# Disable rest of block.
				$disableprefix = "" if ($line =~ /^[^#]*\}/);
			}
			close(INETDCONF);
			close(INETDCONFNEW);

			if ($disabled) {
				chmod(0644, $inetd_conf);
				unlink($inetd_conf);
				rename($inetd_conf_new, $inetd_conf) or Exit(1, "Could not rename $inetd_conf_new to $inetd_conf: $!\n");
				chmod(0644, $inetd_conf);
				Log(0, "Disabled the \"$ftpservicename\" service in the $inetd_conf file.\n");
				$tdisabled++;
				UninstallLog("UncommentOutFTPLineInXinetdD(\"$inetd_conf\");\n");
			} else {
				unlink($inetd_conf_new);
			}
		}
	}

	if ($tdisabled > 0) {
		return (1);
	} else {
		Log(0, "$inetd_d does not appear to be handling the \"$ftpservicename\" service.\n");
		return (0);
	}
}	# CommentOutFTPLineInXinetdD




sub TellInetdToReloadConfiguration
{
	my (@pids) = PsGrep("inetd", "program", "pids");
	my ($pid) = 0;
	my ($npids) = scalar(@pids);

	if ($npids == 0) {
		Log(1, "Warning: No Inetd process is running?\n");
		return;
	}

	if ($npids > 1) {
		Log(1, "Warning: Multiple Inetd processes detected?\n");
		for $pid (@pids) {
			Log(1, "  PID $pid\n");
		}
	}

	for $pid (@pids) {
		Log(0, "Sending SIGHUP to Inetd running on PID $pid.\n");
		kill HUP => $pid;
	}

	Log(1, "Giving Inetd a few seconds to reload.\n");
	sleep(2);
}	# TellInetdToReloadConfiguration




sub TellXinetdToReloadConfiguration
{
	my (@pids) = PsGrep("xinetd", "program", "pids");
	my ($pid) = 0;
	my ($npids) = scalar(@pids);

	if ($npids == 0) {
		Log(1, "Warning: No Xinetd process is running?\n");
		return;
	}

	if ($npids > 1) {
		Log(1, "Warning: Multiple Xinetd processes detected?\n");
		for $pid (@pids) {
			Log(1, "  PID $pid\n");
		}
	}

	for $pid (@pids) {
		Log(0, "Sending SIGUSR2 to Xinetd running on PID $pid.\n");
		kill USR2 => $pid;
	}

	Log(1, "Giving Xinetd a few seconds to reload.\n");
	sleep(2);
}	# TellXinetdToReloadConfiguration




sub WaitForFTPServerToStart
{
	my ($i);
	my ($n) = @_;
	my ($isRunning) = 0;
	$n = 4 if ((! defined($n)) || ($n < 1));

	for ($i = 1; $i <= $n; $i++) {
		Log(0, "Checking if the FTP service has started.\n");
		$isRunning = IsThereAnFTPServerServing(1);
		last if ($isRunning == 1);
		Log(2, sprintf("Sleeping %d seconds.\n", (3 * $i)));
		sleep(3 * $i) if ($i < $n);
	}
	return ($isRunning);
}	# WaitForFTPServerToStart




sub WaitForFTPServerToExit
{
	my ($i);
	my ($n) = @_;
	my ($isRunning) = 0;
	$n = 4 if ((! defined($n)) || ($n < 1));

	for ($i = 1; $i <= $n; $i++) {
		$isRunning = IsThereAnFTPServerServing(1);
		last if ($isRunning == 0);
		Log(0, "Checking if the FTP service is still active.\n");
		Log(2, sprintf("Sleeping %d seconds.\n", (3 * $i)));
		sleep(3 * $i) if ($i < $n);
	}
	return (! $isRunning);
}	# WaitForFTPServerToExit




sub DisableInetdHandlingOfFTP
{
	if (CommentOutFTPLineInInetdConf()) {
		TellInetdToReloadConfiguration();	
		if (! WaitForFTPServerToExit()) {
			Log(0, "\n");
			Log(0, "ERROR: FTP was disabled in the Inetd configuration file, but the\n"); 
			Log(0, "       FTP service is still accepting connections.  You need to\n");
			Log(0, "       manually shutdown and disable your existing FTP service then\n");
			Log(0, "       restart the installation process.\n");
			Log(0, "\n");
		}
	}
	if ((CommentOutFTPLineInXinetdConf()) || (CommentOutFTPLineInXinetdD())) {
		TellXinetdToReloadConfiguration();	
		if (! WaitForFTPServerToExit()) {
			Log(0, "\n");
			Log(0, "ERROR: FTP was disabled in the Xinetd configuration file, but the\n"); 
			Log(0, "       FTP service is still accepting connections.  You need to\n");
			Log(0, "       manually shutdown and disable your existing FTP service then\n");
			Log(0, "       restart the installation process.\n");
			Log(0, "\n");
		}
	}
}	# DisableInetdHandlingOfFTP




sub KillFTPd
{
	my ($victim) = @_;
	my (@procs) = PsGrep($victim, "program", "pids");
	my (%origprocs);
	my ($proc);
	my ($nproc) = 0;

	return (0) if (scalar(@procs) == 0);
	Log(1, sprintf("Sending TERM signal to %d $victim processes.\n", scalar(@procs)));

	for $proc (@procs) {
		Log(2, "  ") if (($nproc % 10) == 0);
		Log(2, sprintf("%-7d", $proc));
		Log(2, "\n") if (($nproc % 10) == 9);
		$origprocs{$proc} = 1;
		$nproc++;
	}
	Log(2, "\n") if (($nproc % 10) != 9);

	kill TERM => @procs;
	sleep(3);
	my ($i);
	for ($i = 0; $i < 3; $i ++) {
		sleep(2);
		@procs = PsGrep($victim, "program", "pids");
		return (0) if (scalar(@procs) == 0);	# All are dead, done.
	}

	my ($newinstanceprocs) = 0;
	my (@stragglers) = ();
	for $proc (@procs) {
		if (defined($origprocs{$proc})) {
			push(@stragglers, $proc);
		} else {
			$newinstanceprocs++;
		}
	}

	if ($newinstanceprocs > 0) {
		Log(1, "New processes were born after the old ones were killed!\n");
	}
	return ($newinstanceprocs) if (scalar(@stragglers) == 0);

	Log(1, sprintf("Sending KILL signal to %d $victim processes.\n", scalar(@stragglers)));
	$nproc = 0;
	for $proc (@stragglers) {
		Log(2, "  ") if (($nproc % 10) == 0);
		Log(2, sprintf("%-7d", $proc));
		Log(2, "\n") if (($nproc % 10) == 9);
		$nproc++;
	}
	Log(2, "\n") if (($nproc % 10) != 9);
	kill KILL => @stragglers;			# KiLL 'Em ALL
	return ($newinstanceprocs);
}	# KillFTPd




sub NoteExistingNcFTPdConfigurationFromCommandLine
{
	my ($line) = @_;

	if ($line =~ /\s(\/\S*\/general.cf)\b/) {
		my ($cf) = $1;
		if ((-f $cf) && ($existing_general_cf eq "")) {
			$existing_general_cf = $cf;
			Log(1, "Found existing NcFTPd config file: $existing_general_cf.\n");
		}
	}
	if ($line =~ /\s(\/\S*\/domain.cf)\b/) {
		my ($cf) = $1;
		if ((-f $cf) && ($existing_domain_cf eq "")) {
			$existing_domain_cf = $cf;
			Log(1, "Found existing NcFTPd config file: $existing_domain_cf.\n");
		}
	}
	if ($line =~ /(\/\S*\/ncftpd)\s/) {
		my ($n) = $1;
		if ((! -d $n) && (-x _) && ($existing_ncftpd eq "")) {
			$existing_ncftpd = $n;
			Log(1, "Found existing NcFTPd at $existing_ncftpd.\n");
		}
	}
}	# NoteExistingNcFTPdConfigurationFromCommandLine




sub DisableExistingFTPServersRunningFromInittab
{
	if (open(ETCINITTAB, "</etc/inittab")) {
		my (@inittab_lines) = <ETCINITTAB>;
		close(ETCINITTAB);

		# nc:345:respawn:/usr/local/etc/ncftpd/ncftpd.sh
		my ($ncftpd_inittab_entries) = 0;
		my ($ftpd_inittab_entries) = 0;
		my ($ftpd) = "NcFTPd";

		my ($line);
		foreach $line (@inittab_lines) {
			next if ($line =~ /^\s*#/);	# skip comments
			next unless ($line =~ /^\w+:\w*:(\w+):.*/);
			next if ($1 eq "off");		# skip "off" services
			if ($line =~ /ncftpd/) {
				Log(1, "Found NcFTPd entry in the /etc/inittab.\n");
				$ncftpd_inittab_entries++;
				$line = "#DISABLED by install_ncftpd.pl# $line";
				if ($ncftpd_inittab_entries > 1) {
					Log(0, "Multiple existing NcFTPd entries found in /etc/inittab -- all will be disabled.\n");
					next;
				}
				NoteExistingNcFTPdConfigurationFromCommandLine($line);
				$ftpd = "NcFTPd";
			} elsif ($line =~ /ftpd/i) {
				Log(1, "Found other ftpd entry in the /etc/inittab.\n");
				$ftpd_inittab_entries++;
				$line = "#DISABLED by install_ncftpd.pl# $line";
				if ($ftpd_inittab_entries > 1) {
					Log(0, "Multiple existing ftpd entries found in /etc/inittab -- all will be disabled.\n");
					next;
				}
				$ftpd = "ftpd" unless ($ncftpd_inittab_entries > 0);
			}
		}
		if (($ncftpd_inittab_entries > 0) || ($ftpd_inittab_entries > 0)) {
			chmod(0644, "/etc/inittab");
			UninstallLog("UncommentOutFTPLinesInInittab();\n");
			if (open(ETCINITTAB, ">/etc/inittab")) {
				print ETCINITTAB @inittab_lines;
				close(ETCINITTAB);
				chmod(0644, "/etc/inittab");
				Log(0, "Disabled $ftpd in /etc/inittab.\n");

				my ($init) = SearchPath("init", "/sbin:/usr/sbin:/usr/bin:/bin");
				$init = "init" unless ($init);
				Log(1, "Running \"$init q\" to reload modified /etc/inittab.\n");
				system($init, "q");

				# At this point, "init" should send a TERM signal
				# to ncftpd.  Some buggy "init" implementations do not,
				# so wait a bit, then try to manually kill off the
				# processes.
				#
				Log(1, "Waiting for $init to ask $ftpd to shutdown.\n");
				sleep(5);
				if (! WaitForFTPServerToExit(1)) {
					Log(0, "Apparently \"$init\" did not shutdown $ftpd.  We'll try to now.\n");
					# KillFTPd("ncftpd|ftpd|in.ftpd|wu\-?ftpd|pro[\-]?ftpd");
					KillFTPd(".*ftpd");
				}

				if (! WaitForFTPServerToExit()) {
					Log(0, "\n");
					Log(0, "ERROR: $ftpd was disabled in the /etc/inittab, but it is still\n"); 
					Log(0, "       accepting connections.  You need to shutdown NcFTPd, then\n");
					Log(0, "       restart the installation process.\n");
					Log(0, "\n");
					Exit(1);
				}
				return (1);		# Done -- no more NcFTPd processes are running.
			} else {
				Log(0, "Could not rewrite the /etc/inittab file: $!\n");
			}
		} else {
			Log(1, "Did not find NcFTPd or ftpd entries in the /etc/inittab.\n");
		}
	}
	return (0);	# Nothing disabled and killed
}	# DisableExistingFTPServersRunningFromInittab




sub DisableExistingNcFTPd
{
	my ($oCOLUMNS);
	my (@procs);

	#
	# This disables NcFTPd when it is not running from the
	# /etc/inittab.
	#

	$oCOLUMNS = $ENV{"COLUMNS"};
	$ENV{"COLUMNS"} = "132" if ((!defined($oCOLUMNS)) || ($oCOLUMNS < 132));
	@procs = PsGrep("respawn", "program", "long");
	$ENV{"COLUMNS"} = $oCOLUMNS if (defined($oCOLUMNS));

	if (scalar(@procs) > 0) {
		Log(1, "The \"respawn\" program is currently running.\n");
		my ($proc);
		for $proc (@procs) {
			if ($proc =~ /ncftpd/) {
				Log(0, "\n");
				Log(0, "ERROR: You appear to be using the \"respawn\" utility to launch NcFTPd:\n");
				Log(0, "\n       ", $proc, "\n\n");
				Log(0, "       You need to both kill this process *AND* make sure you disable\n");
				Log(0, "       respawn from running when the system boots.  Once that is\n");
				Log(0, "       completed, restart the installation process.\n");
				Exit(1);
			}
		}
	}
	
	$oCOLUMNS = $ENV{"COLUMNS"};
	$ENV{"COLUMNS"} = "132" if ((!defined($oCOLUMNS)) || ($oCOLUMNS < 132));
	@procs = PsGrep("ncftpd", "program", "long");
	$ENV{"COLUMNS"} = $oCOLUMNS if (defined($oCOLUMNS));

	if (scalar(@procs) > 0) {
		Log(0, "NcFTPd is currently running.\n");
		my ($proc);
		for $proc (@procs) {
			NoteExistingNcFTPdConfigurationFromCommandLine($proc);
		}
		Log(0, "Terminating the running instance of NcFTPd.\n");
		if (KillFTPd("ncftpd") > 0) {
			Log(0, "\n");
			Log(0, "ERROR: NcFTPd respawned when it was killed.\n");
			Log(0, "       You need manually shutdown NcFTPd so it no longer respawns,\n");
			Log(0, "       and then restart the installation process.\n");
			Exit(1);
		}

		if (! WaitForFTPServerToExit()) {
			Log(0, "\n");
			Log(0, "ERROR: NcFTPd was terminated, but some other process is still\n");
			Log(0, "       accepting connections.  You need to terminate those processes\n");
			Log(0, "       and prevent them from restarting at boot time.  Once that is\n");
			Log(0, "       completed, restart the installation process.\n");
			Log(0, "\n");
			Exit(1);
		}
	} else {
		Log(0, "NcFTPd is not currently running.\n");
	}
}	# DisableExistingNcFTPd




sub CheckIfSomeOtherFTPServerIsStillRunning
{
	if (IsThereAnFTPServerServing(1)) {
		Log(0, "\n");
		Log(0, "ERROR: An unknown process is still accepting FTP connections.\n"); 
		Log(0, "       You need to terminate those processes *and* prevent them\n");
		Log(0, "       from restarting at boot time.  Once that is completed,\n");
		Log(0, "       restart the installation process.\n");
		Log(0, "\n");
		Exit(1);
	}
}	# CheckIfSomeOtherFTPServerIsStillRunning




sub DisableRcDir
{
	my ($rc_dir) = @_;

	my (@rc_scripts) = glob("$rc_dir/[SKIP]*");
	return if (scalar(@rc_scripts) < 1);

	my ($rc_script, $rc_file, $script_type, $disabled_rc_script_path, $i);
	for $rc_script (@rc_scripts) {
		next if (-d $rc_script);
		$rc_file = $rc_script;
		$rc_file =~ s/^.*\///;
		if ($rc_file =~ /(ftp|ftpd)\b/i) {
			#
			# This appears to be an rc script that
			# starts up NcFTPd or some other ftpd.
			# Disable it by renaming it with a prefix
			# that "rc" won't look for.
			#
			$script_type = "?";
			$script_type = "startup" if (substr($rc_file, 0, 1) eq "S");
			$script_type = "shutdown" if (substr($rc_file, 0, 1) eq "K");
			Log(0, "Disabling system $script_type script $rc_script.\n");
			$i = 0;
			for ($i = 1; $i < 1000; $i++) {
				$disabled_rc_script_path = $rc_script;
				$disabled_rc_script_path =~ s/\/[^\/]+$//;
				$disabled_rc_script_path .= "/#DISABLED#_$rc_file";
				$disabled_rc_script_path .= ".$i" if ($i > 1);
				last if (! -f $disabled_rc_script_path);
			}
			if (rename($rc_script, $disabled_rc_script_path)) {
				Log(1, "Renamed $rc_script to $disabled_rc_script_path.\n");
				UninstallLog("Rename(\"$disabled_rc_script_path\", \"$rc_script\");\n");
			} else {
				Log(0, "Could not rename $rc_script to $disabled_rc_script_path: $!\n");
			}
		}
	}
}	# DisableRcDir




sub DisableRcScripts
{
	my (@rc_dirs) = (
		"rc0.d",
		"rc1.d",
		"rc2.d",
		"rc3.d",
		"rc4.d",
		"rc5.d",
		"rcS.d",
		"rc6.d",
	);

	my (@rc_parent_dirs) = (
		"/etc",			# Solaris
		"/etc/rc.d",		# Red Hat
		"/etc/init.d",		# ??
		"/sbin",		# HP-UX
		"/sbin/rc.d",		# ??
		"/sbin/init.d",		# SuSE
	);

	my ($rc_parent_dir, $rc_dir, $rc_dirpath);
	for $rc_parent_dir (@rc_parent_dirs) {
		for $rc_dir (@rc_dirs) {
			$rc_dirpath = "$rc_parent_dir/$rc_dir";
			if (-d $rc_dirpath) {
				DisableRcDir($rc_dirpath);
			}
		}
	}
}	# DisableRcScripts




sub NoteExistingNcFTPdVersion
{
	my ($cf, $cfdir, $n);

	if ($existing_ncftpd eq "") {
		$cf = "";
		$cf = $existing_general_cf if ($existing_general_cf ne "");
		$cf = $existing_domain_cf if ($existing_domain_cf ne "");
		if ($cf ne "") {
			$cfdir = $cf;
			$cfdir =~ s/\/[^\/]+$//;
			$n = "$cfdir/ncftpd";
			if (-x $n) {
				$existing_ncftpd = $n;
			}
		}
		if ($existing_ncftpd eq "") {
			if (-x $new_ncftpd) {
				$existing_ncftpd = $new_ncftpd;
			} else {
				$n = SearchPath("ncftpd", "", ".::$extra_paths");
				$existing_ncftpd = $n if ($n ne "");
			}
		}
	}

	if (($existing_general_cf eq "") && (-f $new_general_cf)) {
		$existing_general_cf = $new_general_cf;
		Log(1, "Found existing NcFTPd config file: $existing_general_cf.\n");
	}

	if (($existing_domain_cf eq "") && (-f $new_domain_cf)) {
		$existing_domain_cf = $new_domain_cf;
		Log(1, "Found existing NcFTPd config file: $existing_domain_cf.\n");
	}

	if (($existing_general_cf ne "") && ($existing_domain_cf eq "")) {
		$cf = $existing_general_cf;
		$cfdir = $cf;
		$cfdir =~ s/\/[^\/]+$//;
		$cf = "$cfdir/domain.cf";
		if (-f $cf) {
			$existing_domain_cf = $cf;
			Log(1, "Found existing NcFTPd config file: $existing_domain_cf.\n");
		}
	} elsif (($existing_general_cf eq "") && ($existing_domain_cf ne "")) {
		$cf = $existing_domain_cf;
		$cfdir = $cf;
		$cfdir =~ s/\/[^\/]+$//;
		$cf = "$cfdir/general.cf";
		if (-f $cf) {
			$existing_general_cf = $cf;
			Log(1, "Found existing NcFTPd config file: $existing_general_cf.\n");
		}
	} elsif (($existing_general_cf eq "") && ($existing_domain_cf eq "") && ($existing_ncftpd ne "")) {
		$cf = $existing_ncftpd;
		$cfdir = $cf;
		$cfdir =~ s/\/[^\/]+$//;
		$cf = "$cfdir/general.cf";
		if (-f $cf) {
			$existing_general_cf = $cf;
			Log(1, "Found existing NcFTPd config file: $existing_general_cf.\n");
		}
		$cf = $existing_ncftpd;
		$cfdir = $cf;
		$cfdir =~ s/\/[^\/]+$//;
		$cf = "$cfdir/domain.cf";
		if (-f $cf) {
			$existing_domain_cf = $cf;
			Log(1, "Found existing NcFTPd config file: $existing_domain_cf.\n");
		}
	}

	if ($existing_ncftpd eq "") {
		$cf = "";
		$cf = $existing_general_cf if ($existing_general_cf ne "");
		$cf = $existing_domain_cf if ($existing_domain_cf ne "");
		if ($cf ne "") {
			$cfdir = $cf;
			$cfdir =~ s/\/[^\/]+$//;
			$n = "$cfdir/ncftpd";
			if (-x $n) {
				$existing_ncftpd = $n;
			}
		}
	}

	if ($existing_ncftpd ne "") {
		my ($ver) = `$existing_ncftpd -b 2>&1`;
		if ($ver =~ /([1234]\.\d+\.\d+)/) {
			$ver = $1;
			$existing_version = $ver;
		} else {
			$ver = `strings $existing_ncftpd 2>/dev/null | fgrep '@(#) NcFTPd '`;
			if ($ver =~ /([1234]\.\d+\.\d+)/) {
				$ver = $1;
				$existing_version = $ver;
			} else {
				$ver = "unknown";
			}
		}
		Log(0, "Found existing NcFTPd (version $ver) at $existing_ncftpd.\n");
	}
}	# NoteExistingNcFTPdVersion




sub LoadExistingConfigs
{
	my ($line, $var, $val);

	if ($existing_general_cf ne "") {
		if (open(CF, "<$existing_general_cf")) {
			Log(1, "Loading existing settings from $existing_general_cf.\n");
			while (defined($line = <CF>)) {
				chomp($line);
				next unless ($line =~ /^([A-Za-z0-9_\-]+)\s*\=\s*([^\n\r]*)/);
				$var = $1;
				$val = $2;
				Log(2, "  general.cf: [$var] = [$val]\n");
			}
			close(CF);
		}
	}

	if ($existing_domain_cf ne "") {
		if (open(CF, "<$existing_domain_cf")) {
			Log(1, "Loading existing settings from $existing_domain_cf.\n");
			while (defined($line = <CF>)) {
				chomp($line);
				next unless ($line =~ /^([A-Za-z0-9_\-]+)\s*\=\s*([^\n\r]*)/);
				$var = $1;
				$val = $2;
				Log(2, "  domain.cf: [$var] = [$val]\n");
			}
			close(CF);
		}
	}
}	# LoadExistingConfigs




sub SafeRename
{
	my ($rnfm, $rnto) = @_;
	return ("") if (! -f $rnfm);

	$rnto = "$rnfm.orig" if (! defined($rnto));
	my ($ornto) = $rnto;
	my ($i);
	for ($i = 2; $i < 1000; $i++) {
		last if (! -f $rnto);
		$rnto = $ornto . "-$i";
	}
	if (rename($rnfm, $rnto)) {
		Log(1, "Renamed $rnfm to $rnto.\n");
		UninstallLog("Rename(\"$rnto\", \"$rnfm\");\n");
		return ($rnto);
	} else {
		Log(0, "Could not rename $rnfm to $rnto: $!\n");
		return ("");
	}
}	# SafeRename




sub SafeRenameWithNcVersion
{
	my ($rnfm, $rnto) = @_;
	$rnto = "$rnfm-$existing_version" if ((! defined($rnto)) && ($existing_version ne ""));
	return (SafeRename($rnfm, $rnto));
}	# SafeRenameWithNcVersion




sub LookupWebUser
{
	my (@webusers) = ("www", "httpd", "http", "apache", "web");
	my ($xwebuser, $xwebgroup);
	my ($name,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell,$expire);

	Log(1, "Checking system user database for a web server username.\n");
	for $xwebuser (@webusers) {
		($name,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell,$expire) = 
			getpwnam($xwebuser);
		if (defined($name) && defined($uid) && defined($gid)) {
			$webuser = $xwebuser;
			$webuid = $uid;
			$webgid = $gid;
			$webgroup = "$gid";
			$xwebgroup = getgrgid($webgid);
			$webgroup = $xwebgroup if (defined($xwebgroup));
			Log(1, "Found web server user \"$webuser\" (UID $webuid), group \"$webgroup\" (GID $webgid) in system user database.\n");
			last;
		}
	}
}	# LookupWebUser




sub MkInstallDirs
{
	Mkdirs($bindir, 00755);
	Mkdirs($sbindir, 00755);
	Mkdirs($sysconfdir, 00755);
	Mkdirs($runtimedatadir, 00755);
	Mkdirs($logdir, 00755);

	# We don't use these yet...
	# Mkdirs($datadir, 00755);
	# Mkdirs($libdir, 00755);
	# Mkdirs($includedir, 00755);
	# Mkdirs($mandir, 00755);

	if ((defined($webgid)) && ($webgid != (-1))) {
		Mkdirs($confdir, 00750, 0, $webgid);
		Mkdirs($rptbindir, 00755, 0, $webgid);
		Mkdirs("$logdir/ncftpd", 00750, $webuid, $webgid);
	} else {
		Mkdirs($confdir, 00755);
		Mkdirs($rptbindir, 00755, 0, 0);
		Mkdirs("$logdir/ncftpd", 00750);
	}
}	# MkInstallDirs



sub FixScript
{
	my ($path, $headerline) = @_;
	my ($line);

	my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = 	
		stat($path);
	return 0 unless (defined($mtime));

	if (open(SRC, "<$path")) {
		$line = <SRC>;
		if (! open(DST, ">$path.new")) {
			close(SRC);
			return 0;
		} else {
			print DST $headerline, "\n";
			while (defined($line = <SRC>)) {
				print DST $line;
			}
			close(DST);
			close(SRC);
			return 0 unless rename("$path.new", $path);
			chmod($mode, $path);
			utime($atime, $mtime, $path);
			return 1;
		}
	}
	return 0;
}	# FixScript




sub InstallDefaultConfigs
{
	my (@cfdata);
	my ($line, $var, $val, $oval, $ovar);

	Log(0, "Installing new NcFTPd config files.\n");

	if (! open(CF, "<$install_src_root/conf/general.cf-dist")) {
		Exit(1, "Could not open: $install_src_root/conf/general.cf-dist: $!\n");
	}
	@cfdata = <CF>;
	close(CF);

	foreach $line (@cfdata) {
		if ($line =~ /^REMOVE-THIS-LINE/) {
			$line = "# (removed by install_ncftpd.pl)\n";
			next;
		}
		next unless ($line =~ /^((#\s*)?[A-Za-z0-9_\-]+)\s*\=\s*([^\n\r]*)/);
		$var = $1;
		$val = $3;
		$oval = $val;
		$ovar = $var;
		$val =~ s-/var/log-$logdir-g;
		$val =~ s-/var/run-$runtimedatadir-g;
		$val =~ s-/home/ftp-$ftphome-g;
		$val =~ s-/usr/local/etc-$sysconfdir-g;
		$val =~ s-/usr/local/bin-$bindir-g;
		$val =~ s-/usr/local/sbin-$sbindir-g;
		$val =~ s-/usr/local-$prefix-g;
		$val = $ftpport if ($var =~ /#?port/);
		if (($var =~ /#?log-owner/) && ($webuser ne "")) {
			$var = "log-owner";
			$val = $webuser;
		}
		if (($var =~ /#?log-group/) && ($webgroup ne "")) {
			$var = "log-group";
			$val = $webgroup;
		}
		if (($var =~ /#?log-umask/) && ($webuser ne "")) {
			$var = "log-umask";
			$val = "027";
		}
		if (($oval ne $val) || ($ovar ne $var)) {
			$line = "$var=$val\n";
			Log(2, "  set general.cf: $var=$val\n");
		}
	}

	if (! open(CF, ">$new_general_cf")) {
		Exit(1, "Could not create: $new_general_cf: $!\n");
	}
	print CF @cfdata;
	close(CF);
	Log(0, "Created $new_general_cf.\n");
	UninstallLog("Remove(\"$new_general_cf\");\n");

	#
	# Domain.cf
	#

	my ($addr, $myhostname, $myipaddr);
	my ($additionalsets) = 0;
	$myipaddr = "127.0.0.1";
	$myhostname = "localhost";
	Log(0, "Looking up IP address and hostname for this machine.\n");
	Log(2, "Running \"$install_src_root/ncftpd -e\".\n");
	$myhostname = `$install_src_root/ncftpd -e 2>/dev/null`;
	chomp($myhostname);
	Log(2, "  result = [$myhostname]\n");
	$addr = gethostbyname($myhostname);
	$myipaddr = inet_ntoa($addr) if (defined($addr));
	Log(2, "  ipaddr = [$myipaddr]\n");

	if (! open(CF, "<$install_src_root/conf/domain.cf-dist")) {
		Exit(1, "Could not open: $install_src_root/conf/domain.cf-dist: $!\n");
	}
	@cfdata = <CF>;
	close(CF);

	foreach $line (@cfdata) {
		if ($line =~ /^REMOVE-THIS-LINE/) {
			$line = "# (removed by install_ncftpd.pl)\n";
			next;
		}
		$additionalsets++ if ($line =~ /^#\ end\ of\ first/);
		next unless ($line =~ /^((#\s*)?[A-Za-z0-9_\-]+)\s*\=\s*([^\n\r]*)/);
		$var = $1;
		$val = $3;
		$oval = $val;
		$ovar = $var;
		$val =~ s-/var/log-$logdir-g;
		$val =~ s-/var/run-$runtimedatadir-g;
		$val =~ s-/home/ftp-$ftphome-g;
		$val =~ s-/usr/local/etc-$sysconfdir-g;
		$val =~ s-/usr/local/bin-$bindir-g;
		$val =~ s-/usr/local/sbin-$sbindir-g;
		$val =~ s-/usr/local-$prefix-g;
		$val = $ftphome if ($var =~ /#?ftp-home/);
		if ($additionalsets == 0) {
			$val = $myhostname if ($var =~ /#?set-name/);
			$val = $myhostname if ($var =~ /#?server-name/);
			$val = $myipaddr if ($var =~ /#?server-address/);
		}
		if (($oval ne $val) || ($ovar ne $var)) {
			$line = "$var=$val\n";
			Log(2, "  set domain.cf: $var=$val\n");
		}
	}

	if (! open(CF, ">$new_domain_cf")) {
		Exit(1, "Could not create: $new_domain_cf: $!\n");
	}
	print CF @cfdata;
	close(CF);
	Log(0, "Created $new_domain_cf.\n");
	UninstallLog("Remove(\"$new_domain_cf\");\n");
}	# InstallDefaultConfigs




sub InstallFile
{
	my ($srcfile, $dstdir, $dstmode, $dstuid, $dstgid) = @_;
	my ($srcpath, $dstpath, $dstfile);

	$dstfile = $srcfile;
	$dstfile =~ s-^.*/--;
	$srcpath = "$install_src_root/$srcfile";
	$dstpath = "$dstdir/$dstfile";

	my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = 	
		stat($srcpath);
	if (! defined($mode)) {
		Exit(1, "ERROR: Could not stat $srcpath: $!\n");
		return (0);
	}

	$dstmode = $mode unless defined($dstmode);
	$dstuid = $uid unless defined($dstuid);
	$dstgid = $gid unless defined($dstgid);

	SafeRenameWithNcVersion($dstpath);
	if (! copy($srcpath, $dstpath)) {
		Exit(1, "ERROR: Could not copy $srcpath to $dstpath: $!\n");
		return (0);
	}
	chmod($dstmode, $dstpath);
	chown($dstuid, $dstgid, $dstpath);
	utime($atime, $mtime, $dstpath);

	UninstallLog("Remove(\"$dstpath\");\n");
	Log(1, "Installed $dstpath.\n");
	return (1);
}	# InstallFile




sub Symlink
{
	my ($srcpath, $dstpath, $force) = @_;

	if ((-l $dstpath) || (-e $dstpath)) {
		if ($force eq "overwrite") {
			if (! unlink($dstpath)) {
				Log(1, "Could not remove existing $dstpath: $!\n");
				# But keep going
			}
		} elsif ($force eq "rename") {
			return 0 if (SafeRenameWithNcVersion($dstpath) eq "");
		} else {
			Log(1, "Warning: could not link $srcpath -> $dstpath: $dstpath already exists\n");
			return 0;
		}
	}
	if (! symlink($srcpath, $dstpath)) {
		Log(0, "Warning: could not link $srcpath -> $dstpath: $!\n");
		return 0;
	} else {
		Log(1, "  Linked $srcpath -> $dstpath.\n");
		UninstallLog("Remove(\"$dstpath\");\n");
	}
	return 1;
}	# Symlink



sub InstallConfigs
{
	my ($oldg, $oldd);

	Log(0, "Installing configuration files.\n");
	$oldg = SafeRenameWithNcVersion($new_general_cf);
	$oldd = SafeRenameWithNcVersion($new_domain_cf);
	if (($oldg ne "") && ($oldd ne "") && (! $create_new_configs)) {
		if (! copy($oldg, $new_general_cf)) {
			Log(0, "ERROR: Could not copy $oldg to $new_general_cf: $!\n");
		} else {
			UninstallLog("Rename(\"$oldg\", \"$new_general_cf\");\n");
		}
		if ($webgid != (-1)) {
			chmod(0640, $new_general_cf);
			chown(0, $webgid, $new_general_cf);
		} else {
			chmod(0600, $new_general_cf);
			chown(0, 0, $new_general_cf);
		}

		if (! copy($oldd, $new_domain_cf)) {
			Log(0, "ERROR: Could not copy $oldd to $new_domain_cf: $!\n");
		} else {
			UninstallLog("Rename(\"$oldd\", \"$new_domain_cf\");\n");
		}
		if ($webgid != (-1)) {
			chmod(0640, $new_domain_cf);
			chown(0, $webgid, $new_domain_cf);
		} else {
			chmod(0600, $new_domain_cf);
			chown(0, 0, $new_domain_cf);
		}
		if ((-f $new_general_cf) && (-f $new_domain_cf)) {
			Log(1, "Using copies of existing NcFTPd config files.\n");
			return;
		}
		Log(0, "Existing NcFTPd config files could not be copied.\n");
	}

	InstallDefaultConfigs();
}	# InstallConfigs




sub InstallPrograms
{
	my ($binuid, $bingid);
	my ($name,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell,$expire);

	$binuid = 0;
	$bingid = 0;

	($name,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell,$expire) = 
		getpwnam("bin");
	if (defined($uid)) {
		$binuid = $uid;
		$bingid = $gid;
	}

	Log(0, "Installing programs.\n");
	InstallFile("ncftpd", $sbindir, 0751, $binuid, $bingid);
	InstallFile("ncftpd_spy", $sbindir, 0700, $binuid, $bingid);
	InstallFile("ncftpd_passwd", $sbindir, 0751, $binuid, $bingid);
	InstallFile("ncftpd_edquota", $sbindir, 0751, $binuid, $bingid);
	InstallFile("ncftpd_repquota", $sbindir, 0751, $binuid, $bingid);
	InstallFile("extra/pgrep.pl", $bindir, 0755, $binuid, $bingid);
	Symlink("$bindir/pgrep.pl", "$bindir/pkill.pl", "overwrite");
	InstallFile("extra/uninstall_ncftpd.pl", $confdir, 0700, 0, $bingid);
	$uninstaller = "$confdir/uninstall_ncftpd.pl";
}	# InstallPrograms




sub InstallStartupScript
{
	my (@initscript);
	my (@inittabscript);
	my (@startupitemscript);
	my ($initscriptpath);
	my ($inittabscriptpath);
	my ($inittabscript_redirect);
	my ($runlevels);
	my ($rc_local);
	my ($rc_local_already);
	my ($line, $nlines);
	my ($rc_link, @rc_links);
	my ($var, $val, %vars, $newval);

	Log(0, "Configuring your system so NcFTPd starts automatically at system boot.\n");
	if (! open(SCRIPT, "<$install_src_root/extra/ncftpd.init")) {
		Exit(1, "Could not open $install_src_root/extra/ncftpd.init: $!\n");
	}
	@initscript = <SCRIPT>;
	close(SCRIPT);

	if (! open(SCRIPT, "<$install_src_root/extra/ncftpd.sh")) {
		Exit(1, "Could not open $install_src_root/extra/ncftpd.sh: $!\n");
	}
	@inittabscript = <SCRIPT>;
	close(SCRIPT);

	%vars = ();
	$vars{"NCFTPD_PREFIX"} = $prefix;
	$vars{"NCFTPD_GENERAL_CF"} = $new_general_cf;
	$vars{"NCFTPD_DOMAIN_CF"} = $new_domain_cf;
	$vars{"NCFTPD_STARTUP_ERROR_LOG"} = $startup_error_log;
	$vars{"NCFTPD_PROG_PATH"} = "$sbindir:$bindir:\$PATH";
	$vars{"NCFTPD_PROG"} = "$new_ncftpd";
	if ($run_ncftpd_verbose > 0) {
		$val = "-v";
		$val = "-vv" if ($run_ncftpd_verbose == 2);
		$val = "-vvv" if ($run_ncftpd_verbose == 3);
		$vars{"NCFTPD_VERBOSITY"} = "$val";
	}

	$nlines = 0;
	foreach $line (@initscript) {
		$nlines++;
		if (($nlines == 1) && (-f "/sbin/chkconfig")) {
			$line .= "#\n";
			$line .= "# chkconfig: 345 85 15\n";
			$line .= "# description: Starts and stops NcFTPd (FTP) service\n";
			$line .= "# processname: ncftpd\n";
			$line .= "# config: $new_general_cf\n";
			$line .= "# config: $new_domain_cf\n";
			$line .= "#\n";
		}
		for $var (keys %vars) {
			if ($line =~ /^\s*$var\=\"?([^\"]*)\"?/) {
				$val = $1;
				$newval = $vars{$var};
				if ($newval ne $val) {
					chomp($line);
					$line = "#Configured by install_ncftpd.pl# $line\n";
					$line .= "$var=\"$newval\"\n";
				}
			}
		}
		if ($line =~ /#SOURCE\-RC/) {
			if ($SYS eq "hpux") {
				$line = "PATH=/usr/sbin:/sbin:/bin:/usr/bin\nexport PATH\n";
			} elsif ($SYS eq "linux") {
				if ($linux_distro eq "SuSE") {
					$line = ". /etc/rc.config\n";
				} else {
					$line = "";
					if (-f "/etc/sysconfig/network") {
						if (-f "/etc/init.d/functions") {
							$line .= "# Source function library.\n";
							$line .= ". /etc/init.d/functions\n";
							$line .= "\n";
						} elsif (-f "/etc/rc.d/init.d/functions") {
							$line .= "# Source function library.\n";
							$line .= ". /etc/rc.d/init.d/functions\n";
							$line .= "\n";
						}
						if (-f "/etc/sysconfig/network") {
							$line .= "# Get config.\n";
							$line .= ". /etc/sysconfig/network\n";
							$line .= "\n";
							$line .= "# Check that networking is up.\n";
							$line .= "if [ \"X\${NETWORKING}\" = \"Xno\" ]\n";
							$line .= "then\n";
							$line .= "	exit 0\n";
							$line .= "fi\n";
							$line .= "\n";
						}
					}
				}
			} else {
				$line = "";
			}
		} elsif ($line =~ /^PKILL\=/) {
			$line = "PKILL=\"${bindir}/pkill.pl\"\n";
		} elsif ($line =~ /^PGREP\=/) {
			$line = "PGREP=\"${bindir}/pgrep.pl\"\n";
		} elsif ($line =~ /ECHO\-N/) {
			chomp($line);
			$line =~ s/\s*#\s*ECHO.*$//;
			if ($SYS =~ /(aix|hpux|solaris)/) {
				$line =~ s/echo\s\-n/echo/;
				$line .= "\'\\c\'";
			}
			$line .= "\n";
		} elsif ($line =~ /ECHO\-E/) {
			chomp($line);
			$line =~ s/\s*#\s*ECHO.*$//;
			unless ($SYS eq "linux") {
				$line =~ s/echo\s\-e/echo/;
			}
			$line .= "\n";
		} elsif ($line =~ /#\s*Successfully\s*backgrounded/) {
			if ($SYS eq "hpux") {
				$line .= "\t\t\tes=4	# HP-UX rc script exit status for backgrounding\n";
			}
		} elsif ($line =~ /^#EXTRA/) {
			$line = "";
			if ($SYS eq "hpux") {
				$line .= "\n";
				$line .= "\'start_msg\')\n";
				$line .= "	echo \'Start NcFTPd\'\n";
				$line .= "	es=0\n";
				$line .= "	;;\n";
				$line .= "\n";
				$line .= "\'stop_msg\')\n";
				$line .= "	echo \'Stopping NcFTPd\'\n";
				$line .= "	es=0\n";
				$line .= "	;;\n";
			}
		}
	}

	foreach $line (@inittabscript) {
		for $var (keys %vars) {
			if ($line =~ /^\s*$var\=\"?([^\"]*)\"?/) {
				$val = $1;
				$newval = $vars{$var};
				if ($newval ne $val) {
					chomp($line);
					$line = "#Configured by install_ncftpd.pl# $line\n";
					$line .= "$var=\"$newval\"\n";
				}
			}
		}
	}

	@rc_links = ();
	$initscriptpath = "";
	$inittabscriptpath = "";
	$rc_local = "";
	$rc_local_already = 0;
	$runlevels = "2345";
	$inittabscript_redirect = "";

	if ($SYS eq "linux") {
		if ($linux_distro eq "SuSE") {
			if (-d "/etc/init.d") {
				if (-d "/etc/rc2.d") {
					# ??
					@rc_links = (
						"/etc/rc2.d/S21ncftpd",
						"/etc/rc2.d/K21ncftpd",
						"/etc/rc3.d/S21ncftpd",
						"/etc/rc3.d/K21ncftpd",
					);
				} else {
					# SuSE 7.x
					@rc_links = (
						"/etc/init.d/rc2.d/S21ncftpd",
						"/etc/init.d/rc2.d/K21ncftpd",
						"/etc/init.d/rc3.d/S21ncftpd",
						"/etc/init.d/rc3.d/K21ncftpd",
					);
				}
				$initscriptpath = "/etc/init.d/ncftpd";
			} else {
				# SuSE 6.x
				@rc_links = (
					"/sbin/init.d/rc2.d/S21ncftpd",
					"/sbin/init.d/rc2.d/K21ncftpd",
					"/sbin/init.d/rc3.d/S21ncftpd",
					"/sbin/init.d/rc3.d/K21ncftpd",
				);
				$initscriptpath = "/sbin/init.d/ncftpd";
			}
		} elsif ($linux_distro =~ /(Red\ Hat|Mandrake|Conectiva|TurboLinux|Caldera|Cobalt|LinuxPPC)/) {

			if (-d "/etc/rc2.d") {
				@rc_links = (
					"/etc/rc2.d/S85ncftpd",
					"/etc/rc3.d/S85ncftpd",
					"/etc/rc5.d/S85ncftpd",
				);
			} else {
				@rc_links = (
					"/etc/rc.d/rc2.d/S85ncftpd",
					"/etc/rc.d/rc3.d/S85ncftpd",
					"/etc/rc.d/rc5.d/S85ncftpd",
				);
			}
			if ((-d "/etc/init.d") && (! -d "/etc/rc.d/init.d")) {
				$initscriptpath = "/etc/init.d/ncftpd";
			} else {
				$initscriptpath = "/etc/rc.d/init.d/ncftpd";
			}
		} elsif ($linux_distro eq "Slackware") {
			$initscriptpath = "/etc/rc.d/rc.ncftpd";
			$rc_local = "/etc/rc.d/rc.local";
		} elsif ($linux_distro eq "Debian") {
			@rc_links = (
				"/etc/rc0.d/K21ncftpd",
				"/etc/rc1.d/K21ncftpd",
				"/etc/rc2.d/S21ncftpd",
				"/etc/rc3.d/S21ncftpd",
				"/etc/rc4.d/S21ncftpd",
				"/etc/rc5.d/S21ncftpd",
				"/etc/rc6.d/K21ncftpd",
				"/etc/rcS.d/K21ncftpd",
			);
			$initscriptpath = "/etc/init.d/ncftpd";
		} else {
			Log(0, "This script has not been customized for your Linux distribution.\n");
			if (-f "/etc/inittab") {
				$inittabscriptpath = "$confdir/ncftpd.inittab.sh";
				Log(0, "  NcFTPd will be installed into the /etc/inittab, rather than use an rc script,\n");
				Log(0, "  which should work OK.\n");
			} else {
				$initscriptpath = "$confdir/ncftpd.init";
				Log(0, "  You will need to manually install a startup script so NcFTPd will be\n");
				Log(0, "  automatically started at boot time.  A sample startup script will be\n");
				Log(0, "  installed as $initscriptpath.\n");
				Log(0, "  If your system uses rc.d directories, you can simply create symbolic links\n");
				Log(0, "  to $initscriptpath, otherwise you can\n");
				Log(0, "  check to see if there is an \"rc.local\" script you can use to run\n");
				Log(0, "  $initscriptpath.\n");
			}
		}
	} elsif ($SYS eq "freebsd") {
		if (-d "/usr/local/etc/rc.d") {
			$initscriptpath = "/usr/local/etc/rc.d/ncftpd.sh";
		} else {
			$initscriptpath = "/etc/rc.ncftpd";
			$rc_local = "/etc/rc.local";
		}
	} elsif ($SYS eq "macosx") {
		#
		# Special setup for Mac OS X.
		# OS X uses a non-traditional way to run startup scripts.
		#
		# We simply choose to extract a pre-built tar which creates
		# the startup scripts in /System/Library/StartupItems.
		#
		if ($verbose > 1) {
			Log(1, "/usr/bin/tar -x -C / -vpf \"$install_src_root/extra/osx_supplement.tar\"\n");
			system("/usr/bin/tar", "-x", "-C", "/", "-vpf", "$install_src_root/extra/osx_supplement.tar");
		} else {
			system("/usr/bin/tar", "-x", "-C", "/", "-pf", "$install_src_root/extra/osx_supplement.tar");
		}

		if (! open(SCRIPT, "</System/Library/StartupItems/NcFTPd/NcFTPd")) {
			Exit(1, "Could not open /System/Library/StartupItems/NcFTPd/NcFTPd: $!\n");
		}
		@startupitemscript = <SCRIPT>;
		close(SCRIPT);
		UninstallLog("Remove(\"/System/Library/StartupItems/NcFTPd/NcFTPd\");\n");
		UninstallLog("Remove(\"/System/Library/StartupItems/NcFTPd/StartupParameters.plist\");\n");
		UninstallLog("Rmdir(\"/System/Library/StartupItems/NcFTPd\");\n");

		foreach $line (@startupitemscript) {
			for $var (keys %vars) {
				if ($line =~ /^\s*$var\=\"?([^\"]*)\"?/) {
					$val = $1;
					$newval = $vars{$var};
					if ($newval ne $val) {
						chomp($line);
						$line = "    $var=\"$newval\"\t# from install_ncftpd.pl\n";
					}
				}
			}
		}

		if (! open(SCRIPT, ">/System/Library/StartupItems/NcFTPd/NcFTPd")) {
			Exit(1, "Could not rewrite /System/Library/StartupItems/NcFTPd/NcFTPd: $!\n");
		}
		print SCRIPT @startupitemscript;
		close(SCRIPT);

		#
		# But copy the standard init script to a suitable location,
		# so they can use it to restart NcFTPd.
		#
		$initscriptpath = "$sbindir/ncftpd.init";
	} elsif ($SYS eq "sco") {
		@rc_links = (
			"/etc/rc2.d/S92ncftpd",
		);
		$initscriptpath = "/etc/init.d/ncftpd";
	} elsif (($SYS eq "unixware") || ($SYS eq "openunix")) {
		@rc_links = (
			"/etc/rc2.d/S75ncftpd",
		);
		$initscriptpath = "/etc/init.d/ncftpd";
	} elsif ($SYS eq "aix") {
		$inittabscriptpath = "/etc/rc.ncftpd";
		$inittabscript_redirect = ' >/dev/console 2>&1';
		$runlevels = "2";
	} elsif ($SYS eq "solaris") {
		@rc_links = (
			"/etc/rc2.d/S75ncftpd",
		);
		$initscriptpath = "/etc/init.d/ncftpd";
	} elsif ($SYS =~ /^irix/) {
		@rc_links = (
			"/etc/rc2.d/S65ncftpd",
		);
		$initscriptpath = "/etc/init.d/ncftpd";
	} elsif ($SYS eq "tru64unix") {
		@rc_links = (
			"/sbin/rc0.d/K62ncftpd",
			"/sbin/rc2.d/K62ncftpd",
			"/sbin/rc3.d/S62ncftpd",
		);
		$initscriptpath = "/sbin/init.d/ncftpd";
	} elsif ($SYS =~ /(openbsd|netbsd|bsdos|sunos)/) {
		$initscriptpath = "/etc/rc.ncftpd";
		$rc_local = "/etc/rc.local";
	} elsif ($SYS eq "hpux") {
		@rc_links = (
			"/sbin/rc1.d/K517ncftpd",
			"/sbin/rc2.d/S517ncftpd",
		);
		$initscriptpath = "/sbin/init.d/ncftpd";
	} elsif (-f "/etc/inittab") {
		$inittab_preferred = 1;
		Log(0, "Warning: Unknown system type ($SYS)!  Startup script was not installed.\n");
		Log(0, "         NcFTPd will be run from the /etc/inittab instead.\n");
	} elsif (-f "/etc/rc.local") {
		$initscriptpath = "/etc/rc.ncftpd";
		$rc_local = "/etc/rc.local";
		Log(0, "Warning: Unknown system type ($SYS)!  Startup script was not installed.\n");
		Log(0, "         NcFTPd will be run from the /etc/rc.local instead.\n");
	} else {
		Log(0, "ERROR: Unknown system type ($SYS)!  Startup script was not installed.\n");
		Log(0, "       You will need to start NcFTPd manually and arrange for it to be\n");
		Log(0, "       started when the system boots.\n");
		return;
	}

	if ($inittab_preferred) {
		$inittabscriptpath = "$confdir/ncftpd.inittab.sh";
		if ($initscriptpath ne "") {
			Log(1, "Using $inittabscriptpath instead of $initscriptpath.\n");
		}
	}

	if ($inittabscriptpath ne "") {
		SafeRenameWithNcVersion($inittabscriptpath);
		if (! open(SCRIPT, ">$inittabscriptpath")) {
			Exit(1, "Could not create $inittabscriptpath: $!\n");
		} else {
			print SCRIPT @inittabscript;
			close(SCRIPT);
		}
		chmod(0755, $inittabscriptpath);
		Log(0, "Installed $inittabscriptpath.\n");
		UninstallLog("Remove(\"$inittabscriptpath\");\n");

		unlink("$sbindir/restart_ncftpd");
		if (open(RESTARTSCRIPT, ">$sbindir/restart_ncftpd")) {
			print RESTARTSCRIPT "#!/bin/sh\n";
			print RESTARTSCRIPT "NCFTPD_STOP_SCRIPT=\`grep \'^pid-file\' \"$new_general_cf\" \| cut -d= -f2\`\n";
			print RESTARTSCRIPT "if [ -x \"\$NCFTPD_STOP_SCRIPT\" ] ; then\n";
			print RESTARTSCRIPT "		echo \'Restarting NcFTPd.\'\n";
			print RESTARTSCRIPT "		/bin/sh \"\$NCFTPD_STOP_SCRIPT\"\n";
			print RESTARTSCRIPT "		exit 0\n";
			print RESTARTSCRIPT "fi\n";
			print RESTARTSCRIPT "exit 1\n";
			close(RESTARTSCRIPT);
			chmod(0700, "$sbindir/restart_ncftpd");
			Log(0, "Created $sbindir/restart_ncftpd.\n");
			UninstallLog("Remove(\"$sbindir/restart_ncftpd\");\n");
		}

		#
		# Now add our entry to the /etc/inittab.
		# When we "init q", NcFTPd will start up.
		#
		if (open(ETCINITTAB, ">>/etc/inittab")) {
			print ETCINITTAB "#\n# Added by install_ncftpd.pl\n#\n";
			print ETCINITTAB "nc:$runlevels:respawn:$inittabscriptpath$inittabscript_redirect\n";
			close(ETCINITTAB);
			Log(1, "Added entry to /etc/inittab.\n");
			UninstallLog("RemoveInittabEntry(\"nc:$runlevels:respawn:$inittabscriptpath$inittabscript_redirect\");\n");
		}
		$inittab_startup_script = $inittabscriptpath;
	} elsif ($initscriptpath ne "") {
		SafeRenameWithNcVersion($initscriptpath);
		if (! open(SCRIPT, ">$initscriptpath")) {
			Exit(1, "Could not create $initscriptpath: $!\n");
		} else {
			print SCRIPT @initscript;
			close(SCRIPT);
		}
		chmod(0755, $initscriptpath);
		Log(1, "Installed $initscriptpath.\n");
		UninstallLog("Remove(\"$initscriptpath\");\n");

		push(@rc_links, "$sbindir/restart_ncftpd");
		push(@rc_links, "$sbindir/stop_ncftpd");
		for $rc_link (@rc_links) {
			Symlink($initscriptpath, $rc_link, "overwrite");
		}
		$rc_startup_script = $initscriptpath;
	}

	if (($rc_local ne "") && ($rc_startup_script ne "")) {
		if (open(RCLOCAL, "<$rc_local")) {
			while (defined($line = <RCLOCAL>)) {
				if ($line =~ /^[^#]*$rc_startup_script/) {
					$rc_local_already++;
					Log(1, "Already found $rc_startup_script in $rc_local.\n");
					last;
				}
			}
			close(RCLOCAL);
		}
		if (($rc_local_already == 0) && (open(RCLOCAL, ">>$rc_local"))) {
			if (($SYS eq "freebsd") && (! -s $rc_local)) {
				print RCLOCAL "#!/bin/sh\n\n. /etc/rc.conf\n\n";
			}
			print RCLOCAL "#\n# Added by install_ncftpd.pl\n#\n";
			print RCLOCAL "$rc_startup_script start\n";
			close(RCLOCAL);
			chmod(0755, $rc_local);
			Log(1, "Added entry to $rc_local.\n");
			UninstallLog("RemoveRcLocalEntry(\"$rc_local\", \"$rc_startup_script start\");\n");
		}
	}
}	# InstallStartupScript




sub StartNcFTPd
{
	if ($rc_startup_script ne "") {
		Log(1, "  /bin/sh $rc_startup_script start\n");
		# This will echo "Starting NcFTPd:"
		system("/bin/sh", $rc_startup_script, "start");
		UninstallLog("KillFTPd(\"ncftpd\");\n");
	} elsif ($inittab_startup_script ne "") {
		my ($init) = SearchPath("init", "/sbin:/usr/sbin:/usr/bin:/bin");
		$init = "init" unless ($init);
		Log(0, "Running \"$init q\" to reload modified /etc/inittab.\n");
		system($init, "q");
		Log(0, "Starting NcFTPd.\n");
	}
	sleep(3);
}	# StartNcFTPd




sub NcFTPdShouldNowBeRunning
{
	if (! WaitForFTPServerToStart()) {
		Log(0, "\nERROR: NcFTPd did not startup.\n\n");
		if (open(ERRLOG, "<$startup_error_log")) {
			my ($line);
			while (defined($line = <ERRLOG>)) {
				Log(0, $line);
			}
		}
		Exit(1);
	}

	Log(0, "\nCONGRATULATIONS!  NcFTPd has been successfully installed.\n");
	Log(0, "Your next step is to customize your installation by editing:\n\n");
	Log(0, "  $new_general_cf\n");
	Log(0, "  $new_domain_cf\n");
	Log(0, "\nBe sure to run $sbindir/restart_ncftpd after doing that.\n");
}	# NcFTPdShouldNowBeRunning




sub Main
{
	umask(077);
	OpenLog();
#	OpenPager();
	SetOSVar();
	Getopts();
	SetOSDefaults();
	if (($> != 0) && (! $force_install)) {
		Exit(1, "You need to be Superuser to install NcFTPd.  You are UID ", $>, ".\n");
	}

	VerifyOSPrerequisites();
	SetPATHEnvironmentVar( VerifyLocation() );
	FindPerl();
	VerifyPackageContents();
	VerifyFreeSpace();
	LookupFTPServiceName();
	AbortIfSomethingWeDontKnowHowToDisableIsUsingPort21();
	KillFTPd("ftpd|in.ftpd|wu\-?ftpd|proftpd");
	CreateEtcShells();
	CreateEtcFtpusers();
	AddFTPGroup();
	AddFTPUser();
	CreateFTPHome();
	IsThereAnFTPServerServing(0);
	DisableInetdHandlingOfFTP();
	DisableExistingFTPServersRunningFromInittab();	# Includes NcFTPd in /etc/inittab.
	DisableExistingNcFTPd();
	CheckIfSomeOtherFTPServerIsStillRunning();
	DisableRcScripts();
	NoteExistingNcFTPdVersion();
	LookupWebUser();
	MkInstallDirs();
	FixScript("extra/pgrep.pl", "#!${perl} -w");
	InstallConfigs();
	InstallPrograms();
	InstallStartupScript();
	StartNcFTPd();
	NcFTPdShouldNowBeRunning();
	Exit(0);
}	# Main

Main();
