More work on modularizing the installation scripts. Also adding more extensive
[koha.git] / installer.pl
1 #!/usr/bin/perl -w # please develop with -w
2
3 #use diagnostics;
4
5 use Install;
6 use strict; # please develop with the strict pragma
7
8
9 $::language='en';
10
11 if ($<) {
12     print "\n\nYou must run koha.upgrade as root.\n\n";
13     exit;
14 }
15 unless ($< == 0) {
16     print "You must be root to run this script.\n";
17     exit 1;
18 }
19
20 $::kohaversion=`cat koha.version`;
21 chomp $::kohaversion;
22
23
24 if ($::kohaversion =~ /RC/) {
25     releasecandidatewarning();
26 }
27
28 if (-e "/etc/koha.conf") {
29     $::installedversion=`grep kohaversion= /etc/koha.conf`;
30     chomp $::installedversion;
31     $::installedversion=~m/kohaversion=(.*)/;
32     $::installedversion=$1;
33     my $installedversionmsg;
34     if ($::installedversion) {
35         $installedversionmsg=getmessage('KohaVersionInstalled', [$::installedversion]);
36     } else {
37         $installedversionmsg=getmessage('KohaUnknownVersionInstalled');
38     }
39
40     my $message=getmessage('KohaAlreadyInstalled', [$::kohaversion, $installedversionmsg]);
41     showmessage($message, 'none');
42     exit;
43 }
44
45 my $continuingmsg=getmessage('continuing');
46
47 my $message=getmessage('WelcomeToKohaInstaller');
48 my $answer=showmessage($message, 'yn');
49
50 if ($answer eq "Y" || $answer eq "y") {
51         print $continuingmsg;
52     } else {
53     print qq|
54 This installer currently does not support a completely automated 
55 setup.
56
57 Please be sure to read the documentation, or visit the Koha website 
58 at http://www.koha.org for more information.
59 |;
60     exit;
61 };
62
63 my $input;
64 $::domainname = `hostname -d`;
65 chomp $::domainname;
66
67
68 # Check for missing Perl Modules
69
70 checkperlmodules();
71
72
73 # Ask for installation directories
74
75 my ($opacdir, $intranetdir) = getinstallationdirectories();
76
77
78
79
80 $::etcdir = '/etc';
81
82
83
84 $::dbname = 'Koha';
85 $::hostname = 'localhost';
86 $::user = 'kohaadmin';
87 $::pass = '';
88
89
90 getdatabaseinfo();
91
92
93
94 getapacheinfo();
95
96
97 print "user: $::httpduser\n";
98 print "conf:  $::realhttpdconf\n";
99 exit;
100
101 getapachevhostinfo();
102
103 #
104 # Update Apache Conf File.
105 #
106 #
107
108 my $logfiledir=`grep ^ErrorLog $$::realhttpdconf`;
109 chomp $logfiledir;
110
111 if ($logfiledir) {
112     $logfiledir=~m#ErrorLog (.*)/[^/]*$#;
113     $logfiledir=$1;
114 }
115
116 unless ($logfiledir) {
117     $logfiledir='logs';
118 }
119 print qq|
120
121 UPDATING APACHE.CONF
122 ====================
123
124 |;
125
126
127 print "Checking for modules that need to be loaded...\n";
128 my $httpdconf='';
129 my $envmodule=0;
130 my $includesmodule=0;
131 open HC, $$::realhttpdconf;
132 while (<HC>) {
133     if (/^\s*#\s*LoadModule env_module /) {
134         s/^\s*#\s*//;
135         print "  Loading env_module in httpd.conf\n";
136         $envmodule=1;
137     }
138     if (/^\s*#\s*LoadModule includes_module /) {
139         s/^\s*#\s*//;
140         print "  Loading includes_module in httpd.conf\n";
141     }
142     if (/\s*LoadModule includes_module / ) {
143         $includesmodule=1;
144     }
145     $httpdconf.=$_;
146 }
147
148 my $apachebackupmade=0;
149 if ($envmodule || $includesmodule) {
150     system("mv -f $$::realhttpdconf $$::realhttpdconf\.prekoha");
151     $apachebackupmade=1;
152     open HC, ">$$::realhttpdconf";
153     print HC $httpdconf;
154     close HC;
155 }
156
157
158 if (0) {
159 #if (`grep 'VirtualHost $servername' $$::realhttpdconf`) {
160 #    print qq|
161 #$$::realhttpdconf appears to already have an entry for Koha
162 #Virtual Hosts.  You may need to edit $$::realhttpdconf
163 #if anything has changed since it was last set up.  This
164 #script will not attempt to modify an existing Koha apache
165 #configuration.
166 #
167 #|;
168     print "Press <ENTER> to continue...";
169     <STDIN>;
170     print "\n";
171 } else {
172     unless ($apachebackupmade) {
173         system("cp -f $$::realhttpdconf $$::realhttpdconf\.prekoha");
174     }
175     my $includesdirectives='';
176     if ($includesmodule) {
177         $includesdirectives.="Options +Includes\n";
178         $includesdirectives.="   AddHandler server-parsed .html\n";
179     }
180     open(SITE,">>$$::realhttpdconf") or warn "Insufficient priveleges to open $$::realhttpdconf for writing.\n";
181 #    print SITE <<EOP
182 #
183 #
184 ## Ports to listen to for Koha
185 #Listen $opacport
186 #Listen $kohaport
187 #
188 ## NameVirtualHost is used by one of the optional configurations detailed below
189 #
190 ##NameVirtualHost 11.22.33.44
191 #
192 ## KOHA's OPAC Configuration
193 #<VirtualHost $servername\:$opacport>
194 #   ServerAdmin $svr_admin
195 #   DocumentRoot $opacdir/htdocs
196 #   ServerName $servername
197 #   ScriptAlias /cgi-bin/koha/ $opacdir/cgi-bin/
198 #   ErrorLog $logfiledir/opac-error_log
199 #   TransferLog $logfiledir/opac-access_log
200 #   SetEnv PERL5LIB "$intranetdir/modules"
201 #   $includesdirectives
202 #</VirtualHost>
203 #
204 ## KOHA's INTRANET Configuration
205 #<VirtualHost $servername\:$kohaport>
206 #   ServerAdmin $svr_admin
207 #   DocumentRoot $intranetdir/htdocs
208 #   ServerName $servername
209 #   ScriptAlias /cgi-bin/koha/ "$intranetdir/cgi-bin/"
210 #   ErrorLog $logfiledir/koha-error_log
211 #   TransferLog $logfiledir/koha-access_log
212 #   SetEnv PERL5LIB "$intranetdir/modules"
213 #   $includesdirectives
214 #</VirtualHost>
215 #
216 ## If you want to use name based Virtual Hosting:
217 ##   1. remove the two Listen lines
218 ##   2. replace $servername\:$opacport wih your.opac.domain.name
219 ##   3. replace ServerName $servername wih ServerName your.opac.domain.name
220 ##   4. replace $servername\:$kohaport wih your intranet domain name
221 ##   5. replace ServerName $servername wih ServerName your.intranet.domain.name
222 ##
223 ## If you want to use NameVirtualHost'ing (using two names on one ip address):
224 ##   1.  Follow steps 1-5 above
225 ##   2.  Uncomment the NameVirtualHost line and set the correct ip address
226 #
227 #EOP
228 #;
229
230
231     print qq|
232
233 Intranet Authentication
234 =======================
235
236 I can set it up so that the Intranet/Librarian site is password protected.
237 |;
238 print "Would you like to do this? ([Y]/N): ";
239 chomp($input = <STDIN>);
240
241 my $apacheauthusername='librarian';
242 my $apacheauthpassword='';
243 unless ($input=~/^n/i) {
244     print "\nEnter a userid to login with [$apacheauthusername]: ";
245     chomp ($input = <STDIN>);
246     if ($input) {
247         $apacheauthusername=$input;
248         $apacheauthusername=~s/[^a-zA-Z0-9]//g;
249     }
250     while (! $apacheauthpassword) {
251         print "\nEnter a password for the $apacheauthusername user: ";
252         chomp ($input = <STDIN>);
253         if ($input) {
254             $apacheauthpassword=$input;
255         }
256         if (!$apacheauthpassword) {
257             print "\nPlease enter a password.\n";
258         }
259     }
260     open AUTH, ">/etc/kohaintranet.pass";
261     my $chars='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
262     my $salt=substr($chars, int(rand(length($chars))),1);
263     $salt.=substr($chars, int(rand(length($chars))),1);
264     print AUTH $apacheauthusername.":".crypt($apacheauthpassword, $salt)."\n";
265     close AUTH;
266     print SITE <<EOP
267
268 <Directory $intranetdir>
269     AuthUserFile /etc/kohaintranet.pass
270     AuthType Basic
271     AuthName "Koha Intranet (for librarians only)"
272     Require  valid-user
273 </Directory>
274 EOP
275 }
276
277     close(SITE);
278
279     print "Successfully updated Apache Configuration file.\n";
280 }
281
282 print qq|
283
284 SETTING UP Z39.50 DAEMON
285 ========================
286 |;
287
288 my $kohalogdir='/var/log/koha';
289 print "Directory for logging by Z39.50 daemon [$kohalogdir]: ";
290 chomp($input = <STDIN>);
291 if ($input) {
292     $kohalogdir=$input;
293 }
294
295 unless (-e "$kohalogdir") {
296     my $result = mkdir 0770, "$kohalogdir"; 
297     if ($result==0) {
298         my @dirs = split(m#/#, $kohalogdir);
299         my $checkdir='';
300         foreach (@dirs) {
301             $checkdir.="$_/";
302             unless (-e "$checkdir") {
303                 mkdir($checkdir, 0775);
304             }
305         }
306     }
307 }
308
309 #
310 # Setup the modules directory
311 #
312 print qq|
313
314 CREATING REQUIRED DIRECTORIES
315 =============================
316
317 |;
318
319
320 unless ( -d $intranetdir ) {
321    print "Creating $intranetdir...\n";
322    my $result=mkdir ($intranetdir, oct(770));
323    if ($result==0) {
324        my @dirs = split(m#/#, $intranetdir);
325         my $checkdir='';
326         foreach (@dirs) {
327             $checkdir.="$_/";
328             unless (-e "$checkdir") {
329                 mkdir($checkdir, 0775);
330             }
331         }
332    }
333    chown (oct(0), (getgrnam($::httpduser))[2], "$intranetdir");
334    chmod (oct(770), "$intranetdir");
335 }
336 unless ( -d "$intranetdir/htdocs" ) {
337    print "Creating $intranetdir/htdocs...\n";
338    mkdir ("$intranetdir/htdocs", oct(750));
339 }
340 unless ( -d "$intranetdir/cgi-bin" ) {
341    print "Creating $intranetdir/cgi-bin...\n";
342    mkdir ("$intranetdir/cgi-bin", oct(750));
343 }
344 unless ( -d "$intranetdir/modules" ) {
345    print "Creating $intranetdir/modules...\n";
346    mkdir ("$intranetdir/modules", oct(750));
347 }
348 unless ( -d "$intranetdir/scripts" ) {
349    print "Creating $intranetdir/scripts...\n";
350    mkdir ("$intranetdir/scripts", oct(750));
351 }
352 unless ( -d $opacdir ) {
353    print "Creating $opacdir...\n";
354    my $result=mkdir ($opacdir, oct(770));
355    if ($result==0) {
356        my @dirs = split(m#/#, $opacdir);
357         my $checkdir='';
358         foreach (@dirs) {
359             $checkdir.="$_/";
360             unless (-e "$checkdir") {
361                 mkdir($checkdir, 0775);
362             }
363         }
364    }
365    chown (oct(0), (getgrnam($::httpduser))[2], "$opacdir");
366    chmod (oct(770), "$opacdir");
367 }
368 unless ( -d "$opacdir/htdocs" ) {
369    print "Creating $opacdir/htdocs...\n";
370    mkdir ("$opacdir/htdocs", oct(750));
371 }
372 unless ( -d "$opacdir/cgi-bin" ) {
373    print "Creating $opacdir/cgi-bin...\n";
374    mkdir ("$opacdir/cgi-bin", oct(750));
375 }
376
377
378
379 print "\n\nINSTALLING KOHA...\n";
380 print "\n\n==================\n";
381 print "Copying internet-html files to $intranetdir/htdocs...\n";
382 system("cp -R intranet-html/* $intranetdir/htdocs/");
383 print "Copying intranet-cgi files to $intranetdir/cgi-bin...\n";
384 system("cp -R intranet-cgi/* $intranetdir/cgi-bin/");
385 print "Copying script files to $intranetdir/scripts...\n";
386 system("cp -R scripts/* $intranetdir/scripts/");
387 print "Copying module files to $intranetdir/modules...\n";
388 system("cp -R modules/* $intranetdir/modules/");
389 print "Copying opac-html files to $opacdir/htdocs...\n";
390 system("cp -R opac-html/* $opacdir/htdocs/");
391 print "Copying opac-cgi files to $opacdir/cgi-bin...\n";
392 system("cp -R opac-cgi/* $opacdir/cgi-bin/");
393
394 system("chown -R root.$::httpduser $opacdir");
395 system("chown -R root.$::httpduser $intranetdir");
396
397
398 print qq|
399
400 KOHA.CONF
401 =========
402 Koha uses a small configuration file that is placed in your /etc/ files
403 directory. The configuration file, will be created in this directory.
404
405 |;
406
407 #Create the configuration file
408 open(SITES,">$::etcdir/koha.conf") or warn "Couldn't create file
409 at $::etcdir.  Must have write capability.\n";
410 print SITES <<EOP
411 database=$::dbname
412 hostname=$::hostname
413 user=$::user
414 pass=$::pass
415 includes=$intranetdir/htdocs/includes
416 intranetdir=$intranetdir
417 opacdir=$opacdir
418 kohalogdir=$kohalogdir
419 kohaversion=$::kohaversion
420 httpduser=$::httpduser
421 EOP
422 ;
423 close(SITES);
424
425 #
426 # Set ownership of the koha.conf file for security
427 #
428 chown((getpwnam($::httpduser)) [2,3], "$::etcdir/koha.conf") or warn "can't chown koha.conf: $!";
429 chmod 0440, "$::etcdir/koha.conf";
430
431
432 print "Successfully created the Koha configuration file.\n";
433
434 print qq|
435
436 MYSQL CONFIGURATION
437 ===================
438 |;
439 my $mysql;
440 my $mysqldir;
441 my $mysqluser = 'root';
442 my $mysqlpass = '';
443
444 foreach my $mysql (qw(/usr/local/mysql
445                       /opt/mysql
446                       )) {
447    if ( -d $mysql ) {
448             $mysqldir=$mysql;
449    }
450 }
451 if (!$mysqldir){
452     $mysqldir='/usr';
453 }
454 print qq|
455 To allow us to create the koha database please supply the 
456 mysql\'s root users password
457 |;
458
459 my $needpassword=1;
460 while ($needpassword) {
461     print "Enter mysql\'s root users password: ";
462     chomp($input = <STDIN>);
463     $mysqlpass = $input;
464     my $result=system("$mysqldir/bin/mysqladmin -u$mysqluser -p$mysqlpass proc > /dev/null 2>&1");
465     if ($result) {
466         print "\n\nInvalid password for the MySql root user.\n\n";
467     } else {
468         $needpassword=0;
469     }
470 }
471
472
473 print qq|
474
475 CREATING DATABASE
476 =================
477 |;
478 my $result=system("$mysqldir/bin/mysqladmin -u$mysqluser -p$mysqlpass create $::dbname");
479 if ($result) {
480     print "\nCouldn't connect to the MySQL server for the reason given above.\n";
481     print "This is a serious problem, the database will not get installed.\a\n";
482     print "Press <ENTER> to continue...";
483     <STDIN>;
484     print "\n";
485 } else {
486     system("$mysqldir/bin/mysql -u$mysqluser -p$mysqlpass $::dbname < koha.mysql");
487     system("$mysqldir/bin/mysql -u$mysqluser -p$mysqlpass mysql -e \"insert into user (Host,User,Password) values ('$::hostname','$::user',password('$::pass'))\"\;");
488     system("$mysqldir/bin/mysql -u$mysqluser -p$mysqlpass mysql -e \"insert into db (Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv, index_priv, alter_priv) values ('%','$::dbname','$::user','Y','Y','Y','Y','Y','Y','Y','Y')\"");
489     system("$mysqldir/bin/mysqladmin -u$mysqluser -p$mysqlpass reload");
490
491     system ("perl -I $intranetdir/modules scripts/updater/updatedatabase");
492
493
494     print qq|
495
496 SAMPLE DATA
497 ===========
498 If you are installing Koha for evaluation purposes,  I have a batch of sample
499 data that you can install now.
500
501 If you are installing Koha with the intention of populating it with your own
502 data, you probably don't want this sample data installed.
503 |;
504     print "\nWould you like to install the sample data? Y/[N]: ";
505     chomp($input = <STDIN>);
506     if ($input =~/^y/i) {
507         system("gunzip sampledata-1.2.gz");
508         system("cat sampledata-1.2 | $mysqldir/bin/mysql -u$mysqluser -p$mysqlpass $::dbname");
509         system("gzip -9 sampledata-1.2");
510         system("$mysqldir/bin/mysql -u$mysqluser -p$mysqlpass $::dbname -e \"insert into branches (branchcode,branchname,issuing) values ('MAIN', 'Main Library', 1)\"");
511         system("$mysqldir/bin/mysql -u$mysqluser -p$mysqlpass $::dbname -e \"insert into printers (printername,printqueue,printtype) values ('Circulation Desk Printer', 'lp', 'hp')\"");
512         print qq|
513
514 Sample data has been installed.  For some suggestions on testing Koha, please
515 read the file doc/HOWTO-Testing.  If you find any bugs, please submit them at
516 http://bugs.koha.org/.  If you need help with testing Koha, you can post a
517 question through the koha-devel mailing list, or you can check for a developer
518 online at +irc.katipo.co.nz:6667 channel #koha.
519
520 You can find instructions for subscribing to the Koha mailing lists at:
521
522     http://www.koha.org
523
524
525 Press <ENTER> to continue...
526 |;
527         <STDIN>;
528     } else {
529         print "\n\nWould you like to add a branch and printer? [Y]/N: ";
530         chomp($input = <STDIN>);
531
532
533         unless ($input =~/^n/i) {
534             my $branch='Main Library';
535             print "Enter a name for the library branch [$branch]: ";
536             chomp($input = <STDIN>);
537             if ($input) {
538                 $branch=$input;
539             }
540             $branch=~s/[^A-Za-z0-9\s]//g;
541             my $branchcode=$branch;
542             $branchcode=~s/[^A-Za-z0-9]//g;
543             $branchcode=uc($branchcode);
544             $branchcode=substr($branchcode,0,4);
545             print "Enter a four letter code for your branch [$branchcode]: ";
546             chomp($input = <STDIN>);
547             if ($input) {
548                 $branchcode=$input;
549             }
550             $branchcode=~s/[^A-Z]//g;
551             $branchcode=uc($branchcode);
552             $branchcode=substr($branchcode,0,4);
553             print "Adding branch '$branch' with code '$branchcode'.\n";
554             system("$mysqldir/bin/mysql -u$mysqluser -p$mysqlpass $::dbname -e \"insert into branches (branchcode,branchname,issuing) values ('$branchcode', '$branch', 1)\"");
555             my $printername='Library Printer';
556             print "Enter a name for the printer [$printername]: ";
557             chomp($input = <STDIN>);
558             if ($input) {
559                 $printername=$input;
560             }
561             $printername=~s/[^A-Za-z0-9\s]//g;
562             my $printerqueue='lp';
563             print "Enter the queue for the printer [$printerqueue]: ";
564             chomp($input = <STDIN>);
565             if ($input) {
566                 $printerqueue=$input;
567             }
568             $printerqueue=~s/[^A-Za-z0-9]//g;
569             system("$mysqldir/bin/mysql -u$mysqluser -p$mysqlpass $::dbname -e \"insert into printers (printername,printqueue,printtype) values ('$printername', '$printerqueue', '')\"");
570         }
571     }
572
573
574 }
575
576
577 chmod 0770, $kohalogdir;
578 chown((getpwnam($::httpduser)) [2,3], $kohalogdir) or warn "can't chown $kohalogdir: $!";
579
580 # LAUNCH SCRIPT
581 print "Modifying Z39.50 daemon launch script...\n";
582 my $newfile='';
583 open (L, "$intranetdir/scripts/z3950daemon/z3950-daemon-launch.sh");
584 while (<L>) {
585     if (/^RunAsUser=/) {
586         $newfile.="RunAsUser=$::httpduser\n";
587     } elsif (/^KohaZ3950Dir=/) {
588         $newfile.="KohaZ3950Dir=$intranetdir/scripts/z3950daemon\n";
589     } else {
590         $newfile.=$_;
591     }
592 }
593 close L;
594 system("mv $intranetdir/scripts/z3950daemon/z3950-daemon-launch.sh $intranetdir/scripts/z3950daemon/z3950-daemon-launch.sh.orig");
595 open L, ">$intranetdir/scripts/z3950daemon/z3950-daemon-launch.sh";
596 print L $newfile;
597 close L;
598
599
600 # SHELL SCRIPT
601 print "Modifying Z39.50 daemon wrapper script...\n";
602 $newfile='';
603 open (S, "$intranetdir/scripts/z3950daemon/z3950-daemon-shell.sh");
604 while (<S>) {
605     if (/^KohaModuleDir=/) {
606         $newfile.="KohaModuleDir=$intranetdir/modules\n";
607     } elsif (/^KohaZ3950Dir=/) {
608         $newfile.="KohaZ3950Dir=$intranetdir/scripts/z3950daemon\n";
609     } elsif (/^LogDir=/) {
610         $newfile.="LogDir=$kohalogdir\n";
611     } else {
612         $newfile.=$_;
613     }
614 }
615 close S;
616
617 system("mv $intranetdir/scripts/z3950daemon/z3950-daemon-shell.sh $intranetdir/scripts/z3950daemon/z3950-daemon-shell.sh.orig");
618 open S, ">$intranetdir/scripts/z3950daemon/z3950-daemon-shell.sh";
619 print S $newfile;
620 close S;
621 chmod 0750, "$intranetdir/scripts/z3950daemon/z3950-daemon-launch.sh";
622 chmod 0750, "$intranetdir/scripts/z3950daemon/z3950-daemon-shell.sh";
623 chmod 0750, "$intranetdir/scripts/z3950daemon/processz3950queue";
624 chown(0, (getpwnam($::httpduser)) [3], "$intranetdir/scripts/z3950daemon/z3950-daemon-shell.sh") or warn "can't chown $intranetdir/scripts/z3950daemon/z3950-daemon-shell.sh: $!";
625 chown(0, (getpwnam($::httpduser)) [3], "$intranetdir/scripts/z3950daemon/processz3950queue") or warn "can't chown $intranetdir/scripts/z3950daemon/processz3950queue: $!";
626
627 print qq|
628
629 ==================
630 = Authentication =
631 ==================
632
633 This release of Koha has a new authentication module.  If you are not already
634 using basic authentication on your intranet, you will be required to log in to
635 access some of the features of the intranet.  You can log in using the userid
636 and password from the /etc/koha.conf configuration file at any time.  Use the
637 "Members" module to add passwords for other accounts and set their permissions.
638
639 [NOTE PERMISSIONS ARE NOT COMPLETED AS OF 1.2.3RC1.  Do not give passwords to
640  any patrons unless you want them to have full access to your intranet.]
641 |;
642 print "Press the <ENTER> key to continue: ";
643 <STDIN>;
644
645
646 #RESTART APACHE
647 print "\n\n";
648 #print qq|
649 #
650 #COMPLETED
651 #=========
652 #Congratulations ... your Koha installation is almost complete!
653 #The final step is to restart your webserver.
654 #
655 #You will be able to connect to your Librarian interface at:
656 #
657 #   http://$servername\:$kohaport/
658 #
659 #and the OPAC interface at :
660 #
661 #   http://$servername\:$opacport/
662 #
663 #
664 #Be sure to read the INSTALL, and Hints files. 
665 #
666 #For more information visit http://www.koha.org
667 #
668 #Would you like to restart your webserver now? (Y/[N]):
669 #|;
670
671 my $restart = <STDIN>;
672 chomp $restart;
673
674 if ($restart=~/^y/i) {
675         # Need to support other init structures here?
676         if (-e "/etc/rc.d/init.d/httpd") {
677             system('/etc/rc.d/init.d/httpd restart');
678         } elsif (-e "/etc/init.d/apache") {
679             system('/etc//init.d/apache restart');
680         } elsif (-e "/etc/init.d/apache-ssl") {
681             system('/etc/init.d/apache-ssl restart');
682         }
683     } else {
684         print qq|
685 Congratulations ... your Koha installation is complete!
686 You will need to restart your webserver before using Koha!
687 |;
688     exit;
689 };