package INSTALL::apache_bundle; use INSTALL::common; use INSTALL::os; use INSTALL::services; use strict; no strict 'subs'; my $CONST_ComponentName = "Apache Webserver Bundle"; my $CONST_StartPriority = 86; my $CONST_StopPriority = 14; my $pOS = \%INSTALL::os::Details; my $debug = 0; my $debug_output = ($debug ? "" : " > /dev/null 2>&1"); use INSTALL::parameter; use INSTALL::global; use INSTALL::component; use INSTALL::substitute; use INSTALL::reboot; use IO::Socket::INET; my $pGlobals = \%INSTALL::global::variables; my $pParameters = \%INSTALL::parameter::variables; #------------------------------------------------------------------------------ # If this flag is set to 1, then the installation of the apache bundle # is configurable. Otherwise, it will be installed regardless of what the # user wants. #------------------------------------------------------------------------------ my $CONST_InstallConfigurable = 1; #------------------------------------------------------------------------------ # Is the specified port listening? If so, return a true value. #============================================================================== sub isListening { (@_ == 1) || die "Invalid number of arguments."; my $port = shift @_; unless($port =~ /^[0-9]+$/) { return(1); } my $sock = IO::Socket::INET->new(PeerAddr => 'localhost', PeerPort => $port, Proto => 'tcp'); if ($sock) { close($sock); return(1); } else { return(undef); } } #------------------------------------------------------------------------------ # Configures (or disables) bundled Apache support. In interactive mode, the # user is given the choice to select various attributes about their Apache # webserver. For non-interactive mode, they are slightly more limited, but # the list of what they may configure are the following command-line # arguments: # install_apache_bundle= # - Will dictate whether or not the bundled Apache server will be installed. # # apache_bundle_port= # - Specifies what port the Apache server is configured to listen on. # In non-interactive mode, this value is not checked for validity and # set to 'an appropriate value', since there is no way to indicate to # the user that that action was performed. It will merely conflict if # something is at that address. # # apache_bundle_startonboot= # - Specifies whether or not the Apache server will be configured to start # on system boot. # # apache_bundle_username= # apache_bundle_group= # - Specifies the username and group that the Apache server is to run as. # These settings are not checked for validity. They are assumed to be # set correctly, since detecting all of the various 'passwd' mechanisms # in the world would be time prohibitive. #============================================================================== sub configure { # Default Apache Bundle options. my $server_root = "$pGlobals->{asphome}/apache-bundle"; my %Options = (apache_bundle::install => "true", apache_bundle::startonboot => "true", apache_bundle::servername => $pGlobals->{full_hostname}, apache_bundle::serverroot => "$server_root", apache_bundle::bin_dir => "$server_root/bin", apache_bundle::binary => "$server_root/bin/chilisoft-httpd", apache_bundle::conf_dir => "$server_root/conf", apache_bundle::conf => "$server_root/conf/httpd.conf", apache_bundle::version => 1.3.19, # Overridden if detected to be different # User configurable apache_bundle::port => 80, apache_bundle::documentroot => "$server_root/htdocs", apache_bundle::maxclients => 150, apache_bundle::user => "nobody", apache_bundle::group => "nobody", # Might be different depending on the OS (is verified). apache_bundle::startonboot => "true", ); # On HP-UX machines, the nobody has an invalid group id. so things # might not work out. so in this case , we might use daemon or www as # the user name. # So we need to be more tolerant towards user name and group. my @password_info ; my ($username,$password,$uid,$gid); my @user_lists = ("nobody","daemon","www") ; foreach (@user_lists) { @password_info = getpwnam($_) ; if ($#password_info > 0) { ($username,$password,$uid,$gid) = @password_info; } # $uid = getpwnam($_) ; $gid = getgrnam($_) ; last if (($uid > 0) && ($gid > 0)) ; } my ($groupname) = getgrgid($gid); if (not $groupname) { # If the groups file does not contain an entry for the gid, # merely use the gid as is. $groupname = $gid; } if (not $username) { $username = $uid; } $Options{apache_bundle::user} = $username ; $Options{apache_bundle::group} = $groupname ; if ((exists $pParameters->{apache_bundle_port}) && ($pParameters->{apache_bundle_port} =~ /^\d+$/)) { $Options{apache_bundle::port} = $pParameters->{apache_bundle_port}; } if (exists $pParameters->{apache_bundle_startonboot}) { $Options{apache_bundle::startonboot} = $pParameters->{apache_bundle_startonboot}; } if (exists $pParameters->{apache_bundle_username}) { $Options{apache_bundle::user} = $pParameters->{apache_bundle_username}; } if (exists $pParameters->{apache_bundle_group}) { $Options{apache_bundle::group} = $pParameters->{apache_bundle_group}; } #--------------------------------------------------------------------------- # Locate and install the apache-bundle tarball. #--------------------------------------------------------------------------- # Find package. my $apache_base = "apache-bundle-([0-9.]+).$pOS->{ostag}"; my $tarball; local *COMPDIR; if (opendir(COMPDIR,$pGlobals->{apache_component_dir})) { foreach my $file (readdir(COMPDIR)) { if ($file =~ /^$apache_base\.tar/) { $Options{apache_bundle::version} = $1; $tarball = "$pGlobals->{apache_component_dir}/$file"; } } closedir(COMPDIR); unless(defined $tarball) { error("Unable to find the $CONST_ComponentName tarball."); goto EXIT_ERROR; } } else { error("Unable to locate $CONST_ComponentName component dir:", " $pGlobals->{apache_component_dir}"); goto EXIT_ERROR; } #--------------------------------------------------------------------------- # Configure the Apache bundle. #--------------------------------------------------------------------------- if ($CONST_InstallConfigurable) { CONFIGURE_QUERY: unless(interactive) { unless((not exists $pParameters->{install_apache_bundle}) || boolean_value($pParameters->{install_apache_bundle})) { goto CONFIGURE_CANCEL; } } else { INSTALL::query::user_menu ("Sun Chili!Soft ASP - Bundled Apache $Options{apache_bundle::version} Configuration", "Sun Chili!Soft ASP includes a ready-to-run Apache 1.3.19 Web server that is ". "configured with Microsoft (TM) FrontPage 2002 Server Extensions support and EAPI ". "(Extended API). If you have not yet configured a Web server, you now have the ". "option to install this preconfigured Apache Web server.\n". " \n". "Note: Sun ChiliSoft ASP supports but does not install FrontPage 2002\n". " Server Extensions; those must be obtained from Microsoft. For\n". " more information about Sun Chili!Soft ASP support for FrontPage\n". " 2002 Server Extensions, see 'Chapter 3' in our production\n". " documentation. For more information about EAPI, including\n". " the mod_ssl / OpenSSL module, visit www.modssl.org"); unless (INSTALL::query::boolean_default ("Would you like to install the bundled Apache $Options{apache_bundle::version} Web server",0)) { goto CONFIGURE_CANCEL; } } } my $install_mode = 'default'; if (interactive) { my $response = INSTALL::query::user_menu ("Sun Chili!Soft ASP - Bundled Apache $Options{apache_bundle::version} Configuration", "The Apache Web server provides many settings that you can choose and configure. ". "You have several configuration options. Select the option below that best suits ". "your needs.\n". "-- Select option 1 ('Default configuration') to have Sun Chili!Soft ASP\n". " automatically configure all settings for you.\n". " \n". "-- Select option 2 ('Specify only the Web server listen port') to\n". " specify only the port number that the Apache Web server will listen\n". " on.\n". " \n". "-- Choose option 3 ('Customize the configure') to specify the\n". " configuration of many different settings.", "Which type of configuration would you like to perform", "terminal_mark=?", "default=Default configuration.", "Specify only the Web server listen port.", "Customize the configuration." ); # Default to 80 (or whatever the user passed as the default), though if that's # not available default to 81,82, etc. for(my $i=$Options{apache_bundle::port}; $i < (64*1024); $i++) { unless (isListening($i)) { $Options{apache_bundle::port} = $i; last; } } unless(exists $Options{apache_bundle::port}) { # Should be impossible, but ... assume the position. $Options{apache_bundle::port} = 80; } if ($response >= 2) { $install_mode= 'custom'; my $port = INSTALL::query::user_function ("What would you like your Web server port to be", sub { return(not isListening(@_)) }, "terminal_mark=?", "default=$Options{apache_bundle::port}", "invalid_choice=That port is currently in use or is invalid, please choose another port" ); $Options{apache_bundle::port} = $port; } if ($response > 2) { DOCUMENTROOT_QUERY: report(""); note("By default, the document root of the Apache Web server is placed inside", "the Sun Chili!Soft ASP directory. However, it is a good idea to", "locate the document root elsewhere, to avoid accidental deletion", "during uninstalls or upgrades of Sun Chili!Soft ASP."); my $question = INSTALL::query::user_function ("Specify your document root", sub { my $path = shift @_; if ((-d $path) || mkpath($path,0755)) { return(1); } else { error("The specified path is neither a directory, nor can it be created."); report("Provided path: ", $path); return(undef); } }, "invalid_choice=", "default=$Options{apache_bundle::documentroot}", "multiline=true"); $Options{apache_bundle::documentroot} = $question; MAXCLIENTS_QUERY: report(""); note("For large machines, the maximum number of clients can usually be", "increased to improve performance. However, this value should not", "be set below the default, as it could severely hamper performance.", "With the Apache Web server, this value is equivalent to the", "maximum number of processes that may be created to handle incoming", "requests."); $question = INSTALL::query::user_function ("Specify the maximum number of simultaneous clients that may\n". "be serviced", sub { my $value = shift @_; if ($value !~ /^[0-9]+$/) { return(error("MaxClients must be a positive integer value.")); } elsif ($value > 256) { return(error("MaxClients is hard-limited in our Apache Web Server to 256.", "If you really require that many concurrent clients, you will be required", "to supply your own Web server.")); } elsif ($value < 1) { return(error("MaxClients must be a positive value.")); } return(1); }, "invalid_choice=", "default=$Options{apache_bundle::maxclients}"); $Options{apache_bundle::maxclients} = $question; USERGROUP_QUERY: report(""); note("It is more secure to run the Apache Web server as a non-privileged", "user, which usually means any user except root. Depending on your", "system configuration and security model used, however, this", "definition may vary. You should do one of the following:", "-- Leave the settings as they are (that is, use the '$Options{apache_bundle::user}' user", " and '$Options{apache_bundle::group}' group).", "", "-- Leave the settings as thay are for now, and later create a user", " specifically assigned to the Web server role. If you choose", " this option, following installation, you must change the 'User'", " (and possibly 'Group') in the Apache Web server configuration", " file:", " $Options{apache_bundle::conf}", "", "-- Configure the settings to meet your Web server security needs."); $question = INSTALL::query::user_function ("Specify the user you wish to run the Web server as", sub { my $user = shift @_; unless (defined getpwnam($user)) { return(error("That does not appear to be a valid username.", "If this user is valid, via NIS, or other undetected mechanism, leave", "the default value and manually update the setting in the configuration", "file (post install):", " $Options{apache_bundle::conf}")); } return(1); }, "invalid_choice=", "default=$Options{apache_bundle::user}"); $Options{apache_bundle::user} = $question; ########## Perform file substitutions my ($username,$password,$uid,$gid) = getpwnam($Options{apache_bundle::user}); my ($groupname) = getgrgid($gid); if (not $groupname) { # If the groups file does not contain an entry for the gid, # merely use the gid as is. $groupname = $gid; } if ($groupname) { $Options{apache_bundle::group} = $groupname; } $question = INSTALL::query::user_function ("Specify the group you wish to run the Web server as", sub { my $group = shift @_; unless (defined getgrnam($group)) { return(error("That does not appear to be a valid group.", "If this group is valid, via NIS, or other undetected mechanism, leave", "the default value and manually update the setting in the configuration", "file (post install):", " $Options{apache_bundle::conf}")); } return(1); }, "default=$Options{apache_bundle::group}"); $Options{apache_bundle::group} = $question; if (INSTALL::reboot::supported) { STARTONBOOT_QUERY: report(""); note("It can be useful to have the Apache Web server start automatically", "on system boot. However, you might want the flexibility that comes", "with manually starting and stopping the Apache Web server. Make", "the choice that best suits your needs."); if (INSTALL::query::boolean_default ("Would you like the Web Server to start on system boot",1)) { $Options{apache_bundle::startonboot} = 'true'; } else { $Options{apache_bundle::startonboot} = 'false'; } } } } my @Menu = ("Sun Chili!Soft ASP - Bundled Apache $Options{apache_bundle::version}", "Configured Apache Server Options:\n". " ServerRoot: $Options{apache_bundle::serverroot}\n". " DocumentRoot: $Options{apache_bundle::documentroot}\n". " Port: $Options{apache_bundle::port}\n". " MaxClients: $Options{apache_bundle::maxclients}\n". " User: $Options{apache_bundle::user}\n". " Group: $Options{apache_bundle::group}\n". " StartOnBoot: $Options{apache_bundle::startonboot}\n". " \n". "Apache Server Locations:\n". " Conf file: $Options{apache_bundle::conf}\n". " Binary: $Options{apache_bundle::binary}\n"); if (interactive && ($install_mode =~ /custom/)) { INSTALL::query::user_menu(@Menu); unless (INSTALL::query::boolean_default ("Would you like to proceed with these settings",1)) { goto CONFIGURE_QUERY; } } my $extract_child_pid = fork; if (not $extract_child_pid) { # Check to see if the Apache bundle was previously installed. If it # was, delete it before extraction. if (-d $Options{apache_bundle::serverroot}) { recursive_rmdir($Options{apache_bundle::serverroot}); } if (interactive) { # Give the user sufficient time to see the chosen options. sleep(2); } unless(decompress($tarball,$pGlobals->{asphome})) { error("Unable to decompress $CONST_ComponentName tarball:", " $tarball"); goto EXIT_ERROR; } exit(0); } if (interactive) { INSTALL::query::user_menu(@Menu); print STDERR "Installing the Apache Webserver ... "; INSTALL::common::spinner_wait($extract_child_pid); print STDERR "\n"; } else { waitpid $extract_child_pid,0; } if (-x $Options{apache_bundle::binary}) { my $version_string = `$Options{apache_bundle::binary} -v`; if ($version_string =~ m@Server version:.*/(\d+)\.(\d+)\.(\d+)@s) { $Options{apache_bundle::version} = "$1.$2.$3"; } } else { error("Unable to locate the Apache Bundle's binary."); note("It was expected be located at: ", " $Options{apache_bundle::binary}"); goto EXIT_ERROR; } my $configure_child_pid = fork; if (not $configure_child_pid) { #--------------------------------------------------------------------------- # Add port to services file, if not already contained therein. #--------------------------------------------------------------------------- if (INSTALL::services::validate_free_ports($Options{apache_bundle::port},1)) { INSTALL::services::allocate_ports ( $Options{apache_bundle::port}, 1, "Sun Chili!Soft ASP $CONST_ComponentName" ); } #--------------------------------------------------------------------------- # Handle reboot sequence. #--------------------------------------------------------------------------- if (boolean_value($Options{apache_bundle::startonboot}) && INSTALL::reboot::supported) { my $script_name = "apache-bundle-$Options{apache_bundle::port}"; INSTALL::reboot::install_script($script_name, "$pGlobals->{scripts_dir}/apachectl 'binary=$Options{apache_bundle::binary}' 'conf=$Options{apache_bundle::conf}' start", "$pGlobals->{scripts_dir}/apachectl 'binary=$Options{apache_bundle::binary}' 'conf=$Options{apache_bundle::conf}' stop", "$pGlobals->{scripts_dir}/apachectl 'binary=$Options{apache_bundle::binary}' 'conf=$Options{apache_bundle::conf}' status", $CONST_StartPriority,$CONST_StopPriority); } #--------------------------------------------------------------------------- # Perform file template substitution. #--------------------------------------------------------------------------- my %Template_Map = ( 'apachectl' => $Options{apache_bundle::bin_dir}, 'httpd.conf' => $Options{apache_bundle::conf_dir} ); foreach my $template (keys %Template_Map) { INSTALL::substitute::template($template,$Template_Map{$template},%Options); } #--------------------------------------------------------------------------- # Finally, add to the webserver-cache. #--------------------------------------------------------------------------- use INSTALL::webserver; INSTALL::webserver::cache_server('apache', $Options{apache_bundle::conf}, webserver_binary => $Options{apache_bundle::binary}); exit(0); } if (interactive) { print STDERR "Configuring webserver options ... "; INSTALL::common::spinner_wait($configure_child_pid); print STDERR "\n"; } else { waitpid $configure_child_pid,0; } #--------------------------------------------------------------------------- # Register component settings. #--------------------------------------------------------------------------- my %component_details; foreach my $key (keys %Options) { my $newkey = $key; $newkey =~ s/apache_bundle:://; $component_details{$newkey} = $Options{$key}; } $component_details{uninstaller} = ("$pGlobals->{perl} -I$pGlobals->{asphome} -I$pGlobals->{apache_component_dir} ". "$pGlobals->{apache_component_dir}/install.pl $pGlobals->{execute_params} \"uninstall=true\""); my $result = INSTALL::component::merge($CONST_ComponentName, $pGlobals->{asphome}, %component_details); #--------------------------------------------------------------------------- # Log the install status. #--------------------------------------------------------------------------- my @summary_log = ("$CONST_ComponentName installed: Yes", " Port: $Options{apache_bundle::port}"); summary_log(@summary_log); component_log(@summary_log); return($result); CONFIGURE_CANCEL: my $summary_log = "$CONST_ComponentName installation: Canceled"; summary_log($summary_log); component_log($summary_log); return(undef); CONFIGURE_ERROR: INSTALL::service::deallocate_ports($Options{apache_bundle::port}); # Deallocate only those ports created by us? my $summary_log = "$CONST_ComponentName installation: Failed"; summary_log($summary_log); component_log($summary_log); return(undef); EXIT_ERROR: exit(1); } #------------------------------------------------------------------------------ # Uninstalls any apache server on the box. #============================================================================== sub uninstall { my $pComponent = INSTALL::component::open($CONST_ComponentName, $pGlobals->{asphome}); if (not $pComponent) { return(error("Unable to open $CONST_ComponentName component.")); } #--------------------------------------------------------------------------- # Stop the apache server. #--------------------------------------------------------------------------- system_log("$pGlobals->{script_dir}/apachectl 'binary=$pComponent->{binary}' ". "'conf=$pComponent->{conf}' stop"); #--------------------------------------------------------------------------- # Remove the reboot scripts, if they exist. #--------------------------------------------------------------------------- if (INSTALL::reboot::supported) { my $script_name = "apache-bundle-$pComponent->{port}"; INSTALL::reboot::uninstall_script($script_name, $CONST_StartPriority, $CONST_StopPriority); } #--------------------------------------------------------------------------- # Free the apache bundle's allocated ports. #--------------------------------------------------------------------------- INSTALL::services::deallocate_ports($pComponent->{port}); #--------------------------------------------------------------------------- # Remove the bundle directory. #--------------------------------------------------------------------------- # To avoid problem's of uninstalling the webserver-to-asp linkage, leave # the directory where it is. Instead, when installing the Apache bundle, # check to see if the directory exists. If it does, remove it. # recursive_rmdir($pComponent->{serverroot}); #--------------------------------------------------------------------------- # Unregister the component. #--------------------------------------------------------------------------- INSTALL::component::unregister($CONST_ComponentName,$pGlobals->{asphome}); #--------------------------------------------------------------------------- # Log the uninstall. #--------------------------------------------------------------------------- my @summary_log = ("$CONST_ComponentName uninstalled:", " Port: $pComponent->{port}"); summary_log(@summary_log); component_log(@summary_log); return(1); }