/ / $|=1; use strict; use lib '/home/httpd/lib/perl/'; use LONCAPA::Configuration; use LONCAPA::Lond; use LONCAPA::SSL; use LONCAPA; use GDBM_File; use Storable qw(thaw); use Term::ReadKey; use Locale::Country; sub get_new_sslkeypass { my $sslkeypass; my $flag=0; # get Password for SSL key while (!$flag) { $sslkeypass = &make_passphrase(); if ($sslkeypass) { $flag = 1; } else { print "Invalid input (a password is required for the SSL key).\n"; } } return $sslkeypass; } sub get_static_config { # get LCperlvars from loncapa_apache.conf my $confdir = '/etc/httpd/conf/'; if ('' eq 'sles10' || '' eq 'sles11' || '' eq 'sles12' || '' eq 'suse10.1' || '' eq 'suse10.2' || '' eq 'suse10.3' || '' eq 'suse11.1' || '' eq 'suse11.2' || '' eq 'suse11.3' || '' eq 'suse11.4' || '' eq 'suse12.1' || '' eq 'suse12.2' || '' eq 'suse12.3' || '' eq 'suse13.1' || '' eq 'suse13.2' || '' eq 'debian5' || '' eq 'debian6' || '' eq 'ubuntu6' || '' eq 'ubuntu8' || '' eq 'ubuntu10' || '' eq 'ubuntu12' || '' eq 'ubuntu14' || '' eq 'ubuntu16') { $confdir = '/etc/apache2/'; } my $filename='loncapa_apache.conf'; my %LCperlvar; if (-e "$confdir$filename") { open(CONFIG,'<'.$confdir.$filename) or die("Can't read $confdir$filename"); while (my $configline=<CONFIG>) { if ($configline =~ /^[^\#]?PerlSetVar/) { my ($unused,$varname,$varvalue)=split(/\s+/,$configline); chomp($varvalue); $LCperlvar{$varname}=$varvalue; } } close(CONFIG); } return \%LCperlvar; } sub get_domain_config { my ($dom,$primaryserver,$isprimary,$url,$perlvarref) = @_; my %confhash; if ($isprimary) { if (ref($perlvarref) eq 'HASH') { my $lonusersdir = $perlvarref->{'lonUsersDir'}; my $fname = $lonusersdir.'/'.$dom.'/configuration.db'; if (-e $fname) { my $dbref=&LONCAPA::locking_hash_tie($fname,&GDBM_READER()); if (ref($dbref) eq 'HASH') { foreach my $key (sort(keys(%{$dbref}))) { my $value = $dbref->{$key}; if ($value =~ s/^__FROZEN__//) { $value = thaw(&LONCAPA::unescape($value)); } else { $value = &LONCAPA::unescape($value); } $confhash{$key} = $value; } &LONCAPA::locking_hash_untie($dbref); } } } } else { if (open(PIPE,"wget --no-check-certificate '$url?primary=$primaryserver&format=raw' |")) { my $config = ''; while (<PIPE>) { $config .= $_; } close(PIPE); if ($config) { my @pairs=split(/\&/,$config); foreach my $item (@pairs) { my ($key,$value)=split(/=/,$item,2); my $what = &LONCAPA::unescape($key); if ($value =~ s/^__FROZEN__//) { $value = thaw(&LONCAPA::unescape($value)); } else { $value = &LONCAPA::unescape($value); } $confhash{$what}=$value; } } } } return (\%confhash); } sub make_passphrase { my ($got_passwd,$firstpass,$secondpass,$passwd); my $maxtries = 10; my $trial = 0; while ((!$got_passwd) && ($trial < $maxtries)) { $firstpass = &get_password('Enter a password for the SSL key (at least 6 characters long)'); if (length($firstpass) < 6) { print('Password too short.'."\n". 'Please choose a password with at least six characters.'."\n". 'Please try again.'."\n"); } elsif (length($firstpass) > 30) { print('Password too long.'."\n". 'Please choose a password with no more than thirty characters.'."\n". 'Please try again.'."\n"); } else { my $pbad=0; foreach (split(//,$firstpass)) {if ((ord($_)<32)||(ord($_)>126)){$pbad=1;}} if ($pbad) { print('Password contains invalid characters.'."\n". 'Password must consist of standard ASCII characters.'."\n". 'Please try again.'."\n"); } else { $secondpass = &get_password('Enter password a second time'); if ($firstpass eq $secondpass) { $got_passwd = 1; $passwd = $firstpass; } else { print('Passwords did not match.'."\n". 'Please try again.'."\n"); } } } $trial ++; } return $passwd; } sub get_password { my ($prompt) = @_; local $| = 1; print $prompt.': '; my $newpasswd = ''; ReadMode 'raw'; my $key; while(ord($key = ReadKey(0)) != 10) { if(ord($key) == 127 || ord($key) == 8) { chop($newpasswd); print "\b \b"; } elsif(!ord($key) < 32) { $newpasswd .= $key; print '*'; } } ReadMode 'normal'; print "\n"; return $newpasswd; } sub send_mail { my ($hostname,$recipient,$subj,$file) = @_; my $from = 'www@'.$hostname; my $certmail = "To: $recipient\n". "From: $from\n". "Subject: ".$subj."\n". "Content-type: text/plain\; charset=UTF-8\n". "MIME-Version: 1.0\n\n"; if (open(my $fh,"<$file")) { while (<$fh>) { $certmail .= $_; } close($fh); $certmail .= "\n\n"; if (open(my $mailh, "|/usr/lib/sendmail -oi -t -odb")) { print $mailh $certmail; close($mailh); print "Mail sent ($subj) to $recipient\n"; } else { print "Sending mail ($subj) to $recipient failed.\n"; } } return; } sub mail_csr { my ($types,$lonCluster,$lonHostID,$desiredhostname,$certsdir,$connectcsr,$replicatecsr,$perlvarref) = @_; my ($camail,$flag); if ($lonCluster eq 'production' || $lonCluster eq 'development') { $camail = $perlvarref->{'SSLEmail'}; } else { $flag=0; # get Certificate Authority E-mail while (!$flag) { print(<<END); ENTER EMAIL ADDRESS TO SEND CERTIFICATE SIGNING REQUESTS END my $choice=<>; chomp($choice); if ($choice ne '') { open(OUT,'>>/tmp/loncapa_updatequery.out'); print(OUT 'Certificate Authority Email Address'."\t".$choice."\n"); close(OUT); $camail=$choice; $flag=1; } else { print "Invalid input (an email address is required).\n"; } } } if ($camail) { my $subj; if (($types eq 'both') || ($types = 'host')) { if (-e "$certsdir/$connectcsr") { $subj = "Certificate Request ($lonHostID)"; print(&send_mail($desiredhostname,$camail,$subj,"$certsdir/$connectcsr")); } } if (($types eq 'both') || ($types = 'hostname')) { if (-e "$certsdir/$replicatecsr") { $subj = "Certificate Request (internal-$desiredhostname)"; print(&send_mail($desiredhostname,$camail,$subj,"$certsdir/$replicatecsr")); } } } } sub ssl_info { print(<<END); ****** Information about Country, State or Province and City ***** A two-letter country code, e.g., US, CA, DE etc. as defined by ISO 3166, is required. A state or province, and a city are also required. This locality information is included in two SSL certificates used internally by LON-CAPA, unless you are running standalone. If your server will be part of either the production or development clusters, then the certificate will need to be signed by the official LON-CAPA Certificate Authority (CA). If you will be running your own cluster then the cluster will need to create its own CA. END } sub get_country { my ($desiredhostname) = @_; # get Country my ($posscountry,$country); if ($desiredhostname =~ /\.(edu|com|org)$/) { $posscountry = 'us'; } else { ($posscountry) = ($desiredhostname =~ /\.(a-z){2}$/); } if ($posscountry) { my $countrydesc = &Locale::Country::code2country($posscountry); if ($countrydesc eq '') { undef($posscountry); } } my $flag=0; while (!$flag) { if ($posscountry) { $posscountry = uc($posscountry); print "ENTER TWO-LETTER COUNTRY CODE [$posscountry]:\n"; } else { print "ENTER TWO-LETTER COUNTRY CODE:\n"; } my $choice=<>; chomp($choice); if ($choice ne '') { if (&Locale::Country::code2country(lc($choice))) { open(OUT,'>>/tmp/loncapa_updatequery.out'); print(OUT 'country'."\t".uc($choice)."\n"); close(OUT); $country=uc($choice); $flag=1; } else { print "Invalid input -- a valid two letter country code is required\n"; } } elsif (($choice eq '') && ($posscountry ne '')) { open(OUT,'>>/tmp/loncapa_updatequery.out'); print(OUT 'country'."\t".$posscountry."\n"); close(OUT); $country = $posscountry; $flag = 1; } else { print "Invalid input -- a country code is required\n"; } } return $country; } sub get_state { # get State or Province my $flag=0; my $state = ''; while (!$flag) { print(<<END); ENTER STATE OR PROVINCE NAME: END my $choice=<>; chomp($choice); if ($choice ne '') { open(OUT,'>>/tmp/loncapa_updatequery.out'); print(OUT 'state'."\t".$choice."\n"); close(OUT); $state=$choice; $flag=1; } else { print "Invalid input (a state or province name is required).\n"; } } return $state; } sub get_city { # get City my $flag=0; my $city = ''; while (!$flag) { print(<<END); ENTER CITY NAME: END my $choice=<>; chomp($choice); if ($choice ne '') { open(OUT,'>>/tmp/loncapa_updatequery.out'); print(OUT 'city'."\t".$choice."\n"); close(OUT); $city=$choice; $flag=1; } else { print "Invalid input (a city is required).\n"; } } return $city; } sub confirm_locality { my ($domainDescription,$country,$state,$city) = @_; my $flag = 0; while (!$flag) { print(<<END); The domain description, country, state and city will be used in the SSL certificates 1) Domain Description: $domainDescription 2) Country: $country 3) State or Province: $state 4) City: $city 5) Everything is correct up above ENTER A CHOICE OF 1-4 TO CHANGE, otherwise ENTER 5: END my $choice=<>; chomp($choice); if ($choice == 1) { print(<<END); 1) Domain Description: $domainDescription ENTER NEW VALUE END my $choice2=<>; chomp($choice2); $domainDescription=$choice2; } elsif ($choice == 2) { print(<<END); 2) Country: $country ENTER NEW VALUE (this should be a two-character code, e,g, US, CA, DE) END my $choice2=<>; chomp($choice2); $country = uc($choice2); } elsif ($choice == 3) { print(<<END); 3) State or Province: $state ENTER NEW VALUE: END my $choice2=<>; chomp($choice2); $state=$choice2; } elsif ($choice == 4) { print(<<END); 4) City: $city ENTER NEW VALUE: END my $choice2=<>; chomp($choice2); $city=$choice2; } elsif ($choice == 5) { $flag=1; $state =~ s{/}{ }g; $city =~ s{/}{ }g; $domainDescription =~ s{/}{ }g; } else { print "Invalid input.\n"; } } return ($domainDescription,$country,$state,$city); } sub make_key { my ($certsdir,$privkey,$sslkeypass) = @_; # generate SSL key if ($certsdir && $privkey) { if (-f "$certsdir/lonKey.enc") { my $mode = 0600; chmod $mode, "$certsdir/lonKey.enc"; } open(PIPE,"openssl genrsa -des3 -passout pass:$sslkeypass -out $certsdir/lonKey.enc 2048 2>&1 |"); close(PIPE); if (-f "$certsdir/$privkey") { my $mode = 0600; chmod $mode, "$certsdir/$privkey"; } open(PIPE,"openssl rsa -in $certsdir/lonKey.enc -passin pass:$sslkeypass -out $certsdir/$privkey -outform PEM |"); close(PIPE); if (-f "$certsdir/lonKey.enc") { my $mode = 0400; chmod $mode, "$certsdir/lonKey.enc"; } if (-f "$certsdir/$privkey") { my $mode = 0400; chmod $mode, "$certsdir/$privkey"; } } else { print "Key creation failed. Missing one or more of: certificates directory, key name\n"; } } sub encrypt_key { my ($certsdir,$privkey,$sslkeypass) = @_; if ($certsdir && $privkey) { if ((-f "$certsdir/$privkey") && (!-f "$certsdir/lonKey.enc")) { open(PIPE,"openssl rsa -des3 -in $certsdir/$privkey -out $certsdir/lonKey.enc |"); } } return; } sub make_host_csr { my ($certsdir,$sslkeypass,$connectcsr,$connectsubj) = @_; # generate SSL csr for hostID if ($certsdir && $connectcsr && $connectsubj) { open(PIPE,"openssl req -key $certsdir/lonKey.enc -passin pass:$sslkeypass -new -batch -subj \"$connectsubj\" -out $certsdir/$connectcsr |"); close(PIPE); } else { print "Creation of certificate signing request failed. Missing one or more of: certificates directory, CSR name, or locality information.\n"; } } sub make_hostname_csr { my ($certsdir,$sslkeypass,$replicatecsr,$replicatesubj) = @_; # generate SSL csr for internal hostname if ($certsdir && $replicatecsr && $replicatesubj) { open(PIPE,"openssl req -key $certsdir/lonKey.enc -passin pass:$sslkeypass -new -batch -subj \"$replicatesubj\" -out $certsdir/$replicatecsr |"); close(PIPE); } else { print "Creation of certificate signing request failed. Missing one or more of: certificates directory, CSR name, or locality information.\n"; } } sub securesetting { my (%perlvar) = @_; my ($securestatus,$securenum); if (($perlvar{'loncAllowInsecure'}) && ($perlvar{'londAllowInsecure'})) { $securenum = 4; $securestatus = 'Allow insecure connections - inbound and outbound'; } elsif (($perlvar{'loncAllowInsecure'}) && (!$perlvar{'londAllowInsecure'})) { $securenum = 3; $securestatus = 'Outbound: allow insecure connections; Inbound: secure only'; } elsif ((!$perlvar{'loncAllowInsecure'}) && ($perlvar{'londAllowInsecure'})) { $securenum = 2; $securestatus = 'Outbound: secure connections only; Inbound: allow insecure'; } elsif ((!$perlvar{'loncAllowInsecure'}) && (!$perlvar{'londAllowInsecure'})) { $securenum = 1; $securestatus = 'Secure connections only - inbound and outbound '; } return ($securestatus,$securenum); } sub get_sslnames { my %sslnames = ( key => 'lonnetPrivateKey', host => 'lonnetCertificate', hostname => 'lonnetHostnameCertificate', ca => 'lonnetCertificateAuthority', ); return %sslnames; } sub get_ssldesc { my %ssldesc = ( key => 'Private Key', host => 'Connections Certificate', hostname => 'Replication Certificate', ca => 'LON-CAPA CA Certificate', ); return %ssldesc; } sub get_cert_status { my ($lonHostID,$perlvarstatic) = @_; my $currcerts = &LONCAPA::SSL::print_certstatus({$lonHostID => 1,},'text','cgi'); my ($lonkeystatus,$lonhostcertstatus,$lonhostnamecertstatus,%sslstatus); my $output = ''; if ($currcerts eq "$lonHostID:error") { $output .= "No information available for SSL certificates\n"; $sslstatus{'key'} = -1; $sslstatus{'host'} = -1; $sslstatus{'hostname'} = -1; $sslstatus{'ca'} = -1; $lonkeystatus = 'unknown status'; $lonhostcertstatus = 'unknown status'; $lonhostnamecertstatus = 'unknown status'; } else { my %sslnames = &get_sslnames(); my %ssldesc = &get_ssldesc(); my ($lonhost,$info) = split(/\:/,$currcerts,2); if ($lonhost eq $lonHostID) { my @items = split(/\&/,$info); foreach my $item (@items) { my ($key,$value) = split(/=/,$item,2); my @data = split(/,/,$value); if (grep(/^\Q$key\E$/,keys(%sslnames))) { if (lc($data[0]) eq 'yes') { $output .= "$ssldesc{$key} ".$perlvarstatic->{$sslnames{$key}}." available with status = $data[1]\n"; if ($key eq 'key') { $lonkeystatus = "status: $data[1]"; if ($data[1] =~ /ok$/) { $sslstatus{$key} = 1; } } else { my $setstatus; if (($key eq 'host') || ($key eq 'hostname')) { if ($data[1] eq 'otherkey') { $sslstatus{$key} = 4; $setstatus = 1; if ($key eq 'host') { $lonhostcertstatus = "status: created with different key"; } elsif ($key eq 'hostname') { $lonhostnamecertstatus = "status: created with different key"; } } elsif ($data[1] eq 'nokey') { $sslstatus{$key} = 5; $setstatus = 1; if ($key eq 'host') { $lonhostcertstatus = "status: created with missing key"; } elsif ($key eq 'hostname') { $lonhostnamecertstatus = "status: created with missing key"; } } } unless ($setstatus) { if ($data[1] eq 'expired') { $sslstatus{$key} = 2; } elsif ($data[1] eq 'future') { $sslstatus{$key} = 3; } else { $sslstatus{$key} = 1; } if ($key eq 'host') { $lonhostcertstatus = "status: $data[1]"; } elsif ($key eq 'hostname') { $lonhostnamecertstatus = "status: $data[1]"; } } } } else { $sslstatus{$key} = 0; $output .= "$ssldesc{$key} ".$perlvarstatic->{$sslnames{$key}}." not available\n"; if (($key eq 'host') || ($key eq 'hostname')) { my $csr = $perlvarstatic->{$sslnames{$key}}; $csr =~s /\.pem$/.csr/; my $csrstatus; if (-e $perlvarstatic->{'lonCertificateDirectory'}."/$csr") { open(PIPE,"openssl req -text -noout -verify -in ".$perlvarstatic->{'lonCertificateDirectory'}."/$csr 2>&1 |"); while(<PIPE>) { chomp(); $csrstatus = $_; last; } close(PIPE); $output .= "Certificate signing request for $ssldesc{$key} available with status = $csrstatus\n\n"; if ($key eq 'host') { $lonhostcertstatus = 'awaiting signature'; } else { $lonhostnamecertstatus = 'awaiting signature'; } $sslstatus{$key} = 3; } else { $output .= "No certificate signing request available for $ssldesc{$key}\n\n"; if ($key eq 'host') { $lonhostcertstatus = 'still needed'; } else { $lonhostnamecertstatus = 'still needed'; } } } elsif ($key eq 'key') { $lonkeystatus = 'still needed'; } } } } } } return ($output,$lonkeystatus,$lonhostcertstatus,$lonhostnamecertstatus,\%sslstatus); } print(<<END); ********************************************* ********************************************* **** **** **** LON-CAPA SYSTEM INFORMATION REQUEST **** **** **** **** Please respond to the choices below **** **** **** ********************************************* ********************************************* END #sleep(3); ../../loncom/hosts.tab my $lonCluster; my $currCluster; if (-l "") { my $currlink = readlink(""); if ($currlink =~ /^new_(existing|standalone|development|production)_hosts\.tab$/) { $currCluster = $1; } my %clustertypes = ( production => 'PRODUCTION', standalone => 'STAND-ALONE', development => 'DEVELOPMENT', existing => 'RUNNING YOUR OWN CLUSTER', ); if (($currCluster) && (exists($clustertypes{$currCluster}))) { print(<<END); The cluster type for this server is currently: $clustertypes{$currCluster} END } } print(<<END); =============================================================================== Which cluster option would you like to have installed? IMPORTANT: to take advantage of the cluster options 1) and 3), you must contact loncapa\@loncapa.org. 1) PRODUCTION - you want to eventually connect this machine to the LON-CAPA content sharing network. This setting is for schools, colleges, and universities, that currently are running - or in the future will run - courses. 2) STAND-ALONE - you want this machine to run in 'stand-alone' mode and not be connected to other LON-CAPA machines for now. 3) DEVELOPMENT - you want to do software (not content!) development with this workstation and eventually link it with the workstations of other LON-CAPA software developers. 4) RUNNING YOUR OWN CLUSTER - this machine is not in the standard LON-CAPA clusters and won't be in the future and you want the existing hosts.tab and domain.tab files to be left alone. (This choice is unlikely what you want to select.) END # Option number 26 will install rawhide_hosts.tab, but # the typical user does not want to be part of an intensive # machine test cluster. # get input # if valid then process, otherwise loop my $flag=0; while (!$flag) { print "ENTER 1, 2, 3, or 4:\n"; my $choice=<>; chomp($choice); if ($choice==1) { $lonCluster='production'; $flag=1; } elsif ($choice==2) { $lonCluster='standalone'; $flag=1; } elsif ($choice==3) { $lonCluster='development'; $flag=1; } elsif ($choice==4) { $lonCluster='existing'; $flag=1; foreach my $file ('hosts.tab','dns_hosts.tab', 'domain.tab','dns_domain.tab') { if (-e '/home/httpd/lonTabs/'.$file) { `cp /home/httpd/lonTabs/$file ../existing_$file`; } else { print <<END; There is no existing /home/httpd/lonTabs/$file END die(''); } } } elsif ($choice==26) { $lonCluster='rawhide'; $flag=1; } } /home/httpd/lonTabs/hosts.tab $|=1; my $domainDescription; my $domainTabExtras; my $primaryLibServer; my $protocol; my $intdom; my $desiredhostname; my $city; my $state; my $country; my @libservers = (); unless (-e "") { print(<<END); WELCOME TO LON-CAPA! If you have questions, please visit http://install.loncapa.org or contact helpdesk\@loncapa.org. =============================================================================== The following 10 values are needed to configure LON-CAPA: * Machine Role * LON-CAPA Domain Name * LON-CAPA Machine ID Name * Server Administration E-mail Address * LON-CAPA Domain's Primary Library Server Machine ID * Web Server Protocol * Internet Domain Name of Your Institution * Hostname * City, State, Country for LON-CAPA SSL certificate * Password for key for creating SSL certificates =============================================================================== In addition, a Support E-mail Address can also be included. If an address is included then one of the options in the LON-CAPA help menu will be a link to a form that a user will complete to request LON-CAPA help. END open(OUT,'>/tmp/loncapa_updatequery.out'); close(OUT); # query for Machine Role print(<<END); **** Machine Role **** Library server (recommended if first-time installation of LON-CAPA): Servers that are repositories of authoritative educational resources. These servers also provide the authoring spaces in which content creators (e.g., faculty instructors) create their learning content. Access server: Servers that load-balance high-traffic delivery of educational resources over the world-wide web. 1) Will this be a library server? (recommended if this is your first install) 2) Or, will this be an access server? END my $flag=0; my $r=''; my $lonRole; while (!$flag) { print "ENTER A CHOICE OF 1 or 2:\n"; my $choice=<>; chomp($choice); if ($choice==1) { open(OUT,'>>/tmp/loncapa_updatequery.out'); print(OUT 'lonRole'."\t".'library'."\n"); close(OUT); $lonRole='library'; $r='l'; $flag=1; } elsif ($choice==2) { open(OUT,'>>/tmp/loncapa_updatequery.out'); print(OUT 'lonRole'."\t".'access'."\n"); close(OUT); $lonRole='access'; $r='a'; $flag=2; } else { } } # need to recommend a machine ID name (ipdomain.l.somenumber) my $hostname=`hostname -f`; chomp($hostname); my $ipdomain=''; if ($hostname=~/([^\.]*)\.([^\.]*)$/) { $ipdomain=$1; } print(<<END); **** Domain **** [This does NOT need to correspond to an internet address domain. Please make this name short AND descriptive of your organization. Domain names are close to impossible to change later!!! Good examples might be "msu" or "bionet" or "vermontcc". Bad examples are "physics" (too general) or "michiganstateuniversity" (too long) or "msuedu" (just make it "msu", or else make it msu.edu) or "msuphysics" (only if there is a good reason to limit to department - we don't know of one) or "mydomain" (what is that?) Avoid multiple domains at the same institution, even if it means that you have to actually work together with your colleagues. You can still run multiple library servers within the same domain. If this domain is eventually going to be part of the main production cluster, you MUST contact the LON-CAPA group at MSU (loncapa\@loncapa.org) to have a domain name assigned, and then use it exactly as given. This is also true for test installs that might eventually turn into production setups. The short domain name needs to be unique, if your aim is to join a cluster containing existing domains. Stop now if you have not yet contacted the MSU LON-CAPA group.] END # get domain name # accept if valid, if not valid, tell user and repeat $flag=0; my $lonDefDomain; while (!$flag) { if ($ipdomain) { print(<<END); ENTER LONCAPA DOMAIN [$ipdomain]: END } else { print(<<END); ENTER LONCAPA DOMAIN: END } my $choice=<>; chomp($choice); my $bad_domain_flag=0; my @bad_domain_names=('res','raw','userfiles','priv','adm','uploaded', 'editupload'); foreach my $bad (@bad_domain_names) { $bad_domain_flag=1 if $choice eq $bad; } if ($choice=~/capa/i) { $bad_domain_flag=1; } if ($ipdomain and $choice=~/^\s*$/) { $choice=$ipdomain; open(OUT,'>>/tmp/loncapa_updatequery.out'); print(OUT 'lonDefDomain'."\t".$choice."\n"); close(OUT); $lonDefDomain=$choice; $flag=1; } elsif (length($choice)>35) { print "Name too long\n"; } elsif (length($choice)<2) { print "Name too short\n"; } elsif ($bad_domain_flag) { print "Invalid input ('$choice' conflicts with LON-CAPA namespace).\n"; print "Please try something different than '$choice'\n"; } elsif ($choice!~/\_/ and $choice=~/^[\w\-.]+$/) { open(OUT,'>>/tmp/loncapa_updatequery.out'); print(OUT 'lonDefDomain'."\t".$choice."\n"); close(OUT); $lonDefDomain=$choice; $r='l'; $flag=1; } else { print "Invalid input (only alphanumeric characters, '-', and '.' supported).\n"; } } # get domain description # accept if valid, if not valid, tell user and repeat $flag=0; while (!$flag) { print(<<END); **** Domain Description **** String describing the domain, to be shown to users. [Example, msu is Michigan State University] ENTER DOMAIN DESCRIPTION: END my $choice=<>; chomp($choice); if ($choice!~/:/) { open(OUT,'>>/tmp/loncapa_updatequery.out'); print(OUT 'domainDescription'."\t".$choice."\n"); close(OUT); $domainDescription=$choice; $flag=1; } else { print "Invalid input (no ':' allowed).\n"; } } my $lonHostID; if ($lonDefDomain) { $lonHostID=$lonDefDomain.$r.int(1+rand(9)); # should be probably also detect # against the hosts.tab } print(<<END); **** Machine ID Name **** [This does NOT need to correspond to internet address names; this name MUST be unique to the whole LON-CAPA network; we recommend that you use a name based off of your institution. Good examples: "msul1" or "bioneta2". Bad examples: "loncapabox" or "studentsinside". Note that machine names are very hard to change later.] END # get machine name # accept if valid, if not valid, tell user and repeat $flag=0; while (!$flag) { if ($ipdomain) { print(<<END); ENTER LONCAPA MACHINE ID [$lonHostID]: END } else { print(<<END); ENTER LONCAPA MACHINE ID: END } my $choice=<>; chomp($choice); if ($choice=~/capa/i) { print "Invalid input (names containing 'capa' are reserved).\n"; } elsif ($lonHostID and $choice=~/^\s*$/) { $choice=$lonHostID; open(OUT,'>>/tmp/loncapa_updatequery.out'); print(OUT 'lonHostID'."\t".$choice."\n"); close(OUT); $lonHostID=$choice; $flag=1; } elsif (length($choice)>45) { print "Name too long\n"; } elsif (length($choice)<4) { print "Name too short\n"; } elsif ($choice!~/\_/ and $choice=~/^[\w\-.]+$/) { open(OUT,'>>/tmp/loncapa_updatequery.out'); print(OUT 'lonHostID'."\t".$choice."\n"); close(OUT); $lonHostID=$choice; $flag=1; } else { print "Invalid input (only alphanumeric characters, '-', and '.' supported).\n"; } } # get primary library server in domain if ($lonRole eq 'library') { if (!grep/^\Q$lonHostID\E$/,@libservers) { push(@libservers,$lonHostID); } if (@libservers == 1) { $primaryLibServer = $libservers[0]; } } $flag=0; while (!$flag) { print(<<END); **** Domain's Primary Library Server ID **** This should be the LON-CAPA machine ID of a library server in your domain. If you only have a single library server in your domain, then the Primary Library server ID will be the machine ID of that server. This server will be where domain data which are not associated with any specific home library server will be stored (e.g., configurations that apply to all nodes in the domain). END if (defined($primaryLibServer)) { print(<<END); ENTER DOMAIN'S PRIMARY LIBRARY SERVER ID [$primaryLibServer]: END } elsif (@libservers > 0) { print(<<END); ENTER DOMAIN'S PRIMARY LIBRARY SERVER ID [$libservers[0]] END } else { print (<<END); No library servers could be identified for this domain. If you have already installed LON-CAPA on a different server (designated as a library server) in this domain, please enter the LONCAPA MACHINE ID of that server. If not, you will need to install a LON-CAPA library server. Enter the MACHINE ID of the server you plan to designate as a library server. END } my $choice=<>; chomp($choice); if ($primaryLibServer and $choice=~/^\s*$/) { $choice=$primaryLibServer; open(OUT,'>>/tmp/loncapa_updatequery.out'); print(OUT 'primaryLibServer'."\t".$choice."\n"); close(OUT); $flag=1; } elsif (length($choice)>35) { print "Name too long\n"; } elsif (length($choice)<4) { print "Name too short\n"; } elsif ($choice!~/\_/ and $choice=~/^[\w\-.]+$/) { open(OUT,'>>/tmp/loncapa_updatequery.out'); print(OUT 'primaryLibServer'."\t".$choice."\n"); close(OUT); $primaryLibServer=$choice; $flag=1; } else { print "Invalid input (only alphanumeric characters, '-', and '.' supported).\n"; } } # get admin e-mail address # accept if valid, if not valid, tell user and repeat $flag=0; my $lonAdmEMail; while (!$flag) { print(<<END); **** Server Administrator's E-mail **** E-mail address of the person who will manage this machine [should be in the form somebody\@somewhere] ENTER ADMIN E-MAIL ADDRESS: END my $choice=<>; chomp($choice); if ($choice=~/\@/) { open(OUT,'>>/tmp/loncapa_updatequery.out'); print(OUT 'lonAdmEMail'."\t".$choice."\n"); close(OUT); $lonAdmEMail=$choice; $flag=1; } else { print "Invalid input (this needs to look like an e-mail address!).\n"; } } # get support e-mail address # accept if valid, if not valid, tell user and repeat $flag=0; my $lonSupportEMail; while (!$flag) { print(<<END); **** Support E-mail **** E-mail address of the person who will receive help requests from LON-CAPA users who access the system via this server. If the address is left blank, then a help support form will not be displayed as part of the help menu. [should be in the form somebody\@somewhere] ENTER SUPPORT E-MAIL ADDRESS: END my $choice=<>; chomp($choice); $choice =~ s/\s//g; if ( ($choice=~/\@/) || $choice eq '') { open(OUT,'>>/tmp/loncapa_updatequery.out'); print(OUT 'lonSupportEMail'."\t".$choice."\n"); close(OUT); $lonSupportEMail=$choice; $flag=1; } else { print "Invalid input (this either needs to be blank, or look like an e-mail address!).\n"; } } # get protocol # accept if valid, if not valid, tell user and repeat $flag=0; while (!$flag) { print(<<END); **** Web Server Protocol **** If you plan to run the Apache server with SSL enabled, the protocol should be: https; otherwise it should be http. ENTER WEB SERVER PROTOCOL [http]: END my $choice=<>; chomp($choice); if ($choice =~ /^https?$/) { open(OUT,'>>/tmp/loncapa_updatequery.out'); print(OUT 'protocol'."\t".$choice."\n"); close(OUT); $protocol=$choice; $flag=1; } elsif ($choice eq '') { open(OUT,'>>/tmp/loncapa_updatequery.out'); print(OUT 'protocol'."\t".'http'."\n"); close(OUT); $protocol = 'http'; $flag = 1; } else { print "Invalid input (only http or https allowed).\n"; } } # get internet domain # accept if valid, if not valid, tell user and repeat $flag=0; while (!$flag) { print(<<END); **** Internet Domain Name of Your Institution **** The internet domain name used for servers at your institution should be provided. This will be similar to: ustate.edu or topcollege.ac.uk or myhostingcompany.com, i.e., the part of a server hostname which indicates to which organization the server belongs. ENTER INTERNET DOMAIN NAME: END my $choice=<>; chomp($choice); if ($choice =~/[^.]+\.[^.]+/) { open(OUT,'>>/tmp/loncapa_updatequery.out'); print(OUT 'internet domain'."\t".$choice."\n"); close(OUT); $intdom=$choice; $flag=1; } else { print "Invalid input (must be at least two levels separated by . - e.g., ustate.edu).\n"; } } # get hostname # accept if valid, if not valid, tell user and repeat $flag=0; my $posshostname; if (($hostname =~ /^[A-Za-z0-9\-]+$/) && ($intdom ne '')) { $posshostname = $hostname.'.'.$intdom; } if (($hostname =~ /^[A-Za-z0-9\-]+\.[A-Za-z0-9\-]+/) && ($hostname =~ /^[A-Za-z0-9.\-]+$/)) { $posshostname = $hostname; } while (!$flag) { print(<<END); ****** Hostname of the server/VM ***** The hostname of the server/VM is required. This will be similar to: somename.ustate.edu or somename.department.ustate.edu, and would be the web address which users would point their web browsers at to access the server. END if ($posshostname) { print "ENTER HOSTNAME OF SERVER [$posshostname]:\n"; } else { print "ENTER HOSTNAME OF SERVER:\n"; } my $choice=<>; chomp($choice); if (($choice =~ /^[A-Za-z0-9\-]+\.[A-Za-z0-9\-]+/) && ($choice =~ /^[A-Za-z0-9.\-]+$/)) { open(OUT,'>>/tmp/loncapa_updatequery.out'); print(OUT 'hostname'."\t".$choice."\n"); close(OUT); $desiredhostname=$choice; $flag=1; } elsif (($choice eq '') && ($posshostname ne '')) { open(OUT,'>>/tmp/loncapa_updatequery.out'); print(OUT 'hostname'."\t$posshostname\n"); close(OUT); $desiredhostname = $posshostname; $flag = 1; } else { print "Invalid input (only letters, numbers, - and . allowed, with at least one .).\n"; } } &ssl_info(); $country = &get_country($desiredhostname); $state = &get_state(); $city = &get_city(); ($domainDescription,$country,$state,$city) = &confirm_locality($domainDescription,$country,$state,$city); my $perlstaticref = &get_static_config(); if (ref($perlstaticref) eq 'HASH') { my ($certsdir,$privkey,$connectcsr,$replicatecsr); $certsdir = $perlstaticref->{'lonCertificateDirectory'}; $privkey = $perlstaticref->{'lonnetPrivateKey'}; $connectcsr = $perlstaticref->{'lonnetCertificate'}; $connectcsr =~ s/\.pem$/.csr/; $replicatecsr = $perlstaticref->{'lonnetHostnameCertificate'}; $replicatecsr =~ s/\.pem$/.csr/; print(<<END); ****** SSL Certificates ***** You need to provide a password to be used for the openssl key which will be stored in $certsdir, and will be used when creating two certificate signing requests: $connectcsr and $replicatecsr END my $sslkeypass = &get_new_sslkeypass(); if ($certsdir && $privkey) { my $connectsubj = "/C=$country/ST=$state/O=$domainDescription/L=$city/CN=$lonHostID/OU=LONCAPA/emailAddress=$lonAdmEMail"; my $replicatesubj = "/C=$country/ST=$state/O=$domainDescription/L=$city/CN=internal-$desiredhostname/OU=LONCAPA/emailAddress=$lonAdmEMail"; # generate SSL key &make_key($certsdir,$privkey,$sslkeypass); # generate SSL csr for hostID &make_host_csr($certsdir,$sslkeypass,$connectcsr,$connectsubj); # generate SSL csr for internal hostname &make_hostname_csr($certsdir,$sslkeypass,$replicatecsr,$replicatesubj); # mail csr files to certificate@lon-capa.org (production or dev clusters). &mail_csr('both',$lonCluster,$lonHostID,$desiredhostname,$certsdir,$connectcsr,$replicatecsr,$perlstaticref); } else { print "Could not acquire standard names for SSL Certificate files from loncapa_apache.conf\n"; } } else { print "Could not acquire standard names for SSL Certificate files from loncapa_apache.conf\n"; } # update loncapa.conf my $confdir = '/etc/httpd/conf/'; if ('' eq 'sles10' || '' eq 'sles11' || '' eq 'sles12' || '' eq 'suse10.1' || '' eq 'suse10.2' || '' eq 'suse10.3' || '' eq 'suse11.1' || '' eq 'suse11.2' || '' eq 'suse11.3' || '' eq 'suse11.4' || '' eq 'suse12.1' || '' eq 'suse12.2' || '' eq 'suse12.3' || '' eq 'suse13.1' || '' eq 'suse13.2' || '' eq 'debian5' || '' eq 'debian6' || '' eq 'ubuntu6' || '' eq 'ubuntu8' || '' eq 'ubuntu10' || '' eq 'ubuntu12' || '' eq 'ubuntu14' || '' eq 'ubuntu16') { $confdir = '/etc/apache2/'; } my $filename='loncapa.conf'; my %perlvar; if (-e "$confdir$filename") { open(CONFIG,'<'.$confdir.$filename) or die("Can't read $confdir$filename"); while (my $configline=<CONFIG>) { if ($configline =~ /^[^\#]*PerlSetVar/) { my ($unused,$varname,$varvalue)=split(/\s+/,$configline); chomp($varvalue); $perlvar{$varname}=$varvalue if $varvalue!~/^\{\[\[\[\[/; } } close(CONFIG); } $perlvar{'lonHostID'}=$lonHostID; $perlvar{'lonDefDomain'}=$lonDefDomain; $perlvar{'lonAdmEMail'}=$lonAdmEMail; $perlvar{'lonSupportEMail'}=$lonSupportEMail; $perlvar{'lonRole'}=$lonRole; unless ($perlvar{'lonLoadLim'} and $perlvar{'lonLoadLim'}!~/\{\[\[\[\[/) { $perlvar{'lonLoadLim'}='2.00'; } unless ($perlvar{'lonUserLoadLim'} and $perlvar{'lonUserLoadLim'}!~/\{\[\[\[\[/) { $perlvar{'lonUserLoadLim'}='0'; } unless ($perlvar{'lonExpire'} and $perlvar{'lonExpire'}!~/\{\[\[\[\[/) { $perlvar{'lonExpire'}='86400'; } unless ($perlvar{'lonReceipt'} and $perlvar{'lonReceipt'}!~/\{\[\[\[\[/) { my $lonReceipt=''; srand(time ^ $$ ^ unpack "%L*", `ps axww | gzip`); my @alnum=(0..9,"a".."z"); foreach my $i (1..20) { $lonReceipt.=$alnum[int(rand(36))]; } $perlvar{'lonReceipt'}=$lonReceipt; } open(OUT,">$confdir$filename") or die("Cannot output to $confdir$filename\n"); foreach my $key (keys %perlvar) { my $value=$perlvar{$key}; my $line = "PerlSetVar $key $value"; if ($value eq '') { $line = '#'.$line; } print(OUT <<END); $line END } close(OUT); } /etc/httpd/conf/ /etc/apache2/ # read values from loncapa.conf my $confdir = ""; my $filename='loncapa.conf'; my %perlvar; if (-e "$confdir$filename") { open(CONFIG,'<'.$confdir.$filename) or die("Can't read $confdir$filename"); while (my $configline=<CONFIG>) { if ($configline =~ /^[^\#]*PerlSetVar/) { my ($unused,$varname,$varvalue)=split(/\s+/,$configline); chomp($varvalue); $perlvar{$varname}=$varvalue; } } close(CONFIG); } unless ($perlvar{'lonLoadLim'} and $perlvar{'lonLoadLim'}!~/\{\[\[\[\[/) { $perlvar{'lonLoadLim'}='2.00'; } unless ($perlvar{'lonUserLoadLim'} and $perlvar{'lonUserLoadLim'}!~/\{\[\[\[\[/) { $perlvar{'lonUserLoadLim'}='0'; } unless ($perlvar{'lonExpire'} and $perlvar{'lonExpire'}!~/\{\[\[\[\[/) { $perlvar{'lonExpire'}='86400'; } unless ($perlvar{'londAllowInsecure'} and $perlvar{'londAllowInsecure'}!~/\{\[\[\[\[/) { $perlvar{'londAllowInsecure'}='1'; } unless ($perlvar{'loncAllowInsecure'} and $perlvar{'loncAllowInsecure'}!~/\{\[\[\[\[/) { $perlvar{'loncAllowInsecure'}='1'; } my ($securestatus,$securenum)=&securesetting(%perlvar); unless ($perlvar{'lonReceipt'} and $perlvar{'lonReceipt'}!~/\{\[\[\[\[/) { my $lonReceipt=''; srand(time ^ $$ ^ unpack "%L*", `ps axww | gzip`); my @alnum=(0..9,"a".."z"); foreach my $i (1..20) { $lonReceipt.=$alnum[int(rand(36))]; } $perlvar{'lonReceipt'}=$lonReceipt; } my $perlvarstatic = &get_static_config(); my (@hosts_files, @domain_files); if ( $lonCluster ne 'existing') { push(@domain_files,'../'.$lonCluster.'_domain.tab', '../'.$lonCluster.'_dns_domain.tab'); push(@hosts_files,'../'.$lonCluster.'_hosts.tab', '../'.$lonCluster.'_dns_hosts.tab'); } push(@domain_files,'/home/httpd/lonTabs/domain.tab', '/home/httpd/lonTabs/dns_domain.tab'); push(@hosts_files,'/home/httpd/lonTabs/hosts.tab', '/home/httpd/lonTabs/dns_hosts.tab'); my @poss_hosts_files = @hosts_files; if (!$domainDescription) { foreach my $file (@domain_files) { open(IN,'<'.$file); while(my $line = <IN>) { if ($line =~ /^\Q$perlvar{'lonDefDomain'}\E\:/) { (undef,$domainDescription,$domainTabExtras)=split(/:/,$line,3); chomp($domainDescription); chomp($domainTabExtras); # the remaining field (primary lib server) is handled later $domainTabExtras = join(':',(split(/:/,$domainTabExtras))[0..5]); last; } } close(IN); last if ($domainDescription); } } if ((!$protocol) || (!$desiredhostname)) { foreach my $file (@hosts_files) { open(IN,'<'.$file); while(my $line = <IN>) { if ($line =~ /^\Q$perlvar{'lonHostID'}\E:\Q$perlvar{'lonDefDomain'}\E\:(?:access|library)\:([^:]+)\:(https?)/) { if (!$desiredhostname) { $desiredhostname = $1; } if (!$protocol) { $protocol = $2; chomp($protocol); } last; } } } } if (!$protocol) { $protocol = 'http'; } if (!$intdom) { foreach my $file (@hosts_files) { open(IN,'<'.$file); while(my $line = <IN>) { if ($line =~ /^\Q$perlvar{'lonHostID'}\E:\Q$perlvar{'lonDefDomain'}\E\:(?:access|library)\:[^:]+\:https?\:([^:]+)/) { $intdom = $1; chomp($intdom); last; } } } } my (%hostnames,%protocols); while(!$primaryLibServer && (@hosts_files || @domain_files)) { my $file = shift(@domain_files); open(IN,'<'.$file); while(my $line = <IN>) { if ($line =~ /^\Q$perlvar{'lonDefDomain'}\E\:/) { $primaryLibServer=(split(/:/,$line))[8]; chomp($primaryLibServer); } } close(IN); last if ($primaryLibServer); $file = shift(@hosts_files); open(IN,'<'.$file); while(my $line = <IN>) { if ($line =~ /^([^\:]+)\:\Q$perlvar{'lonDefDomain'}\E\:library\:([^\:]+)/) { push(@libservers,$1); $hostnames{$1} = $2; } } # make list unique @libservers = keys(%{{ map { $_ => 1 } (@libservers) }}); close(IN); if (@libservers == 1) { $primaryLibServer = $libservers[0]; } } # get hostname of primaryLibServer my ($primary_hostname,$primary_protocol); if ($primaryLibServer) { if ($hostnames{$primaryLibServer}) { $primary_hostname = $hostnames{$primaryLibServer}; $primary_protocol = $protocols{$primaryLibServer}; } else { foreach my $file (@poss_hosts_files) { open(IN,'<'.$file); while (my $line = <IN>) { if ($line =~ /^([^\:]+)\:\Q$perlvar{'lonDefDomain'}\E\:library\:([^\:]+):(https?)/) { if ($1 eq $primaryLibServer) { $primary_hostname = $2; $primary_protocol = $3; last; } } } close(IN); last if ($primary_hostname); } } } # implement editing logic below, interactively # update loncapa.conf until 18 is entered my $flag=0; # # Changes to 5, 6, and 14 not supported if configuration.db set on primary library server. # (requires either this machine to be primary library server or for LON-CAPA and Apache # to be running on primary library server). # my ($isprimary,$domconf,$url,$gotdomconf,$adminmail,$supportmail,$connectssl,%setbygui); if ($primaryLibServer eq $perlvar{'lonHostID'}) { $isprimary = 1; } else { unless ($primary_protocol eq 'https') { $primary_protocol = 'http'; } $url = $primary_protocol.'://'.$primary_hostname.'/cgi-bin/listdomconfig.pl'; } my %sslnames = &get_sslnames(); my %ssldesc = &get_ssldesc(); my $domconf = &get_domain_config($perlvar{'lonDefDomain'},$primaryLibServer,$isprimary, $url,$perlvarstatic); if (ref($domconf)) { $gotdomconf = 1; if (ref($domconf->{'contacts'}) eq 'HASH') { if (exists($domconf->{'contacts'}->{'adminemail'})) { $adminmail = $domconf->{'contacts'}->{'adminemail'}; } if (exists($domconf->{'contacts'}->{'supportemail'})) { $supportmail = $domconf->{'contacts'}->{'supportemail'}; } } if (ref($domconf->{'ssl'}) eq 'HASH') { foreach my $connect ('connto','connfrom') { if (ref($domconf->{'ssl'}->{$connect}) eq 'HASH') { my ($sslreq,$sslnoreq,$currsetting); my %contypes; foreach my $type ('dom','intdom','other') { my $key; if ($domconf->{'ssl'}->{'connect'}->{$type} eq 'req') { $key = 'yes'; } else { $key = 'no'; } if ($type eq 'dom') { $contypes{$key} .= ' own domain,'; } elsif ($type eq 'intdom') { $contypes{$key} .= ' own institution,'; } elsif ($type eq 'other') { $contypes{$key} .= ' other domains,'; } } foreach my $key (sort(keys(%contypes))) { $contypes{$key} =~ s/^\s//; $contypes{$key} =~ s/,$//; if ($key eq 'yes') { $currsetting .= ' Yes ('.$contypes{$key}.'),'; } elsif ($key eq 'no') { $currsetting .= ' No ('.$contypes{$key}.')'; } $currsetting =~ s/,$//; } if ($currsetting ne '') { $connectssl = $sslnames{$connect}.' -- '.$currsetting.' | '; } } } $connectssl =~ s/\s\|\s$//; } } if ($connectssl) { $setbygui{'securestatus'} = 1; $securestatus = 'Set by domain configuration via web GUI. Currently: '.$connectssl; } if ($adminmail) { $adminmail = 'Set by domain configuration via web GUI. Currently: '.$adminmail; $setbygui{'lonAdmEMail'} = 1; } else { $adminmail = $perlvar{'lonAdmEMail'}; } if ($supportmail) { $supportmail = 'Set by domain configuration via web GUI. Currently: '.$supportmail; $setbygui{'lonSupportEMail'} = 1; } else { $supportmail = $perlvar{'lonSupportEMail'}; } print "\nRetrieving status information for SSL key and certificates ...\n\n"; my ($certinfo,$lonkeystatus,$lonhostcertstatus,$lonhostnamecertstatus,$sslref) = &get_cert_status($perlvar{'lonHostID'},$perlvarstatic); print $certinfo; my %sslstatus; if (ref($sslref) eq 'HASH') { %sslstatus = %{$sslref}; } while (!$flag) { print(<<END); =============================================================================== This is now the current configuration of your machine. 1) Domain Name: $perlvar{'lonDefDomain'} 2) Domain Description: $domainDescription 3) Machine Name: $perlvar{'lonHostID'} 4) ID of primary library server for domain: $primaryLibServer 5) Server Administrator's E-mail Address: $adminmail 6) Support E-mail Address: $supportmail 7) Web Server Protocol (http or https): $protocol 8) Internet Domain Name: $intdom 9) Hostname: $desiredhostname 10) Role: $perlvar{'lonRole'} 11) Cache Expiration Time: $perlvar{'lonExpire'} (seconds) 12) Server Load: $perlvar{'lonLoadLim'} 13) User Load: $perlvar{'lonUserLoadLim'} 14) LON-CAPA "internal" connections: $securestatus 15) Private Key for SSL: $lonkeystatus 16) SSL Certificate for LON-CAPA server connections: $lonhostcertstatus 17) SSL Certificate for Content Replication: $lonhostnamecertstatus 18) Everything is correct up above END my @error; foreach my $v ($perlvar{'lonDefDomain'},$perlvar{'lonHostID'}) { if (length($v)>35) { push(@error,"Name $v too long"); } if (length($v)<2) { push(@error,"Name $v too short"); } if ($v=~/capa/i) { if ($v!~/^oucapa\d+$/ && ($v!~/^capa\d+$/ && $perlvar{'lonDefDomain'} eq 'uwsp')) { push(@error,"Name $v contains 'capa'"); } } foreach my $bad ('res','raw','userfiles','priv','adm','uploaded', 'editupload') { push(@error,"\nName $v reserved.") if $v eq $bad; } if ($v=~/[^\w\-.]/) { push(@error,"Name $v contains special characters"); } } if ($domainDescription =~ /^\s*$/) { push(@error,"Domain Description is blank."); } elsif ($domainDescription!~/^[\(\)\-\w\s,]+$/) { push(@error,"Domain Description contains special characters."); } foreach my $v ($perlvar{'lonExpire'},$perlvar{'lonLoadLim'}) { unless ($v=~/^[\d+\.]+$/) { push(@error,"Number expected instead of $v"); } } unless (($perlvar{'lonRole'} eq 'library') || ($perlvar{'lonRole'} eq 'access')) { push(@error,"Invalid Role"); } unless (($protocol eq 'http') || ($protocol eq 'https')) { push(@error,"Invalid Protocol (must be http or https"); } if (!defined($intdom)) { push(@error,"No internet domain name designated. Enter something like ustate.edu"); } elsif ($intdom !~ /[^.]+\.\w{2,6}$/) { push(@error,"Invalid Internet domain name (must be at least two levels separated by . - e.g., ustate.edu"); } if (!defined($primaryLibServer)) { if (@libservers > 0) { push(@error,"No primary library server ID designated. Choose from: ".join(',',sort(@libservers))); } else { push(@error,"No library servers in this domain (including current server)"); } } else { if (length($primaryLibServer)>35) { push(@error,"Primary Library Server ID: $primaryLibServer too long"); } if (length($primaryLibServer)<2) { push(@error,"Primary Library Server ID: $primaryLibServer too short"); } if ($primaryLibServer =~/capa/i) { if ($primaryLibServer!~/^oucapa\d+$/ && ($primaryLibServer!~/^capa\d+$/ && $perlvar{'lonDefDomain'} eq 'uwsp')) { push(@error,"Primary library server ID $primaryLibServer contains 'capa'") } } foreach my $bad ('res','raw','userfiles','priv','adm','uploaded', 'editupload') { push(@error,"Primary library server ID $primaryLibServer reserved.") if $primaryLibServer eq $bad; } if ($primaryLibServer=~/[^\w\-.]/) { push(@error,"Primary library server ID $primaryLibServer contains special characters"); } } my ($certsdir,$privkey,$connectcsr,$replicatecsr); $certsdir = $perlvarstatic->{'lonCertificateDirectory'}; $privkey = $perlvarstatic->{'lonnetPrivateKey'}; $connectcsr = $perlvarstatic->{'lonnetCertificate'}; $connectcsr =~ s/\.pem$/.csr/; $replicatecsr = $perlvarstatic->{'lonnetHostnameCertificate'}; $replicatecsr =~ s/\.pem$/.csr/; if (@error) { print "\n*** ERRORS: \n\t".join("\n\t",@error)."\n"; } print(<<END); ENTER A CHOICE OF 1-17 TO CHANGE, otherwise ENTER 18: END my $choice=<>; chomp($choice); if ($choice==1) { print(<<END); 1) Domain Name: $perlvar{'lonDefDomain'} ENTER NEW VALUE (this is an internal value used to identify a group of LON-CAPA machines, it must be alphanumerical, we suggest using a part of your actual DNS domain. For example, for the machine loncapa.msu.edu, we set the Domain to msu): END my $choice2=<>; chomp($choice2); $perlvar{'lonDefDomain'}=$choice2; } elsif ($choice==2) { print(<<END); 2) Domain Description: $domainDescription ENTER NEW VALUE (this should be a string that describes your domain, spaces and punctuation are fine except for ':'): END my $choice2=<>; chomp($choice2); $domainDescription=$choice2; } elsif ($choice==3) { print(<<END); 3) Machine Name: $perlvar{'lonHostID'} ENTER NEW VALUE (this will be the name of the machine in the LON-CAPA network it cannot contain any of '_' '-' '.' or ':'. We suggest that if you are in the domain 'example' and are the first library server you enter 'examplel1') : END my $choice2=<>; chomp($choice2); $perlvar{'lonHostID'}=$choice2; } elsif ($choice==4) { print(<<END); 4) ID of primary library server for domain: $primaryLibServer ENTER NEW VALUE (this will be the LON-CAPA Machine ID of a library server in your domain; it cannot contain any of '_' '-' '.' or ':'. This server will be where domain data which are not associated with any specific home library server will be stored (e.g., e-mail broadcast by Domain Coordinators to users in the domain). END my $choice2=<>; chomp($choice2); $primaryLibServer=$choice2; } elsif ($choice==5) { if ($setbygui{'lonAdmEMail'}) { print(<<END); 5) Server Administrator's E-mail Address: $adminmail Use the web GUI (as domain coordinator) to make changes after completing the UPDATE. END } else { print(<<END); 5) Server Administrator's E-mail Address: $perlvar{'lonAdmEMail'} ENTER NEW VALUE: END my $choice2=<>; chomp($choice2); $perlvar{'lonAdmEMail'}=$choice2; $adminmail=$perlvar{'lonAdmEMail'}; } } elsif ($choice==6) { if ($setbygui{'lonSupportEMail'}) { print(<<END); 6) Support E-mail Address: $supportmail Use the web GUI (as domain coordinator) to make changes after completing the UPDATE. END } else { print(<<END); 6) Support E-mail Address: $perlvar{'lonSupportEMail'} ENTER NEW VALUE: END my $choice2=<>; chomp($choice2); $perlvar{'lonSupportEMail'}=$choice2; $supportmail=$perlvar{'lonSupportEMail'}; } } elsif ($choice==7) { print(<<END); 7) Server Protocol (http or https): ENTER NEW VALUE: (this should be either 'http' or 'https' if in doubt set to 'http'): END my $choice2=<>; chomp($choice2); $protocol=$choice2; } elsif ($choice==8) { print(<<END); 8) Internet Domain Name of Institution ENTER NEW VALUE: END my $choice2=<>; chomp($choice2); $intdom=$choice2; } elsif ($choice==9) { print(<<END); 9) Hostname of Server/VM ENTER NEW VALUE: END my $choice2=<>; chomp($choice2); $desiredhostname=$choice2; } elsif ($choice==10) { print(<<END); 10) Role: $perlvar{'lonRole'} ENTER NEW VALUE (this should be either 'access' or 'library' if in doubt select 'library'): END my $choice2=<>; chomp($choice2); $perlvar{'lonRole'}=$choice2; } elsif ($choice==11) { print(<<END); 11) Cache Expiration Time: $perlvar{'lonExpire'} ENTER NEW VALUE (in seconds, 86400 is a reasonable value): END my $choice2=<>; chomp($choice2); $perlvar{'lonExpire'}=$choice2; } elsif ($choice==12) { print(<<END); 12) Server Load: $perlvar{'lonLoadLim'} ENTER NEW VALUE: END my $choice2=<>; chomp($choice2); $perlvar{'lonLoadLim'}=$choice2; } elsif ($choice==13) { print(<<END); 13) User Load: $perlvar{'lonUserLoadLim'} Numer of users that can login before machine is 'overloaded' ENTER NEW VALUE (integer value, 0 means there is no limit): END my $choice2=<>; chomp($choice2); $perlvar{'lonUserLoadLim'}=$choice2; } elsif ($choice==14) { if ($setbygui{'securestatus'}) { print(<<END); 14) Allow only secure connections: $securestatus Use the web GUI (as domain coordinator) to make changes after completing the UPDATE. END } else { print(<<END); 14) Allow only secure connections: $securestatus The Lon-CAPA communication daemons lonc and lond can be configured to allow only secure connections by default. POSSIBLE CHOICES: 1) allow only secure connections and don't connect to machines that can not be connected to securely 2) allow only secure connections but allow this machine to connect to machines that don't support secure connections 3) allow insecure connections to this machine but only allow connections to machines that support secure connections 4) allow insecure connections ENTER NEW VALUE (currently $securenum): END my $choice2=<>; chomp($choice2); if ($choice2 eq '1') { $perlvar{'loncAllowInsecure'}=0;$perlvar{'londAllowInsecure'}=0; } elsif ($choice2 eq '2') { $perlvar{'loncAllowInsecure'}=0;$perlvar{'londAllowInsecure'}=1; } elsif ($choice2 eq '3') { $perlvar{'loncAllowInsecure'}=1;$perlvar{'londAllowInsecure'}=0; } elsif ($choice2 eq '4') { $perlvar{'loncAllowInsecure'}=1;$perlvar{'londAllowInsecure'}=1; } ($securestatus,$securenum)=&securesetting(%perlvar); } } elsif ($choice==15) { if ($sslstatus{'key'} == 1) { print(<<END); 15) Private Key for SSL: $lonkeystatus POSSIBLE CHOICES: 1) overwrite existing key 2) make no change ENTER NEW VALUE END my $choice2=<>; chomp($choice2); if ($choice2 eq '1') { my $sslkeypass = &get_new_sslkeypass(); &make_key($certsdir,$privkey,$sslkeypass); } } elsif ($sslstatus{'key'} == 0) { print(<<END); 15) Private Key for SSL: $lonkeystatus END my $sslkeypass = &get_new_sslkeypass(); &make_key($certsdir,$privkey,$sslkeypass); print "\nRetrieving status information for SSL key and certificates ...\n\n"; ($certinfo,$lonkeystatus,$lonhostcertstatus,$lonhostnamecertstatus,$sslref) = &get_cert_status($perlvar{'lonHostID'},$perlvarstatic); if (ref($sslref) eq 'HASH') { %sslstatus = %{$sslref}; } } } elsif ($choice==16) { if (($sslstatus{'host'} == 1) || ($sslstatus{'host'} == 2) || ($sslstatus{'host'} == 3)) { print(<<END); 16) SSL Certificate for LON-CAPA server connections: $lonhostcertstatus POSSIBLE CHOICES: 1) create new certificate signing request with new key 2) create new certificate signing request with existing key 3) resend current certificate signing request 4) make no change ENTER NEW VALUE END my $choice2=<>; chomp($choice2); if (($choice2 eq '1') || ($choice2 eq '2')) { &ssl_info(); my $country = &get_country($desiredhostname); my $state = &get_state(); my $city = &get_city(); my $connectsubj = "/C=$country/ST=$state/O=$domainDescription/L=$city/CN=$perlvar{'lonHostID'}/OU=LONCAPA/emailAddress=$adminmail"; ($domainDescription,$country,$state,$city) = &confirm_locality($domainDescription,$country,$state,$city); my $sslkeypass; if ($choice2 eq '1') { $sslkeypass = &get_new_sslkeypass(); &make_key($certsdir,$privkey,$sslkeypass); } elsif ($choice2 eq '2') { $sslkeypass = &get_password('Enter existing password for SSL key'); &encrypt_key($certsdir,$privkey,$sslkeypass); } &make_host_csr($certsdir,$sslkeypass,$connectcsr,$connectsubj); &mail_csr('host',$lonCluster,$perlvar{'lonHostID'},$desiredhostname,$certsdir,$connectcsr,$replicatecsr,$perlvarstatic); print "\nRetrieving status information for SSL key and certificates ...\n\n"; ($certinfo,$lonkeystatus,$lonhostcertstatus,$lonhostnamecertstatus,$sslref) = &get_cert_status($perlvar{'lonHostID'},$perlvarstatic); if (ref($sslref) eq 'HASH') { %sslstatus = %{$sslref}; } } elsif ($choice2 eq '3') { if (-e "$certsdir/$connectcsr") { &mail_csr('host',$lonCluster,$perlvar{'lonHostID'},$desiredhostname,$certsdir,$connectcsr,$replicatecsr,$perlvarstatic); } } } elsif (($sslstatus{'host'} == 0) || ($sslstatus{'host'} == 4) || ($sslstatus{'host'} == 5)) { my $sslkeypass; if ($sslstatus{'key'} == 1) { print(<<END); 16) SSL Certificate for LON-CAPA server connections: $lonhostcertstatus POSSIBLE CHOICES: 1) create new certificate signing request with new key 2) create new certificate signing request with existing key 3) make no change ENTER NEW VALUE END my $choice2=<>; chomp($choice2); if ($choice2 eq '1') { $sslkeypass = &get_new_sslkeypass(); &make_key($certsdir,$privkey,$sslkeypass); } elsif ($choice2 eq '2') { $sslkeypass = &get_password('Enter existing password for SSL key'); &encrypt_key($certsdir,$privkey,$sslkeypass); } } else { print(<<END); 16) SSL Certificate for LON-CAPA server connections: $lonhostcertstatus END $sslkeypass = &get_new_sslkeypass(); } &ssl_info(); my $country = &get_country($desiredhostname); my $state = &get_state(); my $city = &get_city(); my $connectsubj = "/C=$country/ST=$state/O=$domainDescription/L=$city/CN=$perlvar{'lonHostID'}/OU=LONCAPA/emailAddress=$adminmail"; &make_host_csr($certsdir,$sslkeypass,$connectcsr,$connectsubj); &mail_csr('host',$lonCluster,$perlvar{'lonHostID'},$desiredhostname,$certsdir,$connectcsr,$replicatecsr,$perlvarstatic); print "\nRetrieving status information for SSL key and certificates ...\n\n"; ($certinfo,$lonkeystatus,$lonhostcertstatus,$lonhostnamecertstatus,$sslref) = &get_cert_status($perlvar{'lonHostID'},$perlvarstatic); if (ref($sslref) eq 'HASH') { %sslstatus = %{$sslref}; } } } elsif ($choice==17) { if (($sslstatus{'hostname'} == 1) || ($sslstatus{'hostname'} == 2) || ($sslstatus{'hostname'} == 3)) { print(<<END); 17) SSL Certificate for Content Replication: $lonhostnamecertstatus POSSIBLE CHOICES: 1) create new certificate signing request with new key 2) create new certificate signing request with existing key 3) resend current certificate signing request 4) make no change ENTER NEW VALUE END my $choice2=<>; chomp($choice2); if (($choice2 eq '1') || ($choice2 eq '2')) { &ssl_info(); my $country = &get_country($desiredhostname); my $state = &get_state(); my $city = &get_city(); my $replicatesubj = "/C=$country/ST=$state/O=$domainDescription/L=$city/CN=internal-$desiredhostname/OU=LONCAPA/emailAddress=$adminmail"; my $sslkeypass; if ($choice2 eq '1') { $sslkeypass = &get_new_sslkeypass(); &make_key($certsdir,$privkey,$sslkeypass); } elsif ($choice2 eq '2') { $sslkeypass = &get_password('Enter existing password for SSL key'); &encrypt_key($certsdir,$privkey,$sslkeypass); } &make_hostname_csr($certsdir,$sslkeypass,$replicatecsr,$replicatesubj); &mail_csr('hostname',$lonCluster,$perlvar{'lonHostID'},$desiredhostname,$certsdir,$connectcsr,$replicatecsr,$perlvarstatic); print "\nRetrieving status information for SSL key and certificates ...\n\n"; ($certinfo,$lonkeystatus,$lonhostcertstatus,$lonhostnamecertstatus,$sslref) = &get_cert_status($perlvar{'lonHostID'},$perlvarstatic); if (ref($sslref) eq 'HASH') { %sslstatus = %{$sslref}; } } elsif ($choice2 eq '3') { if (-e "$certsdir/$replicatecsr") { &mail_csr('hostname',$lonCluster,$perlvar{'lonHostID'},$desiredhostname,$certsdir,$connectcsr,$replicatecsr,$perlvarstatic); } } } elsif (($sslstatus{'hostname'} == 0) || ($sslstatus{'hostname'} == 4) || ($sslstatus{'hostname'} == 5)) { my $sslkeypass; if ($sslstatus{'key'} == 1) { print(<<END); 17) SSL Certificate for Content Replication: $lonhostnamecertstatus POSSIBLE CHOICES: 1) create new certificate signing request with new key 2) create new certificate signing request with existing key 3) make no change ENTER NEW VALUE END my $choice2=<>; chomp($choice2); if ($choice2 eq '1') { $sslkeypass = &get_new_sslkeypass(); &make_key($certsdir,$privkey,$sslkeypass); } elsif ($choice2 eq '2') { $sslkeypass = &get_password('Enter existing password for SSL key'); &encrypt_key($certsdir,$privkey,$sslkeypass); } } else { print(<<END); 17) SSL Certificate for Content Replication: $lonhostnamecertstatus END $sslkeypass = &get_new_sslkeypass(); } &ssl_info(); my $country = &get_country($desiredhostname); my $state = &get_state(); my $city = &get_city(); my $replicatesubj = "/C=$country/ST=$state/O=$domainDescription/L=$city/CN=internal-$desiredhostname/OU=LONCAPA/emailAddress=$adminmail"; &make_hostname_csr($certsdir,$sslkeypass,$replicatecsr,$replicatesubj); &mail_csr('hostname',$lonCluster,$perlvar{'lonHostID'},$desiredhostname,$certsdir,$connectcsr,$replicatecsr,$perlvarstatic); print "\nRetrieving status information for SSL key and certificates ...\n\n"; ($certinfo,$lonkeystatus,$lonhostcertstatus,$lonhostnamecertstatus,$sslref) = &get_cert_status($perlvar{'lonHostID'},$perlvarstatic); if (ref($sslref) eq 'HASH') { %sslstatus = %{$sslref}; } } } elsif (($choice==18) && (!@error)) { $flag=1; } else { print "Invalid input.\n"; } } open(OUT,">$confdir$filename") or die("Cannot output to $confdir$filename\n"); foreach my $key (keys %perlvar) { my $value=$perlvar{$key}; my $line = "PerlSetVar $key $value"; if ($value eq '') { $line = '#'.$line; } print(OUT <<END) unless ($perlvarstatic->{$key}); $line END } close(OUT); loncom/hosts.tab unless (-l "") { if ($desiredhostname eq '') { my $hostname=`hostname -f`;chomp($hostname); $desiredhostname = $hostname; } my $date=`date -I`; chomp($date); my $lonHostID=$perlvar{'lonHostID'}; $lonHostID=~s/[^\w\-.]//g; my $lineexistflag=0; my $hostidexistflag=0; my $line2insert=<<END; $perlvar{'lonHostID'}:$perlvar{'lonDefDomain'}:$perlvar{'lonRole'}:$desiredhostname:$protocol:$intdom END if (!$domainTabExtras) { $domainTabExtras=':::::'; } my $domaininsert="$perlvar{'lonDefDomain'}:$domainDescription:$domainTabExtras:$primaryLibServer\n"; if ($lonCluster eq 'standalone') { open(OUT,'>../'.$lonCluster.'_hosts.tab') or die('file generation error'); print(OUT $line2insert); print OUT ("^$desiredhostname:$protocol\n"); close(OUT); open(OUT,'>../'.$lonCluster.'_dns_hosts.tab') or die('file generation error'); print(OUT $line2insert); close(OUT); open(OUT,'>../'.$lonCluster.'_domain.tab') or die('file generation error'); print(OUT $domaininsert); close(OUT); open(OUT,'>../'.$lonCluster.'_dns_domain.tab') or die('file generation error'); print(OUT $domaininsert); close(OUT); } if ($flag==1) { `rm -f ../hosts.tab`; `rm -f ../dns_hosts.tab`; `ln -s ${lonCluster}_dns_hosts.tab ../dns_hosts.tab`; open(IN,'<../'.$lonCluster.'_dns_hosts.tab'); while(my $line = <IN>) { if ($line =~ /^\Q$line2insert\E$/) { $lineexistflag=1; } if ($line =~ /^\Q$lonHostID\E\:/) { $hostidexistflag=1; } } close(IN); if ($hostidexistflag and !$lineexistflag) { print <<END; WARNING: $lonHostID already exists inside loncapa/loncom/${lonCluster}_dns_hosts.tab. The entry inside ${lonCluster}_dns_hosts.tab does not match your settings. An entry inside ${lonCluster}_hosts.tab will be made with your new values. END `grep -v "$lonHostID:" ../${lonCluster}_hosts.tab > ../new_${lonCluster}_hosts.tab`; open(OUT,'>>../new_'.$lonCluster.'_hosts.tab') or die("cannot open loncom/${lonCluster}_hosts.tab for output\n"); print(OUT $line2insert); close(OUT); `ln -s new_${lonCluster}_hosts.tab ../hosts.tab`; # email appropriate message `echo "REPLACE:$lonCluster:$lonHostID:$date:$line2insert" | mail -s "REPLACE:$lonCluster:$lonHostID:$protocol:$intdom:$date" installrecord\@mail.lon-capa.org`; } elsif ($hostidexistflag and $lineexistflag) { print <<END; Entry exists in ${lonCluster}_dns_hosts.tab. Making duplicate entry in ${lonCluster}_hosts.tab END `grep -v "$lonHostID:" ../${lonCluster}_hosts.tab > ../new_${lonCluster}_hosts.tab`; open(OUT,'>>../new_'.$lonCluster.'_hosts.tab') or die("cannot open loncom/${lonCluster}_hosts.tab for output\n"); print(OUT $line2insert); close(OUT); `ln -s new_${lonCluster}_hosts.tab ../hosts.tab`; # email appropriate message `echo "STABLEUPDATE:$lonCluster:$lonHostID:$date:$line2insert" | mail -s "STABLEUPDATE:$lonCluster:$lonHostID:$protocol:$intdom:$date" installrecord\@mail.lon-capa.org`; } elsif (!$hostidexistflag and !$lineexistflag) { print <<END; New entry for $lonCluster. END `cat ../${lonCluster}_hosts.tab > ../new_${lonCluster}_hosts.tab`; open(OUT,'>>../new_'.$lonCluster.'_hosts.tab') or die("cannot open loncom/new_${lonCluster}_hosts.tab for output\n"); print(OUT $line2insert); close(OUT); `ln -s new_${lonCluster}_hosts.tab ../hosts.tab`; # email appropriate message `echo "INSERT:$lonCluster:$lonHostID:$date:$line2insert" | mail -s "INSERT:$lonCluster:$lonHostID:$protocol:$intdom:$date" installrecord\@mail.lon-capa.org`; } } $lineexistflag=0; my $domainexistflag=0; if ($flag==1) { `rm -f ../domain.tab`; `rm -f ../dns_domain.tab`; `ln -s ${lonCluster}_dns_domain.tab ../dns_domain.tab`; open(IN,'<../'.$lonCluster.'_dns_domain.tab'); while(my $line = <IN>) { if ($line =~/^\Q$domaininsert\E$/) { $lineexistflag=1; } if ($line =~/^\Q$perlvar{'lonDefDomain'}\E\:/) { $domainexistflag=1; } } close(IN); if ($domainexistflag and !$lineexistflag) { print <<END; WARNING: $perlvar{'lonDefDomain'} already exists inside loncapa/loncom/${lonCluster}_dns_domain.tab. The entry inside ${lonCluster}_dns_domain.tab does not match your settings. An entry will be made in inside ${lonCluster}_domain.tab with your new values. END `grep -v "$perlvar{'lonDefDomain'}:" ../${lonCluster}_domain.tab > ../new_${lonCluster}_domain.tab`; open(OUT,'>>../new_'.$lonCluster.'_domain.tab') or die("cannot open loncom/${lonCluster}_domain.tab for output\n"); print(OUT $domaininsert); close(OUT); `ln -s new_${lonCluster}_domain.tab ../domain.tab`; # email appropriate message `echo "REPLACEdom:$lonCluster:$lonHostID:$date:$domaininsert" | mail -s "REPLACEdom:$lonCluster:$lonHostID:$date" installrecord\@mail.lon-capa.org`; } elsif ($domainexistflag and $lineexistflag) { `grep -v "$perlvar{'lonDefDomain'}:" ../${lonCluster}_domain.tab > ../new_${lonCluster}_domain.tab`; open(OUT,'>>../new_'.$lonCluster.'_domain.tab') or die("cannot open loncom/${lonCluster}_domain.tab for output\n"); print(OUT $domaininsert); close(OUT); print <<END; Entry exists in ${lonCluster}_dns_domain.tab. Making duplicate entry in ${lonCluster}_domain.tab END `ln -s new_${lonCluster}_domain.tab ../domain.tab`; # email appropriate message `echo "STABLEUPDATEdom:$lonCluster:$lonHostID:$date:$domaininsert" | mail -s "STABLEUPDATEdom:$lonCluster:$lonHostID:$date" installrecord\@mail.lon-capa.org`; } elsif (!$domainexistflag and !$lineexistflag) { print <<END; New entry for $lonCluster. END `cat ../${lonCluster}_domain.tab > ../new_${lonCluster}_domain.tab`; open(OUT,'>>../new_'.$lonCluster.'_domain.tab') or die("cannot open loncom/new_${lonCluster}_domain.tab for output\n"); print(OUT $domaininsert); close(OUT); `ln -s new_${lonCluster}_domain.tab ../domain.tab`; # email appropriate message `echo "INSERTdom:$lonCluster:$lonHostID:$date:$domaininsert" | mail -s "INSERTdom:$lonCluster:$lonHostID:$date" installrecord\@mail.lon-capa.org`; } } }