egroupware/doc/rpm-build/post_install.php

702 lines
22 KiB
PHP
Executable File

#!/usr/bin/php
<?php
/**
* EGroupware - RPM post install: automatic install or update EGroupware
*
* @link http://www.egroupware.org
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @author RalfBecker@outdoor-training.de
* @version $Id$
*/
if (php_sapi_name() !== 'cli') // security precaution: forbit calling post_install as web-page
{
die('<h1>rpm_post_install.php must NOT be called as web-page --> exiting !!!</h1>');
}
$verbose = false;
$config = array(
'php' => '/usr/bin/php',
'pear' => '/usr/bin/pear',
'source_dir' => '/usr/share/egroupware',
'data_dir' => '/var/lib/egroupware',
'header' => '$data_dir/header.inc.php', // symlinked to source_dir by rpm
'setup-cli' => '$source_dir/setup/setup-cli.php',
'domain' => 'default',
'config_user' => 'admin',
'config_passwd' => randomstring(),
'db_type' => 'mysqli',
'db_host' => 'localhost',
'db_port' => 3306,
'db_name' => 'egroupware',
'db_user' => 'egroupware',
'db_pass' => randomstring(),
'db_grant_host' => 'localhost',
'db_root' => 'root', // mysql root user/pw to create database
'db_root_pw' => '',
'backup' => '',
'admin_user' => 'sysop',
'admin_passwd'=> randomstring(),
'lang' => 'en', // languages for admin user and extra lang to install
'charset' => 'utf-8',
'start_db' => '/sbin/service mysqld',
'autostart_db' => '/sbin/chkconfig --level 345 mysqld on',
'start_webserver' => '/sbin/service httpd',
'autostart_webserver' => '/sbin/chkconfig --level 345 httpd on',
'distro' => 'rh',
'account-auth' => 'sql',
'account_min_id' => '',
'ldap_suffix' => 'dc=local',
'ldap_host' => 'localhost',
'ldap_admin' => 'cn=admin,$suffix',
'ldap_admin_pw' => '',
'ldap_base' => 'o=$domain,$suffix',
'ldap_root_dn' => 'cn=admin,$base',
'ldap_root_pw' => randomstring(),
'ldap_context' => 'ou=accounts,$base',
'ldap_search_filter' => '(uid=%user)',
'ldap_group_context' => 'ou=groups,$base',
'mailserver' => '',
'smtpserver' => 'localhost,25',
'smtp' => '', // see setup-cli.php --help config
'imap' => '',
'sieve' => '',
'folder' => '',
'install-update-app' => '', // install or update a single (non-default) app
'webserver_user'=> 'apache', // required to fix permissions
'php5enmod' => '',
);
// read language from LANG enviroment variable
if (($lang = isset($_ENV['LANG']) ? $_ENV['LANG'] : $_SERVER['LANG']))
{
@list($lang,$nat) = preg_split('/[_.]/',$lang);
if (in_array($lang.'-'.strtolower($nat),array('es-es','pt-br','zh-tw')))
{
$lang .= '-'.strtolower($nat);
}
$config['lang'] = $lang;
}
$config['source_dir'] = dirname(dirname(dirname(__FILE__)));
/**
* Set distribution spezific defaults
*
* @param string $distro=null default autodetect
*/
function set_distro_defaults($distro=null)
{
global $config;
if (is_null($distro))
{
$distro = file_exists('/etc/SuSE-release') ? 'suse' : (file_exists('/etc/debian_version') ? 'debian' :
(file_exists('/etc/mandriva-release') ? 'mandriva' : 'rh'));
}
switch (($config['distro'] = $distro))
{
case 'suse':
// openSUSE 12.1+ no longer uses php5
if (file_exists('/usr/bin/php5')) $config['php'] = '/usr/bin/php5';
if (file_exists('/usr/bin/pear5')) $config['pear'] = '/usr/bin/pear5';
$config['start_db'] = '/sbin/service mysql';
$config['autostart_db'] = '/sbin/chkconfig --level 345 mysql on';
$config['start_webserver'] = '/sbin/service apache2';
$config['autostart_webserver'] = '/sbin/chkconfig --level 345 apache2 on';
$config['ldap_suffix'] = 'dc=site';
$config['ldap_admin'] = $config['ldap_root_dn'] = 'cn=Administrator,$suffix';
$config['ldap_root_pw'] = '$admin_pw';
$config['ldap_base'] = '$suffix';
$config['ldap_context'] = 'ou=people,$base';
$config['ldap_group_context'] = 'ou=group,$base';
$config['webserver_user'] = 'wwwrun';
break;
case 'debian':
// service not in Debian5, only newer Ubuntu, which complains about /etc/init.d/xx
if (file_exists('/usr/sbin/service'))
{
$config['start_db'] = '/usr/sbin/service mysql';
$config['start_webserver'] = '/usr/sbin/service apache2';
}
else
{
$config['start_db'] = '/etc/init.d/mysql';
$config['start_webserver'] = '/etc/init.d/apache2';
}
// enable mcrypt
if (file_exists('/usr/sbin/php5enmod'))
{
$config['php5enmod'] = '/usr/sbin/php5enmod';
// Ubuntu only needs "-s ALL" and Debian gives an error, because it does not support it
if (!file_exists('/etc/php5/conf.d/')) $config['php5enmod'] = ' -s ALL';
$config['php5enmod'] = 'mcrypt';
}
$config['autostart_db'] = '/usr/sbin/update-rc.d mysql defaults';
$config['autostart_webserver'] = '/usr/sbin/update-rc.d apache2 defaults';
$config['webserver_user'] = 'www-data';
break;
case 'mandriva':
$config['ldap_suffix'] = 'dc=site';
$config['ldap_admin'] = $config['ldap_root_dn'] = 'uid=LDAP Admin,ou=System Accounts,$suffix';
$config['ldap_root_pw'] = '$admin_pw';
$config['ldap_base'] = '$suffix';
$config['ldap_context'] = 'ou=People,$base';
$config['ldap_group_context'] = 'ou=Group,$base';
break;
default:
$config['distro'] = 'rh';
// fall through
case 'rh':
// some MySQL packages (mysql.com, MariaDB, ...) use "mysql" as service name instead of RH default "mysqld"
if (!file_exists('/etc/init.d/mysqld') && file_exists('/etc/init.d/mysql'))
{
foreach(array('start_db','autostart_db') as $name)
{
$config[$name] = str_replace('mysqld','mysql',$config[$name]);
}
}
break;
}
}
set_distro_defaults();
// read config from command line
$argv = $_SERVER['argv'];
$prog = array_shift($argv);
// check if we have EGW_POST_INSTALL set and prepend it to the command line (command line has precedence)
if (($config_set = isset($_ENV['EGW_POST_INSTALL']) ? $_ENV['EGW_POST_INSTALL'] : @$_SERVER['EGW_POST_INSTALL']))
{
$conf = array();
$config_set = preg_split('/[ \t]+/',trim($config_set));
while($config_set)
{
$val = array_shift($config_set);
if (($quote = $val[0]) == "'" || $quote == '"') // arguments might be quoted with ' or "
{
while (substr($val,-1) != $quote)
{
if (!$config_set) throw new Exception('Invalid EGW_POST_INSTALL enviroment variable!');
$val .= ' '.array_shift($config_set);
}
$val = substr($val,1,-1);
}
$conf[] = $val;
}
$argv = array_merge($conf,$argv);
}
$auth_type_given = false;
while(($arg = array_shift($argv)))
{
if ($arg == '-v' || $arg == '--verbose')
{
$verbose = true;
}
elseif($arg == '-h' || $arg == '--help')
{
usage();
}
elseif($arg == '--suse')
{
set_distro_defaults('suse');
}
elseif($arg == '--distro')
{
set_distro_defaults(array_shift($argv));
}
elseif(substr($arg,0,2) == '--' && isset($config[$name=substr($arg,2)]))
{
$config[$name] = array_shift($argv);
switch($name)
{
case 'auth_type':
$auth_type_given = true;
break;
case 'account_repository': // auth-type defaults to account-repository
if (!$auth_type_given)
{
$config['auth_type'] = $config[$name];
}
break;
}
}
else
{
usage("Unknown argument '$arg'!");
}
}
$replace = array();
foreach($config as $name => $value)
{
$replace['$'.$name] = $value;
if (strpos($value,'$') !== false)
{
$config[$name] = strtr($value,$replace);
}
}
// basic config checks
foreach(array('php','source_dir','data_dir','setup-cli') as $name)
{
if (!file_exists($config[$name])) bail_out(1,$config[$name].' not found!');
}
// install/upgrade required pear packages
check_install_pear_packages();
$setup_cli = $config['php'].' -d memory_limit=256M '.$config['setup-cli'];
if (!file_exists($config['header']) || filesize($config['header']) < 200) // default header redirecting to setup is 147 bytes
{
// --> new install
$extra_config = '';
// check for localhost if database server is started and start it (permanent) if not
if ($config['db_host'] == 'localhost' && $config['start_db'])
{
exec($config['start_db'].' status',$dummy,$ret);
if ($ret)
{
system($config['start_db'].' start');
system($config['autostart_db']);
}
}
// create database
$setup_db = $setup_cli.' --setup-cmd-database sub_command=create_db';
foreach(array('domain','db_type','db_host','db_port','db_name','db_user','db_pass','db_root','db_root_pw','db_grant_host') as $name)
{
$setup_db .= ' '.escapeshellarg($name.'='.$config[$name]);
}
run_cmd($setup_db);
// check if ldap is required and initialise it
// we need to specify account_repository and auth_type to --install as extra config, otherwise install happens for sql!
@list($config['account_repository'],$config['auth_type'],$rest) = explode(',',$config['account-auth'],3);
$extra_config .= ' '.escapeshellarg('account_repository='.$config['account_repository']);
$extra_config .= ' '.escapeshellarg('auth_type='.(empty($config['auth_type']) ? $config['account_repository'] : $config['auth_type']));
if (empty($rest)) unset($config['account-auth']);
if ($config['account_repository'] == 'ldap' || $config['auth_type'] == 'ldap')
{
// set account_min_id to 1100 if not specified to NOT clash with system accounts
$extra_config .= ' '.escapeshellarg('account_min_id='.(!empty($config['account_min_id']) ? $config['account_min_id'] : 1100));
$setup_ldap = $setup_cli.' --setup-cmd-ldap sub_command='.
($config['account_repository'] == 'ldap' ? 'create_ldap' : 'test_ldap');
foreach(array(
'domain','ldap_suffix','ldap_host','ldap_admin','ldap_admin_pw', // non-egw params: only used for create
'ldap_base','ldap_root_dn','ldap_root_pw','ldap_context','ldap_search_filter','ldap_group_context', // egw params
) as $name)
{
if (strpos($value=$config[$name],'$') !== false)
{
$config[$name] = $value = strtr($value,array(
'$suffix' => $config['ldap_suffix'],
'$base' => $config['ldap_base'],
'$admin_pw' => $config['ldap_admin_pw'],
));
}
$setup_ldap .= ' '.escapeshellarg($name.'='.$value);
if (!in_array($name,array('domain','ldap_suffix','ldap_admin','ldap_admin_pw')))
{
$extra_config .= ' '.escapeshellarg($name.'='.$value);
}
}
run_cmd($setup_ldap);
}
// enable mcrypt extension eg. for Ubuntu 14.04+
if (!empty($config['php5enmod']))
{
run_cmd($config['php5enmod']);
}
// create header
$setup_header = $setup_cli.' --create-header '.escapeshellarg($config['config_passwd'].','.$config['config_user']).
' --domain '.escapeshellarg($config['domain'].','.$config['db_name'].','.$config['db_user'].','.$config['db_pass'].
','.$config['db_type'].','.$config['db_host'].','.$config['db_port']);
run_cmd($setup_header);
// install egroupware
$setup_install = $setup_cli.' --install '.escapeshellarg($config['domain'].','.$config['config_user'].','.$config['config_passwd'].','.$config['backup'].','.$config['charset'].','.$config['lang'])
.$extra_config;
run_cmd($setup_install);
if ($config['data_dir'] != '/var/lib/egroupware')
{
// set files dir different from default
$setup_config = $setup_cli.' --config '.escapeshellarg($config['domain'].','.$config['config_user'].','.$config['config_passwd']).
' --files-dir '.escapeshellarg($config['data_dir'].'/files').' --backup-dir '.escapeshellarg($config['data_dir'].'/backup');
run_cmd($setup_config);
}
// create mailserver config (fmail requires at least minimal config given as default, otherwise fatal error)
$setup_mailserver = $setup_cli.' --config '.escapeshellarg($config['domain'].','.$config['config_user'].','.$config['config_passwd']);
foreach(array('account-auth','smtpserver','smtp','postfix','mailserver','imap','cyrus','sieve') as $name)
{
if (!empty($config[$name])) $setup_mailserver .= ' --'.$name.' '.escapeshellarg($config[$name]);
}
run_cmd($setup_mailserver);
// create first user
$setup_admin = $setup_cli.' --admin '.escapeshellarg($config['domain'].','.$config['config_user'].','.$config['config_passwd'].','.
$config['admin_user'].','.$config['admin_passwd'].',,,,'.$config['lang']);
run_cmd($setup_admin);
// check if webserver is started and start it (permanent) if not
if ($config['start_webserver'])
{
exec($config['start_webserver'].' status',$dummy,$ret);
if ($ret)
{
system($config['start_webserver'].' start');
system($config['autostart_webserver']);
}
else
{
system($config['start_webserver'].' reload');
}
}
// fix egw_cache evtl. created by root, stoping webserver from accessing it
fix_perms();
echo "\n";
echo "EGroupware successful installed\n";
echo "===============================\n";
echo "\n";
echo "Please note the following user names and passwords:\n";
echo "\n";
echo "Setup username: $config[config_user]\n";
echo " password: $config[config_passwd]\n";
echo "\n";
echo "EGroupware username: $config[admin_user]\n";
echo " password: $config[admin_passwd]\n";
echo "\n";
echo "You can log into EGroupware by pointing your browser to http://localhost/egroupware/\n";
echo "Please replace localhost with the appropriate hostname, if you connect remote.\n\n";
if (empty($config['db_root_pw']))
{
echo "*** Database has no root password set, please fix that immediatly: mysqladmin -u root password NEWPASSWORD\n\n";
}
}
else
{
// --> existing install --> update
// get user from header and replace password, as we dont know it
$old_password = patch_header($config['header'],$config['config_user'],$config['config_passwd']);
// register a shutdown function to put old password back in any case
register_shutdown_function('patch_header',$config['header'],$config['config_user'],$old_password);
// update egroupware
$setup_update = $setup_cli.' --update '.escapeshellarg('all,'.$config['config_user'].','.$config['config_passwd'].',,'.$config['install-update-app']);
$ret = run_cmd($setup_update,$output,array(4,15));
switch($ret)
{
case 4: // header needs an update
$header_update = $setup_cli.' --update-header '.escapeshellarg($config['config_passwd'].','.$config['config_user']);
run_cmd($header_update);
$ret = run_cmd($setup_update,$output,15);
if ($ret != 15) break;
// fall through
case 15: // missing configuration (eg. mailserver)
if (!$verbose) echo implode("\n",(array)$output)."\n";
break;
case 0:
echo "\nEGroupware successful updated\n";
break;
}
// fix egw_cache evtl. created by root, stoping webserver from accessing it
fix_perms();
// restart running Apache, to force APC to update changed sources and/or Apache configuration
$output = array();
run_cmd($config['start_webserver'].' status && '.$config['start_webserver'].' restart', $output, true);
exit($ret);
}
/**
* Patches a given password (for header admin) into the EGroupware header.inc.php and returns the old one
*
* @param string $filename
* @param string &$user username on return(!)
* @param string $password new password
* @return string old password
*/
function patch_header($filename,&$user,$password)
{
$header = file_get_contents($filename);
$umatches = $pmatches = null;
if (!preg_match('/'.preg_quote("\$GLOBALS['egw_info']['server']['header_admin_user'] = '", '/')."([^']+)';/m",$header,$umatches) ||
!preg_match('/'.preg_quote("\$GLOBALS['egw_info']['server']['header_admin_password'] = '", '/')."([^']*)';/m",$header,$pmatches))
{
bail_out(99,"$filename is no regular EGroupware header.inc.php!");
}
file_put_contents($filename,preg_replace('/'.preg_quote("\$GLOBALS['egw_info']['server']['header_admin_password'] = '", '/')."([^']*)';/m",
"\$GLOBALS['egw_info']['server']['header_admin_password'] = '".$password."';",$header));
$user = $umatches[1];
return $pmatches[1];
}
/**
* Runs given shell command, exists with error-code after echoing the output of the failed command (if not already running verbose)
*
* @param string $cmd
* @param array &$output=null $output of command
* @param int|array|true $no_bailout=null exit code(s) to NOT bail out, or true to never bail out
* @return int exit code of $cmd
*/
function run_cmd($cmd,array &$output=null,$no_bailout=null)
{
global $verbose;
if ($verbose)
{
echo $cmd."\n";
$ret = null;
system($cmd,$ret);
}
else
{
$output[] = $cmd;
exec($cmd,$output,$ret);
}
if ($ret && $no_bailout !== true && !in_array($ret,(array)$no_bailout))
{
bail_out($ret,$verbose?null:$output);
}
return $ret;
}
/**
* Stop programm execution with a given exit code and optional extra message
*
* @param int $ret=1
* @param array|string $output line(s) to output before temination notice
*/
function bail_out($ret=1,$output=null)
{
if ($output) echo implode("\n",(array)$output);
echo "\n\nInstallation failed --> exiting!\n\n";
exit($ret);
}
/**
* Return a rand string, eg. to generate passwords
*
* @param int $len=16
* @return string
*/
function randomstring($len=16)
{
static $usedchars = array(
'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f',
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
'w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L',
'M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
'@','!','%','&','(',')','=','?',';',':','#','_','-','<',
'>','|','[',']','}', // dont add /\,'"{ as we have problems dealing with them
);
$str = '';
for($i=0; $i < $len; $i++)
{
$str .= $usedchars[mt_rand(0,count($usedchars)-1)];
}
return $str;
}
/**
* Give usage information and an optional error-message, before stoping program execution with exit-code 90 or 0
*
* @param string $error=null optional error-message
*/
function usage($error=null)
{
global $prog,$config;
echo "Usage: $prog [-h|--help] [-v|--verbose] [--distro=(suse|rh|debian)] [options, ...]\n\n";
echo "options and their defaults:\n";
foreach($config as $name => $default)
{
if (in_array($name, array('postfix','cyrus'))) continue; // do NOT report deprecated options
if (in_array($name,array('config_passwd','db_pass','admin_passwd','ldap_root_pw')))
{
$default = '<16 char random string>';
}
echo '--'.str_pad($name,20).$default."\n";
}
if ($error)
{
echo "$error\n\n";
exit(90);
}
exit(0);
}
/**
* Get installed pear packages, optional from a certain channel
*
* @global type $config
* @param string $channel=''
* @return null|array with package => version
*/
function pear_list($channel='')
{
global $config;
$out = $ret = null;
exec($config['pear'].' list'.($channel?' -c '.$channel:''),$out,$ret);
if ($channel && $ret == 1)
{
return null;
}
if ($ret)
{
echo "Error running pear command ($config[pear])!\n";
exit(95);
}
$packages_installed = array();
foreach($out as $line)
{
$matches = null;
if (preg_match('/^([a-z0-9_]+)\s+([0-9.]+[a-z0-9]*)\s+([a-z]+)/i',$line,$matches))
{
$packages_installed[($channel?$channel.'/':'').$matches[1]] = $matches[2];
}
}
return $packages_installed;
}
/**
* Check if required PEAR packges are installed and install them if not, update pear packages with to low version
*/
function check_install_pear_packages()
{
global $config;
$packages_installed = pear_list();
// some setup files use autoloader
define('EGW_SERVER_ROOT', dirname(dirname(__DIR__)));
define('EGW_INCLUDE_ROOT', EGW_SERVER_ROOT);
define('EGW_API_INC', EGW_SERVER_ROOT.'/phpgwapi/inc');
include_once(EGW_API_INC.'/common_functions.inc.php');
// read required packages from apps
$packages = array('PEAR' => true, 'HTTP_WebDAV_Server' => '999.egw-pear'); // pear must be the first, to run it's update first!
$channels = array();
$egw_pear_packages = array();
$setup_info = array();
foreach(scandir($config['source_dir']) as $app)
{
if (is_dir($dir=$config['source_dir'].'/'.$app) && file_exists($file=$dir.'/setup/setup.inc.php')) include $file;
}
foreach($setup_info as $app => $data)
{
if (isset($data['check_install']))
{
foreach($data['check_install'] as $package => $args)
{
if ($args['func'] == 'pear_check')
{
if (!$package) $package = 'PEAR';
// if package is prefixed with a channel, list or discover it first
if (strpos($package, '/'))
{
list($channel) = explode('/', $package);
if (!in_array($channel, $channels))
{
if (($channel_packages = pear_list($channel)))
{
$packages_installed += $channel_packages;
}
else
{
$discover_cmd = $config['pear'].' channel-discover '.$channel;
echo "$discover_cmd\n"; system($discover_cmd);
}
$channels[] = $channel;
}
}
// only overwrite lower version or no version
if (!isset($packages[$package]) || $packages[$package] === true || isset($args['version']) && version_compare($args['version'],$packages[$package],'>'))
{
$packages[$package] = isset($args['version']) ? $args['version'] : true;
}
}
}
}
if ($app == 'egw-pear')
{
$egw_pear_packages['HTTP_WebDAV_Server'] = $egw_pear_packages['Net_IMAP'] = '999.egw-pear';
}
}
//echo 'Installed: '; print_r($packages_installed);
//echo 'egw-pear: '; print_r($egw_pear_packages);
//echo 'Required: '; print_r($packages);
$to_install = array_diff(array_keys($packages),array_keys($packages_installed),array_keys($egw_pear_packages));
$need_upgrade = array();
foreach($packages as $package => $version)
{
if ($version !== true && $version !== '999.egw-pear' && isset($packages_installed[$package]) &&
version_compare($version, $packages_installed[$package], '>'))
{
$need_upgrade[] = $package;
}
}
//echo 'Need upgrade: '; print_r($need_upgrade);
//echo 'To install: '; print_r($to_install);
if (($to_install || $need_upgrade))
{
if (getmyuid())
{
echo "You need to run as user root to be able to install/upgrade required PEAR packages!\n";
}
else
{
echo "Install/upgrade required PEAR packages:\n";
// need to run upgrades first, they might be required for install!
if ($need_upgrade)
{
if (in_array('PEAR',$need_upgrade)) // updating pear itself can be very tricky, this is what's needed for stock RHEL pear
{
$cmd = $config['pear'].' channel-update pear.php.net';
echo "$cmd\n"; system($cmd);
$cmd = $config['pear'].' upgrade --force Console_Getopt Archive_Tar';
echo "$cmd\n"; system($cmd);
}
$cmd = $config['pear'].' upgrade '.implode(' ',$need_upgrade);
echo "$cmd\n"; system($cmd);
}
if ($to_install)
{
$cmd = $config['pear'].' install '.implode(' ', $to_install);
echo "$cmd\n"; system($cmd);
}
}
}
}
function lang() {} // required to be able to include */setup/setup.inc.php files
/**
* fix egw_cache perms evtl. created by root, stoping webserver from accessing it
*/
function fix_perms()
{
global $config;
if (file_exists('/tmp/egw_cache'))
{
system('/bin/chown -R '.$config['webserver_user'].' /tmp/egw_cache');
system('/bin/chmod 700 /tmp/egw_cache');
}
}