#!/usr/bin/perl
# change the above line to point to the perl program
################################################################################
# system_webwork_setup.pl
#
# This file changes the permissions to the correct ones in each of the
# subdirectories and the main course directory. It also modifies Global.pm and
# the various perl scripts.
#
# $Id: system_webwork_setup.pl 2 2001-06-14 17:08:51Z sam $
################################################################################
#################### initialization
require 5.000;
use strict;
use Cwd;
use File::Copy;
# define built-in defaults
my $DEFAULT_CHROOT_DIR = '/www/webwork-root';
my $DEFAULT_PERL_PATH = '/usr/bin/perl';
my $DEFAULT_CGI_URL = '/cgi-bin/webwork/system/';
my $DEFAULT_HTML_URL = '/webwork_system_html';
# define code strings
my $CGI_DEBUG_TAG = 'WeBWorKCGIDebugURL';
my $CGI_NODEBUG_TAG = 'WeBWorKCGINoDebugURL';
my $MAIN_DIR_TAG = 'mainWeBWorKDirectory';
# scope and undefine setup variables
our $no_prompts = undef;
our $system_setup_mode = undef;
our $chrootDir = undef;
our $mainDir = undef;
our $perlPath = undef;
our $cgiURL = undef;
our $htmlURL = undef;
our $groupName = undef;
our $update_stuff_in_courses = undef;
our $chgrp_files_and_dirs = undef;
our $chmod_files_and_dirs = undef;
our $local_preprocessor = undef;
our $local_postprocessor = undef;
# read defaults in from defaults file
my $DEFAULTS_FILE = ((getpwuid $<)[7]) . '/system_webwork_setup.defaults';
if(-e $DEFAULTS_FILE) {
print "Reading defaults file...";
require $DEFAULTS_FILE;
print " done.\n";
}
#################### text strings
my $INTRO_TEXT = q{
+----------------------+
| System Webwork Setup |
+----------------------+
This script is used to setup the main WeBWorK system. It will create
initialization files, set groups and permissions for files and directories,
and modify files. A "demo" or "working" version of the system can be setup.
This script must be run from the the main WeBWorK system directory, for
example from /www/webwork/system or /www/webwork-root/webwork/system
You will need the following information:
1) If the web server is running in a chroot environment, you need to know
the directory the server will see as root, usually something like
/www/webwork-root
2) The path to perl on your system. Usually this is something like
/usr/bin/perl or /usr/local/bin/perl
3) The group (e.g. wwadmin) containing the user names of anyone who has the
authority to modify the webwork system files. Who ever runs this script must
be a member of this group. This should be set up by your system
administrator before running this script. (Note: This is not required for a
demo version.)
4) The cgi WeBWorK URL. Usually this is something like
http://www.math.rochester.edu/cgi-bin/webwork/system or
http://webwork.math.rochester.edu/cgi-bin/webwork/system. In the above
cases, you can just enter /cgi-bin/webwork/system which is prefered but if
you do, don't forget the initial / .
5) The html WeBWorK URL. Usually this is something like
http://www.math.rochester.edu/webwork_system_html. Again in this case
/webwork_system_html is prefered.
See the html document
webwork/system/system_html/docs/techdescription/settingupsystem.html for
detailed explanations. The examples here are taken from those directions.
};
my $MODE_TEXT = q{
You can set up a "working" or a "demo" WeBWorK system. A "demo" system
should only be used as a sample system, never for a system that will be used
with actual courses with real students. The main difference between a
"working" version and a "demo" version is that in a "working" version you
will be promped to enter a group (e.g. wwadmin) where as in a "demo"
version, the group will be set yo your own default group (e.g. fac). Anyone
in the group will have permission to modify all webwork files. You can set
up "working" and "demo" courses under either a "working" or a "demo" system,
but normally you would not set up a "working" course under a "demo" system.
};
my $CHROOT_TEXT = q{
If your web server is set to run chrooted (that is, it sees some directory
other than "/" as the root directory), WeBWorK needs to know what that
directory is so that it can set file paths accordingly.
};
my $PERL_TEXT = q{
WeBWorK needs to know the path of your perl binary, so that this information
can be used in the headers of cgi scripts. If you were able to run this
script by typing "./system_webwork_setup.pl", the path to a perl binary is
listed on the first line of this script.
};
my $CGI_URL_TEXT = q{
In order for generated HTML to be able to invoke CGI scripts, WeBWorK needs
to know the URL which points to the main WeBWorK system cgi subdirectory.
For example, http://www.math.rochester.edu.edu/cgi-bin/webwork/system might
be a valid CGI URL. If both static HTML and CGIs reside on the same host
(which is true in most cases), you can omit the
http://www.math.rochester.edu section of the URL. Make sure to include a
leading slash in this case.
};
my $HTML_URL_TEXT = q{
WeBWorK also needs to know the URL of the main HTML directory, the system
html subdirectory. For example,
http://www.math.rochester.edu/webwork_system_html or /webwork_system_html.
};
my $GROUP_TEXT = q{
WeBWorK needs to know what the admin group is. This group should have been
set up by your system administrator and must contain at least your user ID.
All files and directories created will have this as their group.
};
my $COURSE_PERMS_TEXT = q{
You have the option to set permissions for the courses directory. If this is
an inital setup, you should probably do so. If this is not an initial setup,
the courses directory is already set up, or the courses directory is shared
between WeBWorK installations, your probably shouldn't.
};
my $CHGRP_TEXT = q{
You have to option to set the group for all system files and directories. If
this is an initial setup, you should probably do this. If this is not an
initial setup, or you have customized the way system files should be
grouped, you probably shoudn't.
};
my $CHMOD_TEXT = q{
You have to option to set the permissions for all system files and
directories. If this is an initial setup, you should probably do this. If
this is not an initial setup, or you have customized the permissions for the
system files, you probably shoudn't.
};
my $CONFIRM_TEXT = q{
Now that I have the necessary information, I can begin modifying the WeBWorK
system files.
};
my $DONE_TEXT = q{
The system setup script is done. Your WeBWorK system directory is now set up
correctly.
};
################################################################################
########## Ask some questions, perform some logic. #############################
################################################################################
#################### introduction
my $temp;
unless($no_prompts) {
page($INTRO_TEXT);
$temp = questionChar("Do you want to continue with setup?", 'y', 'y', 'n');
exit unless $temp eq 'y';
}
print "Okay, here we go...\n";
#################### working or demo?
unless(defined $system_setup_mode) {
page($MODE_TEXT);
$temp = questionChar("Shall we set up a working version or a demo version?", 'w', 'w', 'd');
$system_setup_mode = "working" if $temp eq 'w';
$system_setup_mode = "demo" if $temp eq 'd';
}
print "System setup mode is: $system_setup_mode\n";
#################### chroot directory
unless(defined $chrootDir) {
page($CHROOT_TEXT);
$temp = questionChar("Is the web server running in a chroot environment?", 'n', 'y', 'n');
if ($temp eq 'y') {
$chrootDir = questionString("What directory does the web server see as root?", $DEFAULT_CHROOT_DIR);
$chrootDir =~ s|/$||; # remove trailing slash
}
}
print "Chroot directory is: $chrootDir\n" if $chrootDir;
print "The web server is not chrooted.\n" unless $chrootDir;
#################### main directory
if(defined $mainDir) {
chdir $mainDir;
} else {
use Cwd;
$mainDir = cwd();
$mainDir or die "Sorry, I couldn't get the working directory. Make sure this script is run from an interactive shell.\n";
$mainDir .= '/' unless $mainDir =~ m|/$|; # ensure trailing slash
}
if($chrootDir) {
# make mainDir relative to chrootDir
$mainDir =~ s/^$chrootDir//;
#$mainDir = "$mainDir";
}
print "We'll use $mainDir as WeBWorK's home";
print " for chrooted scripts\nand $chrootDir$mainDir for regular scripts" if $chrootDir;
print ".\n";
#################### perl path
unless(defined $perlPath) {
page($PERL_TEXT);
$perlPath = questionString("What is the full path to PERL?", $DEFAULT_PERL_PATH);
}
print "Path to PERL binary is: $perlPath\n";
#################### CGI URL
unless(defined $cgiURL) {
page($CGI_URL_TEXT);
while (1) {
$cgiURL = questionString("What is the CGI URL?", $DEFAULT_CGI_URL);
if( ($cgiURL =~ m|^/|) or ($cgiURL =~ m|^http://|) ) {
last;
} else {
$temp = questionChar("That doesn't look like a valid URL. Would you like to use it anyway?", 'n', 'y', 'n');
last if $temp eq 'y';
}
}
$cgiURL .= "/" unless $cgiURL =~ m"/$"; # ensure trailing slash
}
print "CGI URL is: $cgiURL\n";
#################### HTML URL
unless(defined $htmlURL) {
page($HTML_URL_TEXT);
while (1) {
$htmlURL = questionString("What is the HTML URL?", $DEFAULT_HTML_URL);
if( ($htmlURL =~ m|^/|) or ($htmlURL =~ m|^http://|) ) {
last;
} else {
$temp = questionChar("That doesn't look like a valid URL. Would you like to use it anyway?", 'n', 'y', 'n');
last if $temp eq 'y';
}
}
$htmlURL .= "/" unless $htmlURL =~ m"/$" ;
}
print "HTML URL is: $htmlURL\n";
#################### admin group
unless(defined $groupName) {
my ($userName, $userGID) = (getpwuid $<)[0,3];
my $userGroupName = (getgrgid $userGID)[0];
if ($system_setup_mode eq 'demo') {
# in demo mode, the group is set to the user's primary group
$groupName = $userGroupName;
} else {
# in working mode, we get to chose
page($GROUP_TEXT);
my $validGroup = 0;
while(1) {
$groupName = questionString("What is the admin group name?", $userGroupName);
my @members = split / /, (getgrnam $groupName)[3];
if($groupName eq $userGroupName) {
print "$groupName is $userName's primary group. Good.\n";
} elsif(grep /$userName/, @members) {
print "$userName is a member of $groupName. Good.\n";
last;
} elsif($< == 0) { # we're root!
print "$userName isn't a member of $groupName, but you're root, so who cares?\n";
last;
} else {
print "That group is not valid. Please make sure the group exists and you are a member.\n";
}
}
}
}
print "Admin group is: $groupName\n";
#################### chmod courses directory
unless(defined $update_stuff_in_courses) {
page($COURSE_PERMS_TEXT);
$temp = questionChar("Do you want to set default $system_setup_mode permissions for the courses directory?", 'y', 'y', 'n');
$update_stuff_in_courses = ($temp eq 'y');
}
print "Permissions ", ($update_stuff_in_courses ? "will" : "will not"), " be set for the courses directory.\n";
#################### chgrp files/directories
$system_setup_mode eq "demo" and $chgrp_files_and_dirs = 1;
unless(defined $chgrp_files_and_dirs) {
page($CHGRP_TEXT);
$temp = questionChar("Do you want to set the group for system files and directories?", 'y', 'y', 'n');
$chgrp_files_and_dirs = ($temp eq 'y');
}
print "Group ", ($chgrp_files_and_dirs ? "will" : "will not"), " be set for system files and directories.\n";
#################### chmod files/directories
$system_setup_mode eq "demo" and $chmod_files_and_dirs = 1;
unless(defined $chmod_files_and_dirs) {
page($CHMOD_TEXT);
$temp = questionChar("Do you want to set the permissions for system files and directories?", 'y', 'y', 'n');
$chmod_files_and_dirs = ($temp eq 'y');
}
print "Permissions ", ($chmod_files_and_dirs ? "will" : "will not"), " be set for system files and directories.\n";
#################### make sure we want to actually do this
unless($no_prompts) {
print $CONFIRM_TEXT;
$temp = questionChar("Do you want to continue with setup?", 'y', 'y', 'n');
exit unless $temp eq 'y';
}
print "\Going to make changes now...\n\n";
################################################################################
########## Now we start changing things... #####################################
################################################################################
#################### run local preprocessor
if(defined $local_preprocessor) {
print "Executing local preprocessor...\n";
&$local_preprocessor;
print "Done with local preprocessor.\n";
}
#################### update Global.pm
#needs: $chrootDir, $mainDir, $cgiURL, $htmlURL
# fix all this chrootDir/mainDir crap
my $fullMainDir;
if ($chrootDir) {
$fullMainDir = qq(defined(\$ENV{HTTP_HOST}) ? '$mainDir': '$chrootDir$mainDir';);
} else {
$fullMainDir = qq('$mainDir';);
}
# okay.
chdir "lib";
-e 'Global.pm' or die "Global.pm doesn't exist! There's no point in continuing.\n";
if(-e 'Global.pm.bak1') {
print "Copying Global.pm.bak1 -> Global.pm.bak2\n";
copy('Global.pm.bak1', 'Global.pm.bak2');
}
print "Copying Global.pm -> Global.pm.bak1\n";
copy('Global.pm', 'Global.pm.bak1');
open OLD_GLOBAL, "Global.pm.bak1";
open NEW_GLOBAL, ">Global.pm";
while (<OLD_GLOBAL>) {
if (/^\$mainDirectory/) {
print NEW_GLOBAL "\$mainDirectory = $fullMainDir\n";
print "\t\$mainDirectory = $fullMainDir\n";
} elsif (/\#$CGI_DEBUG_TAG$/) {
print NEW_GLOBAL "\#\$cgiWebworkURL = \"$cgiURL\"\; \#$CGI_DEBUG_TAG\n";
print "\t\#\$cgiWebworkURL = \"$cgiURL\"\; \#$CGI_DEBUG_TAG\n";
} elsif (/\#$CGI_NODEBUG_TAG$/) {
print NEW_GLOBAL "\$cgiWebworkURL = \"${cgiURL}cgi-scripts/\"\; \#$CGI_NODEBUG_TAG\n";
print "\t\$cgiWebworkURL = \"${cgiURL}cgi-scripts/\"\; \#$CGI_NODEBUG_TAG\n";
} elsif (/^\$htmlWebworkURL/) {
print NEW_GLOBAL "\$htmlWebworkURL = \"$htmlURL\"\;\n";
print "\t\$htmlWebworkURL = \"$htmlURL\"\;\n";
} else {
print NEW_GLOBAL $_;
}
}
close NEW_GLOGAL;
close OLD_GLOBAL;
chmod(0644, "Global.pm");
chdir "..";
print "Done updating Global.pm\n\n";
#################### update couses stuff
# uses: $update_stuff_in_courses
if($update_stuff_in_courses) {
print "Setting permissions for ../courses directory.\n\n";
chmod(0755, '../courses') or warn "Warning: I can't set permissions for ../courses directory. It's possible that the directory doesn't exist or you don't have permission to change it.\n";
}
#################### update #! and use lines
# uses: $chrootDir, $mainDir, $perlPath
# i must fix this some day
print "Fixing #! and \"use\" lines...\n";
my $TAG = "mainWeBWorKDirectory";
my $USE_LINE;
if ($chrootDir) { $USE_LINE = "use lib ('${mainDir}lib/', '${chrootDir}${mainDir}lib/'); \# $TAG\n"; }
else { $USE_LINE = "use lib '${mainDir}lib/'; \# $TAG\n"; }
# if we want to use absolute use lib paths, we have to include both the
# chrooted mainDir and non-chrooted mainDir for chrooted httpds
# fix up the *.pl files
foreach my $dir ('cgi/cgi-scripts', 'scripts', 'courseScripts') {
foreach my $file (<${dir}/*.pl>) {
fixFile($file, $USE_LINE);
}
}
# fix up the course_webwork_setup.pl file and some modules.
my @files = ('lib/PGtranslator.pm', 'lib/Auth.pm', 'lib/capa2PG.pm');
push @files,'../courses/demoCourse/course_webwork_setup.pl' if $update_stuff_in_courses;
foreach my $file (@files) {
if($file eq 'lib/PGtranslator.pm') { fixFile($file, $USE_LINE); }
else { fixFile($file); }
}
sub fixFile
{
my ($file, $use_line) = @_;
# read the file
open FILE, $file || die "CAN'T READ $file!\n Fix the problem and run the setup script again";
my @lines = <FILE>;
close FILE || die "CAN'T CLOSE $file!\n Fix the problem and run the setup script again";
# fix perl path
my $num = 0;
$num = $lines[0] =~ s/^#!.*/#!$perlPath/g; # fix #!... line
warn "couldn't fix #! line in $file\n" unless $num;
# fix use lines
if($use_line) {
$num = 0;
foreach (@lines) {
if (/^.*\#\s*\Q$TAG\E.*$/) {
$num++;
$_ = $use_line;
}
}
warn "fixed $num 'use lib' lines in $file\n" if $num > 1;
}
# write the file
open FILE, ">$file" || die "CAN'T WRITE $file!\n Fix the problem and run the setup script again";
print FILE @lines;
close FILE || die "CAN'T CLOSE (writing) $file!\n Fix the problem and run the setup script again";
}
print "done fixing #! and \"use\" lines.\n\n";
#################### chgrp system stuff
# uses: $chgrp_files_and_dirs, $groupName
if($chgrp_files_and_dirs) {
print "Setting group on system files and directories...\n";
# R=recursive, P=don't follow symlinks
system "chgrp -PR $groupName .";
print "Done setting group.\n\n";
}
#################### chmod system stuff
# uses: $chmod_files_and_dirs
if($chmod_files_and_dirs) {
print "Setting permissions on system files and directories for $system_setup_mode mode...\n";
if ($system_setup_mode eq "demo") {
# get some general permissions for files and directories
system "find . -type d -print0 | xargs -0 chmod 0711";
system "find . -type f -print0 | xargs -0 chmod 0644";
# add executable privs to scripts
system "find cgi scripts -type f -print0 | xargs -0 chmod +x";
# give everyone write access to logs
# (we should probably just be chowning the log directory to the webserver)
system "chmod o+w logs/*";
# make this script executable and safe
system "chmod u+x,o- system_webwork_setup.pl"
} else {
# get some general permissions for files and directories
system "find . -type d -print0 | xargs -0 chmod 0771";
system "find . -type f -print0 | xargs -0 chmod 0664";
# add executable privs to scripts
system "find cgi scripts -type f -print0 | xargs -0 chmod +x";
# give everyone write access to logs
# (we should probably just be chowning the log directory to the webserver)
system "chmod o+w logs/*";
# make this script executable and safe
system "chmod ug+x,o- system_webwork_setup.pl"
}
print "done setting permissions.\n\n";
}
#################### fix up the documemtation html files
# uses: $htmlURL, $cgiURL
print "Fixing image, cgi-bin, and ref lines in documentation html files...\n";
foreach my $dir ('system_html/helpFiles') {
foreach my $file (<${dir}/*.html>) {
open FILE, $file || die "CAN'T READ $file!\n Fix the problem and run the setup script again";
my @lines = <FILE>;
close FILE || die "CAN'T CLOSE $file!\n Fix the problem and run the setup script again";
foreach my $line (@lines) {
$line =~ s|<IMG SRC=".*?images/|<IMG SRC="${htmlURL}images/|g; # fix "images" line
$line =~ s|<A HREF=".*?feedback.pl">|<A HREF="${cgiURL}feedback.pl">|g; # fix "cgi-bin" line
$line =~ s|<A HREF=".*?docs/">|<A HREF="${htmlURL}docs/">|g; # fix "Ref" line
}
open FILE, ">$file" || die "CAN'T WRITE $file!\n Fix the problem and run the setup script again";
print FILE @lines;
close FILE || die "CAN'T CLOSE (writing) $file!\n Fix the problem and run the setup script again";
}
}
print "done fixing documentation files.\n\n";
#################### run local postprocessor
if(defined $local_postprocessor) {
print "Executing local preprocessor...\n";
&$local_postprocessor;
print "Done with local preprocessor.\n";
}
#################### finish up
page($DONE_TEXT);
################################################################################
sub page
{
my @string_lines = split /^/, shift; #/
# not really optimal, but we're going to assume a constant screen height.
my $SCREEN_HEIGHT = 20;
while(@string_lines) {
print join "", @string_lines[0..$SCREEN_HEIGHT-1];
if(scalar @string_lines >= $SCREEN_HEIGHT) {
print "\n[Press ENTER to continues...]";
<STDIN>;
print "\n";
}
@string_lines = @string_lines[$SCREEN_HEIGHT..$#string_lines];
}
}
sub questionChar
{
my ($question, $default, @valid) = @_;
my $answer;
do {
print $question, " ";
foreach (@valid) {
$_ eq $default and print "[";
print $_;
$_ eq $default and print "]";
}
print " ";
$answer = <STDIN>;
$answer =~ s/^\s*//;
$answer = substr $answer, 0, 1;
$answer = lc $answer;
$answer or $answer = $default;
} while (not grep(/$answer/, @valid));
return $answer;
}
sub questionString
{
my ($question, $default, $emptyOK) = @_;
my $answer;
print $question, " [", $default, "] ";
$answer = <STDIN>;
chomp $answer;
$answer =~ s/^\s*//;
$answer or $answer = $default;
return $answer;
}