#!/usr/bin/php array ( // users "user" => getmyuid(), "group" => getmygid(), // charset "charset" => "utf-8", // dirs "tmp_dir" => "/var/tmp", "www_dir" => "/var/www", "tpl_dir" => "/usr/local/etc/web-config-templates", "config" => "/usr/local/etc/web_sites.json" ), "httpd" => array ( "user" => 'www-data', "group" => 'www-data', "root" => "/etc/apache2", "logs" => "/var/log/apache2", "enabled_dir" => "sites-enabled", "available_dir" => "sites-available", ), "nginx" => array ( "user" => 'www-data', "group" => 'www-data', "root" => "/etc/nginx", "logs" => "/var/log/nginx", "fpm" => "unix:/var/run/php5-fpm.sock", "enabled_dir" => "sites-enabled", "available_dir" => "sites-available", ), "commands" => array ( "start" => array ( "nginx" => "service nginx start", "apache2" => "service apache2 start", // "fpm" => "service php5-fpm start" ), "stop" => array ( "nginx" => "service nginx stop", "apache2" => "service apache2 stop", // "fpm" => "service php5-fpm stop" ), "restart" => array ( "nginx" => "service nginx restart", "apache2" => "service apache2 restart", // "fpm" => "service php5-fpm restart" ), "reload" => array ( "nginx" => "service nginx reload", "apache2" => "service apache2 restart", // "fpm" => "service php5-fpm reload" ) ), "servers" => array ( // [name, prefix] array ("Локальный (127.0.0.1)", "local"), array ("Глобальный (доступный из интернета)", "global"), ) ); if ($argc < 2) return help(); $sites = load_config($config['common']['config']); $action = strtolower($argv[1]); switch ($action) { case "add": $restart_flag = true; $site = array ( "host" => "*", "port" => 80, "domain" => "", "mail" => "", "fpm_pool" => $config["nginx"]["fpm"], "charset" => $config["common"]["charset"], "www_dir" => $config["common"]["www_dir"], "www_dir_uid" => $config["common"]["user"], "www_dir_gid" => $config["common"]["group"], "is_static" => false, // false, "is_default" => false, "is_enabled" => true, ); echo "\033[1;34mДомен: \033[0m"; $site['domain'] = preg_replace("/[^a-z0-9\-\_\.]/i", "", trim(read_stdin($site['domain']))); $domain_lc = strtolower($site['domain']); if (empty($site['domain'])) return display_error("Не введён домен!"); if (isset($sites[$domain_lc])) return display_error("Домен %s уже существует!", $domain_lc); $site['www_dir'] .= '/'.$domain_lc; $site['mail'] = "admin@".$domain_lc; echo "\033[1;34mПорт [".$site['port']."]: \033[0m"; $site['port'] = (int)read_stdin($site['port']); if ($site['port'] < 0x0000 || $site['port'] > 0xFFFF) return display_error("Не правильно введён номер порта!\n"); echo "\033[1;34mАльясы (через запятую): \033[0m"; $site['aliases'] = parse_aliases(read_stdin(), $site['domain']); echo "\033[1;34mКорневой каталог [".$site['www_dir']."]: \033[0m"; $site['www_dir'] = read_stdin($site['www_dir']); echo "\033[1;34mUID корневого каталога [".$site['www_dir_uid']."]: \033[0m"; $site['www_dir_uid'] = read_stdin($site['www_dir_uid']); echo "\033[1;34mGID корневого каталога [".$site['www_dir_gid']."]: \033[0m"; $site['www_dir_gid'] = read_stdin($site['www_dir_gid']); echo "\033[1;34mКодировка [".$site['charset']."]: \033[0m"; $site['charset'] = read_stdin($site['charset']); echo "\033[1;34mE-mail [".$site['mail']."]: \033[0m"; $site['mail'] = read_stdin($site['mail']); echo "\033[1;34mСервер [".$site['host']."] (* - любой): \033[0m"; $site['host'] = read_stdin($site['host']); if ($site['host'] == '*') $site['host'] = ''; // echo "\033[1;34mFPM pool [".$site['fpm_pool']."]: \033[0m"; // $site['fpm_pool'] = read_stdin($site['fpm_pool']); $def = ($site['is_static'] ? 'yes' : 'no'); echo "\033[1;34mСоздать сайт для отдачи статики (yes/no)? [".$def."]: \033[0m"; $site['is_static'] = (strcasecmp("yes", read_stdin($def)) == 0); $def = ($site['is_enabled'] ? 'yes' : 'no'); echo "\033[1;34mВключить сайт после создания (yes/no)? [".$def."]: \033[0m"; $site['is_enabled'] = (strcasecmp("yes", read_stdin($def)) == 0); $def = ($restart_flag ? 'yes' : 'no'); echo "\033[1;34mПерезагрузить сервер после создания (yes/no)? [".$def."]: \033[0m"; $restart_flag = (strcasecmp("yes", read_stdin($def)) == 0); $def = ($site['is_default'] ? 'yes' : 'no'); echo "\033[1;34mСделать этот сайт сайтом по-умолчанию (yes/no)? [".$def."]: \033[0m"; $site['is_default'] = (strcasecmp("yes", read_stdin($def)) == 0); add_site($site); if ($site['is_enabled']) enable_site($domain_lc); write_config($config['common']['config'], $sites); if ($restart_flag) exec_command("restart"); echo "\033[1;32mСайт успешно создан!\033[0;0m\n"; echo "Адрес: http://".$site['domain'].":".$site['port']."\n"; echo "Каталог: ".$site['www_dir']."\n"; break; case "edit": $domain_lc = isset($argv[2]) ? strtolower($argv[2]) : ''; if (!$domain_lc || !isset($sites[$domain_lc])) display_error("Сайт %s не найден!", $domain_lc); $restart_flag = true; $site = &$sites[$domain_lc]; echo "\033[1;34mПорт [".$site['port']."]: \033[0m"; $site['port'] = (int)read_stdin($site['port']); if ($site['port'] < 0x0000 || $site['port'] > 0xFFFF) return display_error("Не правильно введён номер порта!\n"); $def = implode(', ', $site['aliases']); echo "\033[1;34mАльясы (через запятую) [".$def."]: \033[0m"; $site['aliases'] = parse_aliases(read_stdin($def), $site['domain']); echo "\033[1;34mКорневой каталог [".$site['www_dir']."]: \033[0m"; $site['www_dir'] = read_stdin($site['www_dir']); echo "\033[1;34mUID корневого каталога [".$site['www_dir_uid']."]: \033[0m"; $site['www_dir_uid'] = read_stdin($site['www_dir_uid']); echo "\033[1;34mGID корневого каталога [".$site['www_dir_gid']."]: \033[0m"; $site['www_dir_gid'] = read_stdin($site['www_dir_gid']); echo "\033[1;34mКодировка [".$site['charset']."]: \033[0m"; $site['charset'] = read_stdin($site['charset']); echo "\033[1;34mE-mail [".$site['mail']."]: \033[0m"; $site['mail'] = read_stdin($site['mail']); echo "\033[1;34mСервер [".$site['host']."] (* - любой): \033[0m"; $site['host'] = read_stdin($site['host']); if ($site['host'] == '*') $site['host'] = ''; // echo "\033[1;34mFPM pool [".$site['fpm_pool']."]: \033[0m"; // $site['fpm_pool'] = read_stdin($site['fpm_pool']); $def = ($site['is_static'] ? 'yes' : 'no'); echo "\033[1;34mСайт для отдачи статики (yes/no)? [".$def."]: \033[0m"; $site['is_static'] = (strcasecmp("yes", read_stdin($def)) == 0); $def = ($restart_flag ? 'yes' : 'no'); echo "\033[1;34mПерезагрузить сервер после редактирования (yes/no)? [".$def."]: \033[0m"; $restart_flag = (strcasecmp("yes", read_stdin($def)) == 0); $def = ($site['is_default'] ? 'yes' : 'no'); echo "\033[1;34mСделать этот сайт сайтом по-умолчанию (yes/no)? [".$def."]: \033[0m"; $site['is_default'] = (strcasecmp("yes", read_stdin($def)) == 0); add_site($site); write_config($config['common']['config'], $sites); if ($restart_flag) exec_command("restart"); echo "\033[1;32mСайт успешно изменён!\033[0;0m\n"; echo "Адрес: http://".$site['domain'].":".$site['port']."\n"; echo "Каталог: ".$site['www_dir']."\n"; break; case "restore": if (isset($argv[2])) { $domain_lc = strtolower($argv[2]); if (isset($sites[$domain_lc])) $sites_to_restore = array($domain_lc => &$sites[$domain_lc]); else display_error("Сайт %s не найден!", $domain_lc); } else $sites_to_restore = &$sites; foreach ($sites_to_restore as $name => $site) { echo "\033[1;33mВосстанавливаем:\033[0;0m ".$name."\n"; add_site($site); if ($site['is_enabled']) enable_site($site['domain']); echo "\033[1;32mСайт восстановлен!\033[0;0m\n\n"; } write_config($config['common']['config'], $sites); exec_command("restart"); break; case "list": if (!$sites) display_error("Сайтов ещё нет!"); foreach ($sites as $name => $site) { echo $name.($site['is_static'] ? " [\033[1;34mstatic\033[0;0m]" : ''); echo ($site['is_default'] ? " [\033[1;34mdefault\033[0;0m]" : ''); echo " [".($site['is_enabled'] ? "\033[1;32mON\033[0;0m" : "\033[1;31mOFF\033[0;0m")."]\n"; echo " \033[1;34mАдрес:\033[0;0m http://".$site['domain'].":".$site['port']."\n"; echo " \033[1;34mКаталог:\033[0;0m ".$site['www_dir']." [".$site['www_dir_uid'].":".$site['www_dir_gid']."]\n"; echo " \033[1;34mКодировка:\033[0;0m ".$site['charset']."\n"; // echo " \033[1;34mFPM pool:\033[0;0m ".$site['fpm_pool']."\n"; echo " \033[1;34mE-mail:\033[0;0m ".$site['mail']."\n"; echo " \033[1;34mСервер:\033[0;0m ".$site['host']."\n"; if ($site['aliases']) { echo " \033[1;34mАльясы:\033[0;0m\n"; foreach ($site['aliases'] as $alias) echo " http://".$alias.":".$site['port']."\n"; } } break; case "enable": if (!isset($argv[2])) display_error("Сайт не найден!\n"); enable_site($argv[2]); write_config($config['common']['config'], $sites); exec_command("restart"); echo "Сайт включен!\n"; break; case "delete": if (!isset($argv[2])) display_error("Не указан сайт!"); echo "\033[1;33mВы уверенны, что хотите удалить сайт \"".$argv[2]."\" (yes/no)? [yes]: \033[0m"; if (strcasecmp("yes", read_stdin('yes')) != 0) exit; disable_site($argv[2]); delete_site($argv[2], (isset($argv[3]) && $argv[3] == 'force')); write_config($config['common']['config'], $sites); echo "\033[1;31mСайт успешно удалён!\033[0m\n"; break; case "disable": if (!isset($argv[2])) display_error("Сайт не найден!\n"); disable_site($argv[2]); write_config($config['common']['config'], $sites); exec_command("restart"); echo "Сайт выключен!\n"; break; case "restart": case "start": case "reload": case "stop": $target = isset($argv[2]) ? $argv[2] : NULL; exec_command($action, $target); break; default: echo "Неизвестная комманда: \033[1;32m".$argv[1]."\033[0;0m\n"; echo "Используйте ".$argv[0]." \033[1;32mhelp\033[0;0m для получения справки. \n"; break; } // Удалить сайт function delete_site($site, $force) { global $sites, $config; $site = strtolower($site); if (isset($sites[$site])) { $domain_lc = strtolower($sites[$site]['domain']); $dirs = array ( array ( 'path' => $config['nginx']['root'].'/'.$config['nginx']['available_dir'].'/'.$domain_lc.'-conf.d', 'force' => true ), array ( 'path' => $config['httpd']['root'].'/'.$config['httpd']['available_dir'].'/'.$domain_lc.'-conf.d', 'force' => true ), array ( 'path' => $config['nginx']['logs'].'/'.$domain_lc, 'force' => false ), array ( 'path' => $config['httpd']['logs'].'/'.$domain_lc, 'force' => false ), array ( 'path' => $config['common']['www_dir'].'/'.$domain_lc, 'force' => true ), array ( 'path' => $config['common']['tmp_dir'].'/'.$domain_lc, 'force' => false ), array( 'path' => $config['httpd']['root'].'/'.$config['httpd']['available_dir'].'/'.$domain_lc.'-vhost.conf', 'force' => false ), array( 'path' => $config['nginx']['root'].'/'.$config['nginx']['available_dir'].'/'.$domain_lc.'-vhost.conf', 'force' => false ) ); foreach ($dirs as $dir) { if (!$dir['force'] || $force) { echo " Удаление \033[1;34m".$dir['path']."\033[0;0m"; if (file_exists($dir['path'])) { if (is_dir($dir['path'])) { if (!fs_delete_recursive($dir['path'])) die(": Невозможно удалить папку!\n"); } else { if (!unlink($dir['path'])) die(": Невозможно удалить файл!\n"); } echo "\n"; } else echo " [\033[1;33mnot exists\033[0;0m]\n"; } } unset($sites[$site]); } else display_error(" Сайт %s не найден!\n", $site); } // Добавить сайт function add_site($site) { global $config, $sites; $domain_lc = strtolower($site['domain']); $dirs = array ( array ( 'path' => $config['nginx']['root'].'/'.$config['nginx']['available_dir'].'/'.$domain_lc.'-conf.d', 'gid' => $config['nginx']['user'], 'uid' => $config['nginx']['group'], 'chmod' => 0755 ), array ( 'path' => $config['httpd']['root'].'/'.$config['httpd']['available_dir'].'/'.$domain_lc.'-conf.d', 'gid' => $config['httpd']['user'], 'uid' => $config['httpd']['group'], 'chmod' => 0755 ), array ( 'path' => $config['nginx']['logs'].'/'.$domain_lc, 'gid' => $config['nginx']['user'], 'uid' => $config['nginx']['group'], 'chmod' => 0755 ), array ( 'path' => $config['httpd']['logs'].'/'.$domain_lc, 'gid' => $config['httpd']['user'], 'uid' => $config['httpd']['group'], 'chmod' => 0755 ), array ( 'path' => $config['common']['www_dir'].'/'.$domain_lc, 'gid' => $config['common']['user'], 'uid' => $config['common']['group'], 'chmod' => 0755 ), array ( 'path' => $config['common']['tmp_dir'].'/'.$domain_lc, 'gid' => $config['common']['user'], 'uid' => $config['common']['group'], 'chmod' => 0777 ), ); foreach ($dirs as $dir) { echo " Создание \033[1;34m".$dir['path']."\033[0;0m"; if (!file_exists($dir['path'])) { if (!mkdir($dir['path'], $dir['chmod'], true)) die(": Невозможно создать каталог!\n"); echo "\n"; } else echo " [\033[1;32mexists\033[0;0m]\n"; if (!chmod($dir['path'], $dir['chmod'])) die(" Невозможно задать CHMOD!\n"); if (!chown($dir['path'], $dir['uid'])) die(" Невозможно задать UID!\n"); if (!chgrp($dir['path'], $dir['gid'])) die(" Невозможно задать GID!\n"); } $nginx_template = "nginx-static.conf"; if (!$site['is_static']) { echo " Создание виртуального хоста apache...\n"; write_file ( $config['httpd']['root'].'/'.$config['httpd']['available_dir'].'/'.$domain_lc.'-vhost.conf', tpl ( $config['common']['tpl_dir']."/apache.conf", array ( 'site' => $site, 'logs_dir' => $config['httpd']['logs'].'/'.$domain_lc, 'cfg_root' => $config['httpd']['root'].'/'.$config['httpd']['available_dir'].'/'.$domain_lc.'-conf.d', 'tmp_dir' => $config['common']['tmp_dir'].'/'.$domain_lc, ) ) ); $nginx_template = "nginx-apache.conf"; } echo " Создание виртуального хоста nginx...\n"; write_file ( $config['nginx']['root'].'/'.$config['nginx']['available_dir'].'/'.$domain_lc.'-vhost.conf', tpl ( $config['common']['tpl_dir']."/".$nginx_template, array ( 'site' => $site, 'logs_dir' => $config['nginx']['logs'].'/'.$domain_lc, 'cfg_root' => $config['nginx']['root'].'/'.$config['nginx']['available_dir'].'/'.$domain_lc.'-conf.d' ) ) ); $sites[$domain_lc] = $site; } // Включить сайт function enable_site($site) { global $config, $sites; $site = strtolower($site); if (isset($sites[$site])) { $sites[$site]['host'] = $sites[$site]['host'] ? $sites[$site]['host'] : '127.0.0.1'; hosts_add($sites[$site]['domain'], '127.0.0.1'); foreach ($sites[$site]['aliases'] as $alias) hosts_add($alias, $sites[$site]['domain']); if (!file_exists($config['nginx']['root'].'/'.$config['nginx']['enabled_dir'].'/'.$site.'-vhost.conf')) { symlink ( $config['nginx']['root'].'/'.$config['nginx']['available_dir'].'/'.$site.'-vhost.conf', $config['nginx']['root'].'/'.$config['nginx']['enabled_dir'].'/'.$site.'-vhost.conf' ); } if (!$sites[$site]['is_static'] && !file_exists($config['httpd']['root'].'/'.$config['httpd']['enabled_dir'].'/'.$site.'-vhost.conf')) { symlink ( $config['httpd']['root'].'/'.$config['httpd']['available_dir'].'/'.$site.'-vhost.conf', $config['httpd']['root'].'/'.$config['httpd']['enabled_dir'].'/'.$site.'-vhost.conf' ); } $sites[$site]['is_enabled'] = true; } else display_error("Сайт \"%s\" не найден!\n", $site); } // Выключить сайт function disable_site($site) { global $config, $sites; $site = strtolower($site); if (isset($sites[$site])) { hosts_delete($sites[$site]['domain']); foreach ($sites[$site]['aliases'] as $alias) hosts_delete($alias); if (file_exists($config['nginx']['root'].'/'.$config['nginx']['enabled_dir'].'/'.$site.'-vhost.conf')) unlink ($config['nginx']['root'].'/'.$config['nginx']['enabled_dir'].'/'.$site.'-vhost.conf'); if (file_exists($config['httpd']['root'].'/'.$config['httpd']['enabled_dir'].'/'.$site.'-vhost.conf')) unlink ($config['httpd']['root'].'/'.$config['httpd']['enabled_dir'].'/'.$site.'-vhost.conf'); $sites[$site]['is_enabled'] = false; } else display_error("Сайт \"%s\" не найден!\n", $site); } function display_error() { $err = call_user_func_array("sprintf", func_get_args()); die("\033[1;31m".str_replace("\033", "", $err)."\033[0;0m\n"); } // Обработать шаблон function tpl($file, $vars) { if (is_array($vars)) extract($vars, EXTR_REFS); ob_start(); include $file; return ob_get_clean(); } // Распарсить альясы function parse_aliases($data, $domain) { $aliases = explode(',', $data); $site_aliases = array(); foreach ($aliases as $alias) { $alias = preg_replace("/[^a-z0-9\-\_\.]/i", "", trim($alias)); if (!empty($alias) && strcasecmp($domain, $alias) != 0 && !in_array($alias, $site_aliases)) $site_aliases[] = $alias; } unset($aliases); return $site_aliases; } // Выполнить команду function exec_command($action, $target = NULL) { global $config; if ($target != NULL) { if (isset($config['commands'][$action], $config['commands'][$action][$target])) { echo "\033[1;33mВыполнение команды \033[1;32m".$action."\033[1;33m для \033[1;32m".$target."\033[1;33m:\033[0;0m\n"; exec($config['commands'][$action][$target]." 2&>1"); } else display_error("Цель '%s' или действие '%s' не найдено!", $target, $action); } else { if (isset($config['commands'][$action])) { foreach ($config['commands'][$action] as $name => $target) exec_command($action, $name); } else display_error("Действие '%s' не найдено!", $action); } } // Добавить сайт в hosts function hosts_add($domain, $ip) { $fp = fopen("/etc/hosts", "r+") or die("Не могу открыть hosts!\n"); flock($fp, LOCK_EX) or die("Не могу заблокировать hosts\n"); clearstatcache(); $data = fread($fp, filesize("/etc/hosts")); $is_exists = false; preg_match_all("/(^|\r?\n)([\d]{0,3}\.[\d]{0,3}\.[\d]{0,3}\.[\d]{0,3})[\s]+([^\r\n]+)/si", $data, $rows, PREG_OFFSET_CAPTURE); foreach ($rows[3] as $i => $host) { if (strcasecmp($host[0], $domain) == 0) { $data = substr_replace($data, $ip, $rows[2][$i][1], strlen($rows[2][$i][0])); $is_exists = true; break; } } if (!$is_exists) $data = trim($data)."\n".$ip."\t".$domain."\n"; fseek($fp, 0); ftruncate($fp, 0); fwrite($fp, $data); flock($fp, LOCK_UN); fclose($fp); } // Удалить сайт из hosts function hosts_delete($domain) { $fp = fopen("/etc/hosts", "r+") or die("Не могу открыть hosts!\n"); flock($fp, LOCK_EX) or die("Не могу заблокировать hosts\n"); clearstatcache(); $data = fread($fp, filesize("/etc/hosts")); $domain = strtolower($domain); fseek($fp, 0); ftruncate($fp, 0); preg_match_all("/(^|\r?\n)([\d]{0,3}\.[\d]{0,3}\.[\d]{0,3}\.[\d]{0,3})[\s]+([^\r\n]+)/si", $data, $rows, PREG_OFFSET_CAPTURE); foreach ($rows[3] as $i => $host) { $host[0] = strtolower($host[0]); if ($host[0] == $domain) { $data = substr_replace($data, '', $rows[0][$i][1], strlen($rows[0][$i][0])); break; } } fwrite($fp, $data); flock($fp, LOCK_UN); fclose($fp); } // Прочитать строку из stdin function read_stdin($def_value = '', $size = 1024) { $fp = fopen('php://stdin', 'r'); $data = trim(fgets($fp, $size)); fclose($fp); return strlen($data) > 0 ? $data : $def_value; } // Рекурсивно удалить папку function fs_delete_recursive($path) { $files = glob($path.'/*'); $state = true; foreach ($files as $file) if (!(is_dir($file) ? fs_delete_recursive($file) : unlink($file))) $state = false; if (!rmdir($path)) $state = false; return $state; } // Загрузить конфиг function load_config($path) { $config = array(); if (file_exists($path) && is_readable($path)) { clearstatcache(); $size = filesize($path); $fp = fopen($path, "r") or die("Не могу открыть файл ".$path." для чтения!\n"); flock($fp, LOCK_EX) or die("Не могу заблокировать файл ".$path."!\n"); $config = json_decode(fread($fp, $size), true); flock($fp, LOCK_UN); fclose($fp); } return $config === false ? array() : $config; } // Записать конфиг function write_config($file, $data) { write_file($file, json_encode($data)); } // Записать файл function write_file($file, $data) { $fp = fopen($file, "w+") or die("Не могу открыть файл ".$file." для записи!\n"); flock($fp, LOCK_EX) or die("Не могу заблокировать файл ".$file."!\n"); fwrite($fp, $data); flock($fp, LOCK_UN); fclose($fp); } function help() { return 0; } ?>