diff --git a/Makefile b/Makefile
index 1fd47e2a..98a1c363 100644
--- a/Makefile
+++ b/Makefile
@@ -24,7 +24,7 @@ LB_DIRS := bin cli config content core domain includes \
infrastructure resources signals streaming tmp www
# Root-level files to copy from MAIN to LB (not inside directories)
-LB_ROOT_FILES := autoload.php bootstrap.php console.php service status update
+LB_ROOT_FILES := autoload.php bootstrap.php console.php service update
# Directories to remove from LB (admin-only content)
LB_DIRS_TO_REMOVE := \
@@ -404,9 +404,7 @@ set_permissions:
# Root-level executables
chmod 0750 $(TEMP_DIR)/service 2>/dev/null || true
- chmod 0750 $(TEMP_DIR)/status 2>/dev/null || true
chmod 0755 $(TEMP_DIR)/tmp 2>/dev/null || true
- chmod 0750 $(TEMP_DIR)/tools 2>/dev/null || true
chmod 0750 $(TEMP_DIR)/update 2>/dev/null || true
chmod 0750 $(TEMP_DIR)/signals 2>/dev/null || true
diff --git a/docs/en/administration/update-system.md b/docs/en/administration/update-system.md
index 428497e0..7c3977e9 100644
--- a/docs/en/administration/update-system.md
+++ b/docs/en/administration/update-system.md
@@ -121,7 +121,7 @@ Final steps are executed in the `post-update` phase of `UpdateCommand`:
6. Verify panel status:
```bash
- sudo /home/xc_vm/status
+ sudo /home/xc_vm/bin/php/bin/php /home/xc_vm/console.php status
```
7. Mark the update process as complete.
diff --git a/docs/en/info/migration_guide.md b/docs/en/info/migration_guide.md
index ae34f43e..077f2b2c 100644
--- a/docs/en/info/migration_guide.md
+++ b/docs/en/info/migration_guide.md
@@ -111,13 +111,13 @@ If login fails due to missing credentials or access code, use the rescue tools.
### Create a Rescue Access Code
```bash
-/home/xc_vm/tools access
+php /home/xc_vm/console.php tools access
```
### Create an Administrator Account
```bash
-/home/xc_vm/tools user
+php /home/xc_vm/console.php tools user
```
> ⚠️ After regaining access, **immediately change** the access code and administrator credentials.
diff --git a/docs/ru/administration/update-system.md b/docs/ru/administration/update-system.md
index da1b9ffe..4c4a8fc4 100644
--- a/docs/ru/administration/update-system.md
+++ b/docs/ru/administration/update-system.md
@@ -124,7 +124,7 @@ sudo /usr/bin/python3 /home/xc_vm/update "/home/xc_vm/tmp/.update.tar.gz" "HASH"
6. Проверка статуса панели:
```bash
- sudo /home/xc_vm/status
+ sudo /home/xc_vm/bin/php/bin/php /home/xc_vm/console.php status
```
7. Завершение процесса обновления.
diff --git a/docs/ru/info/migration_guide.md b/docs/ru/info/migration_guide.md
index ca2ed758..a55908f6 100644
--- a/docs/ru/info/migration_guide.md
+++ b/docs/ru/info/migration_guide.md
@@ -111,13 +111,13 @@ sudo /home/xc_vm/bin/php/bin/php /home/xc_vm/console.php tools migration "/tmp/b
### Создание резервного кода доступа
```bash
-/home/xc_vm/tools access
+php /home/xc_vm/console.php tools access
```
### Создание администратора
```bash
-/home/xc_vm/tools user
+php /home/xc_vm/console.php tools user
```
> ⚠️ После восстановления доступа **немедленно смените** код доступа и учётные данные администратора.
diff --git a/install b/install
index 9bbfa7a9..f9a76e52 100644
--- a/install
+++ b/install
@@ -291,7 +291,7 @@ if __name__ == "__main__":
printc("Installing XC_VM")
if os.path.exists("./xc_vm.tar.gz"):
os.system('sudo tar -zxvf "./xc_vm.tar.gz" -C "/home/xc_vm/"')
- if not os.path.exists("/home/xc_vm/status"):
+ if not os.path.exists("/home/xc_vm/console.php"):
printc("Failed to extract! Exiting")
sys.exit(1)
@@ -520,7 +520,7 @@ if __name__ == "__main__":
time.sleep(10)
- os.system("sudo /home/xc_vm/status 1")
+ os.system("sudo /home/xc_vm/bin/php/bin/php /home/xc_vm/console.php status 1")
os.system("sudo chown xc_vm:xc_vm -R /home/xc_vm/config/")
os.system(
"sudo /home/xc_vm/bin/php/bin/php /home/xc_vm/console.php startup >/dev/null 2>&1"
diff --git a/src/cli/Commands/BalancerCommand.php b/src/cli/Commands/BalancerCommand.php
index ad3adccd..a0aa1a97 100644
--- a/src/cli/Commands/BalancerCommand.php
+++ b/src/cli/Commands/BalancerCommand.php
@@ -139,9 +139,10 @@ class BalancerCommand implements CommandInterface {
if ($rType == 1) {
if ($this->sendFileSSH($rConn, $rInstallDir . $rInstallFiles, '/tmp/' . $rInstallFiles, true)) {
echo "Extracting to directory\n";
- $this->runSSH($rConn, 'sudo rm -rf ' . MAIN_HOME . 'status');
+ $this->runSSH($rConn, 'sudo rm -rf ' . MAIN_HOME . 'console.php');
$this->runSSH($rConn, 'sudo tar -zxvf "/tmp/' . $rInstallFiles . '" -C "' . MAIN_HOME . '"');
- if (!file_exists(MAIN_HOME . 'status')) {
+ $rRemoteCheck = trim($this->runSSH($rConn, 'test -f ' . MAIN_HOME . 'console.php && echo OK')['output']);
+ if ($rRemoteCheck !== 'OK') {
$db->query('UPDATE `servers` SET `status` = 4 WHERE `id` = ?;', $rServerID);
echo "Failed to extract files! Exiting\n";
return 1;
@@ -157,9 +158,10 @@ class BalancerCommand implements CommandInterface {
$fileHash = $this->runSSH($rConn, 'md5=($(md5sum /tmp/XC_VM.tar.gz)); echo $md5;');
if (!empty($fileHash['output']) && $hash == trim($fileHash['output'])) {
echo "Extracting to directory\n";
- $this->runSSH($rConn, 'sudo rm -rf ' . MAIN_HOME . 'status');
+ $this->runSSH($rConn, 'sudo rm -rf ' . MAIN_HOME . 'console.php');
$this->runSSH($rConn, 'sudo tar -zxvf "/tmp/XC_VM.tar.gz" -C "' . MAIN_HOME . '"');
- if (file_exists(MAIN_HOME . 'status')) {
+ $rRemoteCheck = trim($this->runSSH($rConn, 'test -f ' . MAIN_HOME . 'console.php && echo OK')['output']);
+ if ($rRemoteCheck === 'OK') {
$this->runSSH($rConn, 'sudo rm -f "/tmp/XC_VM.tar.gz"');
} else {
$db->query('UPDATE `servers` SET `status` = 4 WHERE `id` = ?;', $rServerID);
@@ -174,7 +176,7 @@ class BalancerCommand implements CommandInterface {
}
if ($rType == 2) {
- if (stripos($this->runSSH($rConn, 'sudo cat /etc/fstab')['output'], STREAMS_PATH) !== true) {
+ if (stripos($this->runSSH($rConn, 'sudo cat /etc/fstab')['output'], STREAMS_PATH) === false) {
echo "Adding ramdisk mounts\n";
$this->runSSH($rConn, 'sudo echo "tmpfs ' . STREAMS_PATH . ' tmpfs defaults,noatime,nosuid,nodev,noexec,mode=1777,size=90% 0 0" >> /etc/fstab');
$this->runSSH($rConn, 'sudo echo "tmpfs ' . TMP_PATH . ' tmpfs defaults,noatime,nosuid,nodev,noexec,mode=1777,size=2G 0 0" >> /etc/fstab');
@@ -300,7 +302,7 @@ class BalancerCommand implements CommandInterface {
$this->runSSH($rConn, 'sudo service xc_vm restart');
if ($rType == 2) {
- $this->runSSH($rConn, 'sudo ' . MAIN_HOME . 'status 1');
+ $this->runSSH($rConn, 'sudo ' . PHP_BIN . ' ' . MAIN_HOME . 'console.php status 1');
$this->runSSH($rConn, 'sudo -u xc_vm ' . PHP_BIN . ' ' . MAIN_HOME . 'console.php startup');
$this->runSSH($rConn, 'sudo -u xc_vm ' . PHP_BIN . ' ' . MAIN_HOME . 'console.php cron:servers');
} else {
diff --git a/src/cli/Commands/StartupCommand.php b/src/cli/Commands/StartupCommand.php
index 282fc695..4041486f 100644
--- a/src/cli/Commands/StartupCommand.php
+++ b/src/cli/Commands/StartupCommand.php
@@ -29,9 +29,7 @@ class StartupCommand implements CommandInterface {
ini_set('display_startup_errors', 1);
error_reporting(32767);
- if (file_exists(MAIN_HOME . 'status')) {
- exec('sudo ' . MAIN_HOME . 'status 1');
- }
+ exec('sudo ' . PHP_BIN . ' ' . MAIN_HOME . 'console.php status 1');
// ── Восстановление daemons.sh при повреждении ────────
if (filesize(MAIN_HOME . 'bin/daemons.sh') == 0) {
diff --git a/src/cli/Commands/UpdateCommand.php b/src/cli/Commands/UpdateCommand.php
index 9d2a86ab..e1acf605 100644
--- a/src/cli/Commands/UpdateCommand.php
+++ b/src/cli/Commands/UpdateCommand.php
@@ -79,7 +79,7 @@ class UpdateCommand implements CommandInterface {
exec('sudo chown -R xc_vm:xc_vm ' . MAIN_HOME);
exec('sudo systemctl daemon-reload');
exec("sudo echo 'net.ipv4.ip_unprivileged_port_start=0' > /etc/sysctl.d/50-allports-nonroot.conf && sudo sysctl --system");
- exec('sudo ' . MAIN_HOME . 'status');
+ exec('sudo ' . PHP_BIN . ' ' . MAIN_HOME . 'console.php status');
break;
}
diff --git a/src/crons/epg.php b/src/crons/epg.php
deleted file mode 100644
index 081b6325..00000000
--- a/src/crons/epg.php
+++ /dev/null
@@ -1,566 +0,0 @@
-loadEPG($rSource, $rCache);
- }
-
- public function getData() {
- $rOutput = [];
- $channelCount = 0;
-
- print_log("[EPG] Starting getData() - parsing channels and languages...");
-
- while ($rNode = $this->rEPGSource->getNode()) {
- $rData = simplexml_load_string($rNode);
- if (!$rData) continue;
-
- $rNodeName = $rData->getName();
-
- if ($rNodeName === 'channel') {
- $rChannelID = trim((string) $rData->attributes()->id);
- $displayName = !empty($rData->{'display-name'}) ? trim((string) $rData->{'display-name'}) : 'Unknown';
-
- if (!array_key_exists($rChannelID, $rOutput)) {
- $rOutput[$rChannelID] = [
- 'display_name' => $displayName,
- 'langs' => []
- ];
- $channelCount++;
- }
- continue;
- }
-
- // ---------- PROGRAMME ----------
- if ($rNodeName !== 'programme') {
- continue;
- }
-
- $rChannelID = trim((string) $rData->attributes()->channel);
-
- if (!array_key_exists($rChannelID, $rOutput)) {
- continue;
- }
-
- if (empty($rData->title)) {
- continue;
- }
-
- foreach ($rData->title as $rTitle) {
- $lang = (string) $rTitle->attributes()->lang;
- if (!empty($lang) && !in_array($lang, $rOutput[$rChannelID]['langs'], true)) {
- $rOutput[$rChannelID]['langs'][] = $lang;
- }
- }
- }
-
- print_log("[EPG] Finished getData() - found $channelCount channels");
- return $rOutput;
- }
-
- public function parseEPG($rEPGID, $rChannelInfo, $rOffset = 0) {
- global $db;
-
- $rInsertQuery = [];
- $programCount = 0;
-
- print_log("[EPG] Starting parseEPG() for EPG ID: $rEPGID (offset: {$rOffset}min)");
-
- while ($rNode = $this->rEPGSource->getNode()) {
- $rData = simplexml_load_string($rNode);
- if (!$rData) {
- continue;
- }
-
- if ($rData->getName() !== 'programme') {
- continue;
- }
-
- $rChannelID = (string) $rData->attributes()->channel;
-
- if (!array_key_exists($rChannelID, $rChannelInfo)) {
- continue;
- }
-
- // --- timestamps ---
- $rStart = strtotime((string) $rData->attributes()->start) + ($rOffset * 60);
- $rStop = strtotime((string) $rData->attributes()->stop) + ($rOffset * 60);
-
- if ($rStart === false || $rStop === false) {
- print_log("[EPG] Warning: Invalid timestamp for channel $rChannelID");
- continue;
- }
-
- $rLangTitle = '';
- $rLangDesc = '';
-
- // Title
- if (!empty($rData->title)) {
- $rTitles = $rData->title;
- $preferredLang = $rChannelInfo[$rChannelID]['epg_lang'];
-
- if (is_object($rTitles)) {
- $rFound = false;
- foreach ($rTitles as $rTitle) {
- if ((string) $rTitle->attributes()->lang === $preferredLang) {
- $rLangTitle = (string) $rTitle;
- $rFound = true;
- break;
- }
- }
- if (!$rFound && count($rTitles) > 0) {
- $rLangTitle = (string) $rTitles[0];
- }
- } else {
- $rLangTitle = (string) $rTitles;
- }
- } else {
- continue;
- }
-
- // Description
- if (!empty($rData->desc)) {
- $rDescriptions = $rData->desc;
- $preferredLang = $rChannelInfo[$rChannelID]['epg_lang'];
-
- if (is_object($rDescriptions)) {
- $rFound = false;
- foreach ($rDescriptions as $rDescription) {
- if ((string) $rDescription->attributes()->lang === $preferredLang) {
- $rLangDesc = (string) $rDescription;
- $rFound = true;
- $rLangDesc = $rDescription;
- break;
- }
- }
- if (!$rFound && count($rDescriptions) > 0) {
- $rLangDesc = (string) $rDescriptions[0];
- }
- } else {
- $rLangDesc = (string) $rDescriptions;
- }
- }
-
- $rInsertQuery[] = '(' .
- $db->escape($rEPGID) . ', ' .
- $db->escape($rChannelID) . ', ' .
- intval($rStart) . ', ' .
- intval($rStop) . ', ' .
- $db->escape($rChannelInfo[$rChannelID]['epg_lang']) . ', ' .
- $db->escape($rLangTitle) . ', ' .
- $db->escape($rLangDesc ?? '') .
- ')';
-
- $programCount++;
- if ($programCount % 1000 === 0) {
- print_log("[EPG] Parsed $programCount programmes so far...");
- }
- }
-
- print_log("[EPG] Finished parseEPG() - collected $programCount programmes");
- return !empty($rInsertQuery) ? $rInsertQuery : false;
- }
-
- public function downloadFile($rSource, $rFilename) {
- print_log("[EPG] Downloading EPG file: $rSource");
-
- $rExtension = pathinfo($rSource, PATHINFO_EXTENSION);
- $rDecompress = '';
-
- if ($rExtension === 'gz') {
- $rDecompress = ' | gunzip -c';
- } elseif ($rExtension === 'xz') {
- $rDecompress = ' | unxz -c';
- }
-
- $rCommand = 'wget -U "Mozilla/5.0" --timeout=30 --tries=3 -O - "' . $rSource . '"' . $rDecompress . ' > ' . $rFilename;
- $rResult = shell_exec($rCommand);
-
- if (file_exists($rFilename) && filesize($rFilename) > 0) {
- print_log("[EPG] Download successful: " . filesize($rFilename) . " bytes");
- return true;
- } else {
- print_log("[EPG] Download failed or file is empty: $rSource");
- return false;
- }
- }
-
- public function loadEPG($rSource, $rCache) {
- try {
- $this->rFilename = TMP_PATH . md5($rSource) . '.xml';
-
- // If caching is enabled, check for existing file
- if (!file_exists($this->rFilename) || !$rCache) {
- if (!$this->downloadFile($rSource, $this->rFilename)) {
- print_log("[EPG] Failed to load EPG source: $rSource");
- return;
- }
- } else {
- print_log("[EPG] Using cached EPG file: " . basename($this->rFilename));
- }
-
- if (!$this->rFilename) {
- FileLogger::log('epg', 'No XML found at: ' . $rSource);
- return;
- }
-
- $rXML = XmlStringStreamer::createStringWalkerParser($this->rFilename);
-
- if (!$rXML) {
- FileLogger::log('epg', 'Not a valid EPG source: ' . $rSource);
- print_log("[EPG] Failed to create XML parser for: $rSource");
- return;
- }
-
- $this->rEPGSource = $rXML;
- $this->rValid = true;
- print_log("[EPG] EPG source loaded successfully: $rSource");
- } catch (Exception $e) {
- FileLogger::log('epg', 'EPG failed to process: ' . $rSource);
- print_log("[EPG] Exception while loading EPG: " . $e->getMessage() . " | Source: $rSource");
- }
- }
-}
-
-function print_log($message) {
- echo "[" . date('Y-m-d H:i:s') . "] " . $message . "\n";
-}
-
-function reconect_db() {
- global $db;
- if ($db->ping()) {
- print_log("[EPG] Database connection is alive.");
- } else {
- print_log("[EPG] Database connection lost. Attempting to reconnect...");
- $db->db_connect();
- if ($db->ping()) {
- print_log("[EPG] Reconnected to the database successfully.");
- } else {
- print_log("[EPG] Failed to reconnect to the database. Exiting.");
- exit(1);
- }
- }
-}
-
-function getBouquetGroups() {
- global $db;
- print_log("[XMLTV] Building bouquet groups...");
- $db->query('SELECT DISTINCT(`bouquet`) AS `bouquet` FROM `lines`;');
- $ApiDependencyIdentifier = [
- 'all' => [
- 'streams' => [],
- 'bouquets' => []
- ]
- ];
-
- foreach ($db->get_rows() as $rRow) {
- $rBouquets = json_decode($rRow['bouquet'] ?? null, true);
-
- if (!is_array($rBouquets) || empty($rBouquets)) {
- print_log("[XMLTV] Skipping invalid/empty bouquet value: " . var_export($rBouquets, true));
- continue;
- }
-
- sort($rBouquets);
- $ApiDependencyIdentifier[implode('_', $rBouquets)] = [
- 'streams' => [],
- 'bouquets' => $rBouquets
- ];
- }
- $count = count($ApiDependencyIdentifier ?? []);
- print_log("[XMLTV] Found $count bouquet groups (including 'all')");
-
- foreach ($ApiDependencyIdentifier as $rGroup => $CacheFlushInterval) {
- $FileReference = [];
-
- foreach ($CacheFlushInterval['bouquets'] as $rBouquetID) {
- $db->query('SELECT `bouquet_channels` FROM `bouquets` WHERE `id` = ?;', $rBouquetID);
-
- foreach ($db->get_rows() as $rRow) {
- $FileReference[] = $rBouquetID;
- $ApiDependencyIdentifier[$rGroup]['streams'] = array_merge($ApiDependencyIdentifier[$rGroup]['streams'], json_decode($rRow['bouquet_channels'], true));
- }
-
- $ApiDependencyIdentifier[$rGroup]['streams'] = array_unique($ApiDependencyIdentifier[$rGroup]['streams']);
- }
-
- $ApiDependencyIdentifier[$rGroup]['bouquets'] = $FileReference;
- }
-
- return $ApiDependencyIdentifier;
-}
-
-function getEPG($rStreamID) {
- return file_exists(EPG_PATH . 'stream_' . $rStreamID) ? igbinary_unserialize(file_get_contents(EPG_PATH . 'stream_' . $rStreamID)) : [];
-}
-
-function shutdown() {
- global $db;
- if (is_object($db)) {
- $db->close_mysql();
- }
- print_log("[EPG] Script finished and database connection closed.");
-}
-
-
-if (posix_getpwuid(posix_geteuid())['name'] != 'xc_vm') {
- exit('Please run as XC_VM!' . "\n");
-}
-
-if (!@$argc) {
- exit(0);
-}
-
-$rEPGID = null;
-if (count($argv) == 2) {
- $rEPGID = intval($argv[1]);
-}
-
-print_log("=== XC_VM[EPG] Process started ===");
-print_log("Mode: " . ($rEPGID ? "Single EPG ID: $rEPGID" : "Full update"));
-
-set_time_limit(0);
-ini_set('memory_limit', -1);
-register_shutdown_function('shutdown');
-require __DIR__ . '/../www/init.php';
-require INCLUDES_PATH . 'libs/XmlStringStreamer.php';
-
-shell_exec('kill -9 `ps -ef | grep \'XC_VM\\[EPG\\]\' | grep -v grep | awk \'{print $2}\'`;');
-cli_set_process_title('XC_VM[EPG]');
-
-if (SettingsManager::getAll()['force_epg_timezone']) {
- date_default_timezone_set('UTC');
- print_log("[SYSTEM] Forced timezone to UTC");
-}
-
-print_log("[EPG] Clearing old channel mappings...");
-if ($rEPGID) {
- $db->query('DELETE FROM `epg_channels` WHERE `epg_id` = ?;', $rEPGID);
- $db->query('SELECT * FROM `epg` WHERE `id` = ?;', $rEPGID);
-} else {
- $db->query('TRUNCATE `epg_channels`;');
- $db->query('SELECT * FROM `epg`;');
-}
-
-$epgSources = $db->get_rows();
-print_log("[EPG] Found " . count($epgSources) . " EPG sources to process");
-
-foreach ($epgSources as $rRow) {
- print_log("[EPG] Processing source ID: {$rRow['id']} | File: {$rRow['epg_file']}");
- $rEPG = new EPG($rRow['epg_file']);
-
- if ($rEPG->rValid) {
- $rData = $rEPG->getData();
-
- reconect_db();
-
- $db->query('UPDATE `epg` SET `data` = ?, `last_updated` = ? WHERE `id` = ?', json_encode($rData, JSON_UNESCAPED_UNICODE), time(), $rRow['id']);
-
- print_log("[EPG] Updated metadata for EPG ID {$rRow['id']}, found " . count($rData) . " channels");
-
- foreach ($rData as $rID => $rArray) {
- $db->query('INSERT INTO `epg_channels`(`epg_id`, `channel_id`, `name`, `langs`) VALUES(?, ?, ?, ?);', $rRow['id'], $rID, $rArray['display_name'], json_encode($rArray['langs']));
- }
- } else {
- print_log("[EPG] Failed to load EPG source ID {$rRow['id']}");
- }
-}
-
-print_log("[EPG] Starting full programme data import...");
-
-if ($rEPGID) {
- $db->query('SELECT DISTINCT(t1.`epg_id`), t2.* FROM `streams` t1 INNER JOIN `epg` t2 ON t2.id = t1.epg_id WHERE t1.`epg_id` IS NOT NULL AND t2.id = ?;', $rEPGID);
-} else {
- $db->query('SELECT DISTINCT(t1.`epg_id`), t2.* FROM `streams` t1 INNER JOIN `epg` t2 ON t2.id = t1.epg_id WHERE t1.`epg_id` IS NOT NULL;');
-}
-
-foreach ($db->get_rows() as $rData) {
- print_log("[EPG] === Processing EPG ID: {$rData['epg_id']} ===");
-
- if ($rData['days_keep'] == 0) {
- print_log("[EPG] Clearing all existing data for EPG ID {$rData['epg_id']}");
- $db->query('DELETE FROM `epg_data` WHERE `epg_id` = ?', $rData['epg_id']);
- }
-
- $rEPG = new EPG($rData['epg_file'], true);
- if ($rEPG->rValid) {
- $db->query('SELECT t1.`channel_id`, t1.`epg_lang`, t1.`epg_offset`, last_row.start
- FROM `streams` t1
- LEFT JOIN (SELECT channel_id, MAX(`start`) as start FROM epg_data WHERE epg_id = ? GROUP BY channel_id) last_row
- ON last_row.channel_id = t1.channel_id
- WHERE `epg_id` = ?;', $rData['epg_id'], $rData['epg_id']);
- $channelMap = $db->get_rows(true, 'channel_id');
-
- $batches = $rEPG->parseEPG($rData['epg_id'], $channelMap, intval($rData['offset']) ?: 0);
-
- reconect_db();
-
- if ($batches) {
- $totalInserted = 0;
- foreach ($batches as $insertBatch) {
- if (!empty($insertBatch)) {
- $db->simple_query('INSERT INTO `epg_data` (`epg_id`,`channel_id`,`start`,`end`,`lang`,`title`,`description`) VALUES ' . $insertBatch);
- $totalInserted += substr_count($insertBatch, '),(') + 1;
- }
- }
- print_log("[EPG] Inserted $totalInserted programmes for EPG ID {$rData['epg_id']}");
- } else {
- print_log("[EPG] No new programmes found for EPG ID {$rData['epg_id']}");
- }
-
- $db->query('UPDATE `epg` SET `last_updated` = ? WHERE `id` = ?', time(), $rData['epg_id']);
- } else {
- print_log("[EPG] Failed to parse EPG file for ID {$rData['epg_id']}");
- }
-
- if ($rData['days_keep'] > 0) {
- $cleanupTime = strtotime('-' . (int)$rData['days_keep'] . ' days');
- if ($cleanupTime !== false) {
- $db->query('DELETE FROM `epg_data` WHERE `epg_id` = ? AND `start` < ?', $rData['epg_id'], $cleanupTime);
- echo "[EPG] Cleaned up old data (older than {$rData['days_keep']} days)\n";
- } else {
- echo "[EPG] Invalid days_keep value, skipping cleanup\n";
- }
- }
-}
-
-print_log("[EPG] Removing duplicate EPG entries...");
-$db->query('DELETE n1 FROM `epg_data` n1, `epg_data` n2 WHERE n1.id < n2.id AND n1.epg_id = n2.epg_id AND n1.channel_id = n2.channel_id AND n1.start = n2.start;');
-
-print_log("[EPG] Cleaning temporary XML files...");
-shell_exec('rm -f ' . TMP_PATH . '*.xml');
-
-print_log("[XMLTV] Starting XMLTV generation...");
-$ApiDependencyIdentifier = getBouquetGroups();
-
-$totalBouquets = count($ApiDependencyIdentifier);
-print_log("[XMLTV] Generating XMLTV for $totalBouquets bouquet(s)");
-
-foreach ($ApiDependencyIdentifier as $rBouquet => $BatchProcessId) {
- if (!(strlen($rBouquet) > 0 && (count($BatchProcessId['streams']) > 0 || $rBouquet == 'all'))) {
- continue;
- }
-
- print_log("[XMLTV] Generating EPG for bouquet: " . ($rBouquet === 'all' ? 'ALL' : $rBouquet));
-
- $rOutput = '';
- $rServerName = htmlspecialchars(SettingsManager::getAll()['server_name'], ENT_XML1 | ENT_QUOTES | ENT_DISALLOWED, 'UTF-8');
- $rOutput .= '' . "\n";
- $rOutput .= '' . "\n";
-
- if ($rBouquet == 'all') {
- $db->query('SELECT `stream_display_name`,`stream_icon`,`channel_id`,`epg_id`,`tv_archive_duration` FROM `streams` WHERE `epg_id` IS NOT NULL AND `channel_id` IS NOT NULL;');
- } else {
- $db->query('SELECT `stream_display_name`,`stream_icon`,`channel_id`,`epg_id`,`tv_archive_duration` FROM `streams` WHERE `epg_id` IS NOT NULL AND `channel_id` IS NOT NULL AND `id` IN (' . implode(',', array_map('intval', $BatchProcessId['streams'])) . ');');
- }
-
- $channels = $db->get_rows();
- $channelCount = count($channels);
- print_log("[XMLTV] Found $channelCount channels in this bouquet");
-
- $fa4629d757fa3640 = [];
- $hasArchive = 0;
-
- foreach ($channels as $rRow) {
- if ($rRow['tv_archive_duration'] > 0) $hasArchive++;
-
- $displayName = htmlspecialchars($rRow['stream_display_name'], ENT_XML1 | ENT_QUOTES | ENT_DISALLOWED, 'UTF-8');
- $icon = htmlspecialchars(ImageUtils::validateURL($rRow['stream_icon']), ENT_XML1 | ENT_QUOTES | ENT_DISALLOWED, 'UTF-8');
- $channelID = htmlspecialchars($rRow['channel_id'], ENT_XML1 | ENT_QUOTES | ENT_DISALLOWED, 'UTF-8');
-
- $rOutput .= "\t";
- $rOutput .= "\t\t$displayName";
- if (!empty($rRow['stream_icon'])) {
- $rOutput .= "\t\t";
- }
- $rOutput .= "\t";
-
- $fa4629d757fa3640[] = $rRow['epg_id'];
- }
-
- $fa4629d757fa3640 = array_unique($fa4629d757fa3640);
-
- if (count($fa4629d757fa3640) > 0) {
- if ($hasArchive > 0) {
- print_log("[XMLTV] Archive channels detected ($hasArchive), including all historical programmes");
- $db->query('SELECT * FROM `epg_data` WHERE `epg_id` IN (' . implode(',', array_map('intval', $fa4629d757fa3640)) . ');');
- } else {
- print_log("[XMLTV] No archive channels, filtering only current/future programmes");
- $db->query('SELECT * FROM `epg_data` WHERE `epg_id` IN (' . implode(',', array_map('intval', $fa4629d757fa3640)) . ') AND `end` >= UNIX_TIMESTAMP();');
- }
-
- $programmes = $db->get_rows();
- $progCount = count($programmes);
- print_log("[XMLTV] Adding $progCount programmes to XML");
-
- $seen = [];
- foreach ($programmes as $rRow) {
- $key = $rRow['channel_id'] . '|' . $rRow['start'];
- if (isset($seen[$key])) continue;
- $seen[$key] = true;
-
- $rTitle = htmlspecialchars($rRow['title'] ?? '', ENT_XML1 | ENT_QUOTES | ENT_DISALLOWED, 'UTF-8');
- $rDescription = htmlspecialchars($rRow['description'] ?? '', ENT_XML1 | ENT_QUOTES | ENT_DISALLOWED, 'UTF-8');
- $rChannelID = htmlspecialchars($rRow['channel_id'], ENT_XML1 | ENT_QUOTES | ENT_DISALLOWED, 'UTF-8');
- $rStart = date('YmdHis', $rRow['start']) . ' ' . str_replace(':', '', date('P', $rRow['start']));
- $rEnd = date('YmdHis', $rRow['end']) . ' ' . str_replace(':', '', date('P', $rRow['end']));
-
- $rOutput .= "\t";
- $rOutput .= "\t\t$rTitle";
- $rOutput .= "\t\t$rDescription";
- $rOutput .= "\t";
- }
- }
-
- $rOutput .= '';
- $fileName = ($rBouquet == 'all' ? 'all' : md5($rBouquet));
- $xmlPath = EPG_PATH . 'epg_' . $fileName . '.xml';
- $gzPath = EPG_PATH . 'epg_' . $fileName . '.xml.gz';
-
- file_put_contents($xmlPath, $rOutput);
- $gz = gzopen($gzPath, 'w9');
- gzwrite($gz, $rOutput);
- gzclose($gz);
-
- print_log("[XMLTV] Saved epg_$fileName.xml.gz (" . number_format(strlen($rOutput)) . " bytes)");
-}
-
-print_log("[CACHE] Building per-stream EPG cache...");
-$db->query('SELECT `id`, `epg_id`, `channel_id` FROM `streams` WHERE `type` = 1 AND `epg_id` IS NOT NULL AND `channel_id` IS NOT NULL;');
-$streams = $db->get_rows();
-print_log("[CACHE] Caching EPG for " . count($streams) . " live streams");
-
-foreach ($streams as $rRow) {
- $rEPG = [];
- $seen = [];
-
- $db->query('SELECT * FROM `epg_data` WHERE `epg_id` = ? AND `channel_id` = ? ORDER BY `start` ASC;', $rRow['epg_id'], $rRow['channel_id']);
- foreach ($db->get_rows() as $prog) {
- if (!in_array($prog['start'], $seen)) {
- $seen[] = $prog['start'];
- $rEPG[] = $prog;
- }
- }
-
- if (count($rEPG) > 0) {
- file_put_contents(EPG_PATH . 'stream_' . $rRow['id'], igbinary_serialize($rEPG));
- }
-}
-
-print_log("[CLEANUP] Removing old cache files...");
-$deleted = 0;
-foreach (scandir(EPG_PATH) as $rFile) {
- if ($rFile === '.' || $rFile === '..') continue;
- $fullPath = EPG_PATH . $rFile;
- if (filemtime($fullPath) < (time() - 10)) {
- unlink($fullPath);
- $deleted++;
- }
-}
-print_log("[CLEANUP] Deleted $deleted old cache files");
-
-print_log("=== EPG processing completed successfully! ===");
diff --git a/src/public/Views/admin/dashboard.php b/src/public/Views/admin/dashboard.php
index 65c5c6ed..7ec54752 100644
--- a/src/public/Views/admin/dashboard.php
+++ b/src/public/Views/admin/dashboard.php
@@ -281,7 +281,7 @@
Database Incomplete
-
Your database is outdated, please run /home/xc_vm/status as root user to update your tables.
+
Your database is outdated, please run sudo /home/xc_vm/console.php status as root user to update your tables.
@@ -295,7 +295,7 @@
Root Crons Missing
-
Root cronjob hasn't run recently, please check root crontab or run /home/xc_vm/status
+
Root cronjob hasn't run recently, please check root crontab or run sudo /home/xc_vm/console.php status
diff --git a/src/status b/src/status
deleted file mode 100644
index c302b906..00000000
--- a/src/status
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/home/xc_vm/bin/php/bin/php
- 0:
- printc(f"Usando archivo local de parche {display_name}: {local_patch_file}", col.OKGREEN)
+ printc(
+ f"Usando archivo local de parche {display_name}: {local_patch_file}",
+ col.OKGREEN,
+ )
temp_tar_file = local_patch_file
else:
- printc(f"Archivo local no encontrado. Descargando binarios {display_name} desde {remote_url}...", col.OKBLUE)
+ printc(
+ f"Archivo local no encontrado. Descargando binarios {display_name} desde {remote_url}...",
+ col.OKBLUE,
+ )
urllib.request.urlretrieve(remote_url, temp_tar_file)
if not (os.path.exists(temp_tar_file) and os.path.getsize(temp_tar_file) > 0):
@@ -262,8 +471,10 @@ def install_distribution_binaries(dist_id, version):
shutil.rmtree(temp_extract_dir)
# 3. Extraer a directorio temporal
- printc(f"Extrayendo binarios de {display_name} a ubicación temporal...", col.OKBLUE)
- with tarfile.open(temp_tar_file, 'r:gz') as tar:
+ printc(
+ f"Extrayendo binarios de {display_name} a ubicación temporal...", col.OKBLUE
+ )
+ with tarfile.open(temp_tar_file, "r:gz") as tar:
tar.extractall(path=temp_extract_dir)
# Detectar automáticamente la estructura del directorio
@@ -272,9 +483,11 @@ def install_distribution_binaries(dist_id, version):
# Posibles rutas donde pueden estar los binarios
possible_paths = [
os.path.join(temp_extract_dir, distro_dirname, "bin"), # debian11/bin/
- os.path.join(temp_extract_dir, "bin"), # bin/ directamente
- os.path.join(temp_extract_dir, distro_dirname), # debian11/ (si bin está adentro)
- temp_extract_dir # raíz del extracto
+ os.path.join(temp_extract_dir, "bin"), # bin/ directamente
+ os.path.join(
+ temp_extract_dir, distro_dirname
+ ), # debian11/ (si bin está adentro)
+ temp_extract_dir, # raíz del extracto
]
source_bin_dir = None
@@ -282,9 +495,11 @@ def install_distribution_binaries(dist_id, version):
if os.path.exists(path):
# Verificar si contiene archivos binarios o directorios típicos
contents = os.listdir(path)
- has_binaries = any(item in contents for item in ['php', 'nginx', 'nginx_rtmp', 'bin'])
+ has_binaries = any(
+ item in contents for item in ["php", "nginx", "nginx_rtmp", "bin"]
+ )
- if has_binaries or path.endswith('/bin'):
+ if has_binaries or path.endswith("/bin"):
source_bin_dir = path
printc(f"Estructura encontrada: {source_bin_dir}", col.OKGREEN)
break
@@ -294,31 +509,45 @@ def install_distribution_binaries(dist_id, version):
printc("Buscando estructura de directorios recursivamente...", col.OKBLUE)
for root, dirs, files in os.walk(temp_extract_dir):
# Buscar directorios que contengan binarios típicos
- if any(item in dirs for item in ['php', 'nginx', 'nginx_rtmp', 'bin']):
+ if any(item in dirs for item in ["php", "nginx", "nginx_rtmp", "bin"]):
source_bin_dir = root
- printc(f"Estructura encontrada recursivamente: {source_bin_dir}", col.OKGREEN)
+ printc(
+ f"Estructura encontrada recursivamente: {source_bin_dir}",
+ col.OKGREEN,
+ )
break
target_bin_dir = "/home/xc_vm/bin"
if not source_bin_dir:
- printc(f"Error: No se pudo encontrar la estructura de binarios en el parche.", col.FAIL)
- printc(f"Contenido de {temp_extract_dir}: {os.listdir(temp_extract_dir)}", col.WARNING)
+ printc(
+ f"Error: No se pudo encontrar la estructura de binarios en el parche.",
+ col.FAIL,
+ )
+ printc(
+ f"Contenido de {temp_extract_dir}: {os.listdir(temp_extract_dir)}",
+ col.WARNING,
+ )
# Mostrar estructura completa para debug
printc("Estructura completa del directorio extraído:", col.WARNING)
for root, dirs, files in os.walk(temp_extract_dir):
- level = root.replace(temp_extract_dir, '').count(os.sep)
- indent = ' ' * 2 * level
+ level = root.replace(temp_extract_dir, "").count(os.sep)
+ indent = " " * 2 * level
printc(f"{indent}{os.path.basename(root)}/", col.WARNING)
- subindent = ' ' * 2 * (level + 1)
+ subindent = " " * 2 * (level + 1)
for file in files[:10]: # Limitar a 10 archivos para no saturar
printc(f"{subindent}{file}", col.WARNING)
if len(files) > 10:
- printc(f"{subindent}... y {len(files) - 10} archivos más", col.WARNING)
+ printc(
+ f"{subindent}... y {len(files) - 10} archivos más", col.WARNING
+ )
return False
# 4. Reemplazar archivos específicos, no todo el directorio
- printc(f"Reemplazando binarios específicos con versiones de {display_name}...", col.OKBLUE)
+ printc(
+ f"Reemplazando binarios específicos con versiones de {display_name}...",
+ col.OKBLUE,
+ )
# Recorrer recursivamente los archivos en el directorio fuente
for root, dirs, files in os.walk(source_bin_dir):
@@ -353,7 +582,7 @@ def install_distribution_binaries(dist_id, version):
"/home/xc_vm/bin/php/bin/php",
"/home/xc_vm/bin/php/sbin/php-fpm",
"/home/xc_vm/bin/nginx/sbin/nginx",
- "/home/xc_vm/bin/nginx_rtmp/sbin/nginx_rtmp"
+ "/home/xc_vm/bin/nginx_rtmp/sbin/nginx_rtmp",
]
for exe_path in executables_to_chmod:
if os.path.exists(exe_path):
@@ -366,7 +595,10 @@ def install_distribution_binaries(dist_id, version):
if os.path.exists(temp_extract_dir):
shutil.rmtree(temp_extract_dir)
- printc(f"Instalación de binarios específicos de {display_name} completada", col.OKGREEN)
+ printc(
+ f"Instalación de binarios específicos de {display_name} completada",
+ col.OKGREEN,
+ )
return True
except Exception as e:
@@ -378,21 +610,24 @@ def install_distribution_binaries(dist_id, version):
shutil.rmtree(temp_extract_dir)
return False
+
# Funciones específicas para cada versión (mantenidas para compatibilidad)
def install_debian12_binaries():
"""Wrapper function for Debian 12"""
- return install_distribution_binaries('debian', '12')
+ return install_distribution_binaries("debian", "12")
+
def install_ubuntu20_binaries():
"""Wrapper function for Ubuntu 20"""
- return install_distribution_binaries('ubuntu', '20')
+ return install_distribution_binaries("ubuntu", "20")
+
def detect_distribution():
"""Detect Linux distribution and version without external modules"""
dist_id = "unknown"
version = "unknown"
family = "unknown"
-
+
# Try /etc/os-release first (standard method)
if os.path.exists("/etc/os-release"):
try:
@@ -406,7 +641,7 @@ def detect_distribution():
version = line.split("=")[1].strip().strip('"')
except:
pass
-
+
# Try older methods
if dist_id == "unknown":
if os.path.exists("/etc/redhat-release"):
@@ -442,42 +677,44 @@ def detect_distribution():
version = line.split("=")[1].strip().strip('"')
except:
pass
-
+
# Determine family
- if dist_id in ['centos', 'rhel', 'rocky', 'almalinux', 'fedora']:
- family = 'redhat'
- elif dist_id in ['ubuntu', 'debian']:
- family = 'debian'
+ if dist_id in ["centos", "rhel", "rocky", "almalinux", "fedora"]:
+ family = "redhat"
+ elif dist_id in ["ubuntu", "debian"]:
+ family = "debian"
else:
family = dist_id
-
+
return {
- 'id': dist_id,
- 'family': family,
- 'version': version,
- 'full_version': version
+ "id": dist_id,
+ "family": family,
+ "version": version,
+ "full_version": version,
}
+
def check_supported_distro(dist_info):
"""Check if distribution is supported"""
- dist_id = dist_info['id']
- version = dist_info['version']
-
+ dist_id = dist_info["id"]
+ version = dist_info["version"]
+
if dist_id in SUPPORTED_DISTROS:
if version in SUPPORTED_DISTROS[dist_id]:
return True
else:
# Check if any supported version starts with the same major version
for supported_version in SUPPORTED_DISTROS[dist_id]:
- if supported_version.startswith(version.split('.')[0]):
+ if supported_version.startswith(version.split(".")[0]):
return True
-
+
# If not in list but is a known family, we'll try anyway
- if dist_info['family'] in ['debian', 'redhat']:
+ if dist_info["family"] in ["debian", "redhat"]:
return True
-
+
return False
+
def run_command(cmd, shell=True, capture_output=False):
"""Run shell command with error handling"""
try:
@@ -490,65 +727,68 @@ def run_command(cmd, shell=True, capture_output=False):
except Exception as e:
return 1, None, str(e)
+
def is_valid_zip(file_path):
"""Check if a file is a valid ZIP archive"""
try:
- with zipfile.ZipFile(file_path, 'r') as zip_ref:
+ with zipfile.ZipFile(file_path, "r") as zip_ref:
# Try to list contents
zip_ref.namelist()
return True
except:
return False
+
def is_valid_tar(file_path):
"""Check if a file is a valid TAR archive"""
try:
- with tarfile.open(file_path, 'r:*') as tar_ref:
+ with tarfile.open(file_path, "r:*") as tar_ref:
# Try to list contents
tar_ref.getmembers()
return True
except:
return False
+
def download_xc_vm():
"""Download XC_VM from GitHub releases - SIGUIENDO LA MISMA LÓGICA DEL BASH"""
printc("Checking for XC_VM installation files...", col.OKBLUE)
-
+
# Check if valid files already exist (igual que antes)
if os.path.exists("./xc_vm.tar.gz") and is_valid_tar("./xc_vm.tar.gz"):
printc("Valid xc_vm.tar.gz found locally", col.OKGREEN)
return True
-
+
if os.path.exists("./XC_VM.zip") and is_valid_zip("./XC_VM.zip"):
printc("Valid XC_VM.zip found locally", col.OKGREEN)
return True
-
+
printc("Not found. Trying to download from GitHub...", col.OKBLUE)
-
+
try:
# 1. Obtener la última versión de la API de GitHub (IGUAL QUE BASH)
printc("Getting latest version from GitHub API...", col.OKBLUE)
api_url = "https://api.github.com/repos/Vateron-Media/XC_VM/releases/latest"
req = urllib.request.Request(api_url)
- req.add_header('User-Agent', 'XC_VM-Installer/1.0')
-
+ req.add_header("User-Agent", "XC_VM-Installer/1.0")
+
with urllib.request.urlopen(req, timeout=10) as response:
data = json.loads(response.read().decode())
- latest_version = data['tag_name']
+ latest_version = data["tag_name"]
printc(f"Latest version: {latest_version}", col.OKGREEN)
-
+
# 2. Descargar XC_VM.zip directamente (IGUAL QUE BASH: wget "https://github.com/Vateron-Media/XC_VM/releases/download/${latest_version}/XC_VM.zip")
download_url = f"https://github.com/Vateron-Media/XC_VM/releases/download/{latest_version}/XC_VM.zip"
printc(f"Downloading: {download_url}", col.OKBLUE)
-
+
# Usar urllib para descargar
urllib.request.urlretrieve(download_url, "XC_VM.zip")
-
+
# Verificar la descarga
if os.path.exists("XC_VM.zip") and os.path.getsize("XC_VM.zip") > 0:
file_size = os.path.getsize("XC_VM.zip")
printc(f"Download successful: XC_VM.zip ({file_size} bytes)", col.OKGREEN)
-
+
# Validar que sea un ZIP válido
if is_valid_zip("XC_VM.zip"):
printc("ZIP archive validated successfully", col.OKGREEN)
@@ -560,50 +800,51 @@ def download_xc_vm():
else:
printc("Download failed or file is empty", col.FAIL)
return False
-
+
except Exception as e:
printc(f"Download error: {e}", col.FAIL)
-
+
# Intentar métodos alternativos si falla
printc("Trying alternative download methods...", col.WARNING)
-
+
# Método alternativo 1: Descarga directa del latest
try:
alt_url = "https://github.com/Vateron-Media/XC_VM/releases/latest/download/XC_VM.zip"
printc(f"Trying alternative: {alt_url}", col.OKBLUE)
urllib.request.urlretrieve(alt_url, "XC_VM.zip")
-
+
if os.path.exists("XC_VM.zip") and is_valid_zip("XC_VM.zip"):
printc("Alternative download successful", col.OKGREEN)
return True
except:
pass
-
+
# Método alternativo 2: Versión específica 1.2.10
try:
specific_url = "https://github.com/Vateron-Media/XC_VM/releases/download/1.2.10/XC_VM.zip"
printc(f"Trying specific version: {specific_url}", col.OKBLUE)
urllib.request.urlretrieve(specific_url, "XC_VM.zip")
-
+
if os.path.exists("XC_VM.zip") and is_valid_zip("XC_VM.zip"):
printc("Specific version download successful", col.OKGREEN)
return True
except:
pass
-
+
return False
+
def install_mariadb_repo(dist_info):
"""Install MariaDB repository based on distribution"""
- dist_id = dist_info['id']
- version = dist_info['version']
- family = dist_info['family']
+ dist_id = dist_info["id"]
+ version = dist_info["version"]
+ family = dist_info["family"]
printc(f"Configuring MariaDB repository for {dist_id} {version}", col.OKBLUE)
- if family == 'debian':
+ if family == "debian":
# Debian/Ubuntu
- if dist_id == 'ubuntu':
+ if dist_id == "ubuntu":
# Try to get codename from /etc/os-release
codename = "jammy" # Default for Ubuntu 22.04
try:
@@ -627,13 +868,24 @@ def install_mariadb_repo(dist_info):
printc(f"Using Ubuntu codename: {codename}", col.OKGREEN)
# Install prerequisites
- run_command("apt-get install -y apt-transport-https curl gnupg software-properties-common")
+ run_command(
+ "apt-get install -y apt-transport-https curl gnupg software-properties-common"
+ )
# Special handling for Ubuntu 20.04 LTS (EOL)
- if dist_id == 'ubuntu' and version.startswith("20"):
- printc("Ubuntu 20.04 LTS detected, using system MariaDB packages...", col.WARNING)
- printc("MariaDB 11.4 is not compatible with Ubuntu 20.04 (libc6 incompatibility)", col.OKBLUE)
- printc("Using Ubuntu 20.04 default MariaDB packages for compatibility", col.OKGREEN)
+ if dist_id == "ubuntu" and version.startswith("20"):
+ printc(
+ "Ubuntu 20.04 LTS detected, using system MariaDB packages...",
+ col.WARNING,
+ )
+ printc(
+ "MariaDB 11.4 is not compatible with Ubuntu 20.04 (libc6 incompatibility)",
+ col.OKBLUE,
+ )
+ printc(
+ "Using Ubuntu 20.04 default MariaDB packages for compatibility",
+ col.OKGREEN,
+ )
# For Ubuntu 20.04, we'll use the system MariaDB packages
# Ubuntu 20.04 default repositories have MariaDB 10.3 which is compatible
@@ -646,7 +898,10 @@ def install_mariadb_repo(dist_info):
if os.path.exists("/usr/share/keyrings/mariadb.gpg"):
os.remove("/usr/share/keyrings/mariadb.gpg")
- printc("Will use Ubuntu 20.04 default MariaDB packages (10.3.x)", col.OKGREEN)
+ printc(
+ "Will use Ubuntu 20.04 default MariaDB packages (10.3.x)",
+ col.OKGREEN,
+ )
# Skip external repository setup for Ubuntu 20.04
except Exception as e:
@@ -655,39 +910,49 @@ def install_mariadb_repo(dist_info):
else:
# For other versions, use the official script
- printc("Adding MariaDB repository using official setup script...", col.OKBLUE)
- run_command("curl -LsS https://r.mariadb.com/downloads/mariadb_repo_setup | bash -s -- --mariadb-server-version='mariadb-11.4'")
+ printc(
+ "Adding MariaDB repository using official setup script...", col.OKBLUE
+ )
+ run_command(
+ "curl -LsS https://r.mariadb.com/downloads/mariadb_repo_setup | bash -s -- --mariadb-server-version='mariadb-11.4'"
+ )
# CORREGIDO: Añadir MaxMind repository solo para Ubuntu (con manejo de errores)
- if dist_id == 'ubuntu':
+ if dist_id == "ubuntu":
printc("Adding MaxMind repository for GeoIP...", col.OKBLUE)
try:
run_command("add-apt-repository -y ppa:maxmind/ppa")
printc("MaxMind repository added successfully", col.OKGREEN)
except Exception as e:
printc(f"MaxMind PPA failed to add: {e}", col.WARNING)
- printc("Continuing without MaxMind PPA (not critical for functionality)", col.OKBLUE)
+ printc(
+ "Continuing without MaxMind PPA (not critical for functionality)",
+ col.OKBLUE,
+ )
else:
printc("Skipping MaxMind PPA (not available for Debian)", col.OKBLUE)
# Update package list
printc("Updating package list...", col.OKBLUE)
run_command("apt-get update")
-
- elif family == 'redhat':
+
+ elif family == "redhat":
# RedHat based distributions
run_command("yum install -y curl")
-
+
# Install MariaDB repository using official script
printc("Adding MariaDB repository...", col.OKBLUE)
- run_command("curl -LsS https://r.mariadb.com/downloads/mariadb_repo_setup | bash -s -- --mariadb-server-version='mariadb-11.4'")
-
+ run_command(
+ "curl -LsS https://r.mariadb.com/downloads/mariadb_repo_setup | bash -s -- --mariadb-server-version='mariadb-11.4'"
+ )
+
else:
printc(f"Unsupported distribution: {dist_id}", col.WARNING)
return False
-
+
return True
+
def secure_mariadb_installation(root_password, dist_info=None):
"""Secure MariaDB installation with root password (from bash script)"""
printc("Securing MariaDB installation", col.OKBLUE)
@@ -706,7 +971,11 @@ def secure_mariadb_installation(root_password, dist_info=None):
ret, out, err = run_command(version_cmd, capture_output=True)
mariadb_version = out.strip() if out else ""
- is_mariadb_103 = "10.3" in mariadb_version or "5.7" in mariadb_version or "5.6" in mariadb_version
+ is_mariadb_103 = (
+ "10.3" in mariadb_version
+ or "5.7" in mariadb_version
+ or "5.6" in mariadb_version
+ )
if is_mariadb_103:
printc("MariaDB 10.3 detected, using legacy password syntax", col.OKBLUE)
@@ -721,7 +990,9 @@ def secure_mariadb_installation(root_password, dist_info=None):
auth_plugin = out.strip() if out else ""
if ret == 0 and auth_plugin == "unix_socket":
- printc("Using unix_socket authentication, converting to password...", col.OKBLUE)
+ printc(
+ "Using unix_socket authentication, converting to password...", col.OKBLUE
+ )
# Convert from unix_socket to password authentication with version-specific syntax
if is_mariadb_103:
# MariaDB 10.3 and older syntax
@@ -732,7 +1003,7 @@ def secure_mariadb_installation(root_password, dist_info=None):
"DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');",
"DROP DATABASE IF EXISTS test;",
"DELETE FROM mysql.db WHERE Db='test' OR Db='test\\\\_%';",
- "FLUSH PRIVILEGES;"
+ "FLUSH PRIVILEGES;",
]
else:
# MariaDB 10.4+ and MariaDB 11.x syntax
@@ -743,15 +1014,15 @@ def secure_mariadb_installation(root_password, dist_info=None):
"DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');",
"DROP DATABASE IF EXISTS test;",
"DELETE FROM mysql.db WHERE Db='test' OR Db='test\\\\_%';",
- "FLUSH PRIVILEGES;"
+ "FLUSH PRIVILEGES;",
]
for sql in sql_commands:
if is_mariadb_103 and "SET PASSWORD" in sql:
# Special handling for SET PASSWORD command
- run_command(f"mariadb -u root -e \"{sql}\"", shell=True)
+ run_command(f'mariadb -u root -e "{sql}"', shell=True)
else:
- run_command(f"mariadb -u root -e \"{sql}\"")
+ run_command(f'mariadb -u root -e "{sql}"')
printc("MariaDB secured with password authentication", col.OKGREEN)
else:
@@ -764,7 +1035,7 @@ def secure_mariadb_installation(root_password, dist_info=None):
# MariaDB 11.x syntax
set_cmd = f"mariadb -u root -e \"ALTER USER 'root'@'localhost' IDENTIFIED BY '{root_password}';\" 2>/dev/null || true"
run_command(set_cmd)
-
+
# Create custom security configuration (like 99-custom.cnf from bash script)
printc("Creating custom MariaDB security configuration...", col.OKBLUE)
custom_conf = """[mysqld]
@@ -776,46 +1047,48 @@ slow_query_log = 1
slow_query_log_file = /var/log/mysql/mariadb-slow.log
long_query_time = 2
log_error = /var/log/mysql/error.log"""
-
+
conf_dir = "/etc/mysql/mariadb.conf.d/"
if not os.path.exists(conf_dir):
conf_dir = "/etc/my.cnf.d/"
if not os.path.exists(conf_dir):
os.makedirs(conf_dir, exist_ok=True)
-
+
custom_path = os.path.join(conf_dir, "99-custom.cnf")
with open(custom_path, "w") as f:
f.write(custom_conf)
-
+
# Create log directory and set permissions
run_command("mkdir -p /var/log/mysql && chown mysql:mysql /var/log/mysql")
-
+
# Restart MariaDB
run_command("systemctl restart mariadb")
time.sleep(3)
-
+
# NO crear /root/mariadb_root_password.txt, solo usar /root/credentials.txt
printc("MariaDB security hardening completed", col.OKGREEN)
return root_password
+
def get_system_ram_mb():
"""Get total system RAM in MB"""
try:
- with open('/proc/meminfo', 'r') as f:
+ with open("/proc/meminfo", "r") as f:
for line in f:
- if line.startswith('MemTotal:'):
+ if line.startswith("MemTotal:"):
mem_kb = int(line.split()[1])
return mem_kb // 1024 # Convert to MB
except:
pass
return 1024 # Value by default if unable to determine
+
def generate_mysql_config(total_ram_mb):
"""Generate MySQL configuration based on total RAM (from bash script logic)"""
-
+
# Calculate based on RAM (similar to bash script)
buffer_pool_mb = int(total_ram_mb * 0.25)
-
+
if total_ram_mb < 512:
buffer_pool_mb = 64
max_connections = 30
@@ -839,20 +1112,20 @@ def generate_mysql_config(total_ram_mb):
if buffer_pool_mb > 2048:
buffer_pool_mb = 2048
max_connections = 200
-
+
# Format buffer pool size
if buffer_pool_mb >= 1024:
buffer_pool_size = f"{buffer_pool_mb // 1024}G"
else:
buffer_pool_size = f"{buffer_pool_mb}M"
-
+
# Calculate other values
key_buffer = min(buffer_pool_mb // 8, 32)
tmp_table_size = min(buffer_pool_mb // 4, 64)
back_log = min(max_connections // 2, 256)
thread_cache = min(max_connections // 4, 64)
buffer_pool_instances = "1" if buffer_pool_mb < 1024 else "2"
-
+
# Generate config from template
config = rMySQLCnfTemplate
config = config.replace("{{KEY_BUFFER}}", str(key_buffer))
@@ -862,21 +1135,25 @@ def generate_mysql_config(total_ram_mb):
config = config.replace("{{BUFFER_POOL_SIZE}}", buffer_pool_size)
config = config.replace("{{BUFFER_POOL_INSTANCES}}", buffer_pool_instances)
config = config.replace("{{THREAD_CACHE}}", str(thread_cache))
-
+
printc(f"RAM detected: {total_ram_mb}MB", col.OKGREEN)
- printc(f"Buffer pool configured: {buffer_pool_size} ({buffer_pool_mb}MB)", col.OKGREEN)
+ printc(
+ f"Buffer pool configured: {buffer_pool_size} ({buffer_pool_mb}MB)", col.OKGREEN
+ )
printc(f"Max connections: {max_connections}", col.OKGREEN)
-
+
return config
+
def generate_random_password(length=32):
"""Generate random password (similar to bash script)"""
- chars = '23456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ'
- return ''.join(random.choice(chars) for _ in range(length))
+ chars = "23456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ"
+ return "".join(random.choice(chars) for _ in range(length))
+
def generate_root_password():
"""Generate secure root password of 20 characters"""
- chars = 'A-Za-z0-9!@#%^*()_+-=,.<>?'
+ chars = "A-Za-z0-9!@#%^*()_+-=,.<>?"
try:
# Try using /dev/urandom
cmd = f"cat /dev/urandom | tr -dc '{chars}' | head -c 20"
@@ -885,11 +1162,13 @@ def generate_root_password():
return out.strip()
except:
pass
-
+
# Fallback to Python random
import string
- chars = string.ascii_letters + string.digits + '!@#%^*()_+-=,.<>?'
- return ''.join(random.choice(chars) for _ in range(20))
+
+ chars = string.ascii_letters + string.digits + "!@#%^*()_+-=,.<>?"
+ return "".join(random.choice(chars) for _ in range(20))
+
def getIP():
try:
@@ -909,6 +1188,7 @@ def getIP():
pass
return "127.0.0.1"
+
def printc(rText, rColour=col.OKBLUE, rPadding=0):
rLeft = int(30 - (len(rText) / 2))
rRight = 60 - rLeft - len(rText)
@@ -933,17 +1213,20 @@ def printc(rText, rColour=col.OKBLUE, rPadding=0):
)
print(" ")
+
def extract_archive(archive_path):
"""Extract archive with proper validation and error handling"""
printc(f"Extracting {archive_path}...", col.OKBLUE)
-
- if archive_path.endswith('.tar.gz') or archive_path.endswith('.tgz'):
+
+ if archive_path.endswith(".tar.gz") or archive_path.endswith(".tgz"):
try:
- with tarfile.open(archive_path, 'r:gz') as tar:
+ with tarfile.open(archive_path, "r:gz") as tar:
# Get member list for debugging
members = tar.getmembers()
- printc(f"Archive contains {len(members)} files/directories", col.OKGREEN)
-
+ printc(
+ f"Archive contains {len(members)} files/directories", col.OKGREEN
+ )
+
# Extract all files
tar.extractall(path="/home/xc_vm/")
printc("Extraction successful", col.OKGREEN)
@@ -951,38 +1234,42 @@ def extract_archive(archive_path):
except Exception as e:
printc(f"Failed to extract tar.gz: {e}", col.FAIL)
return False
-
- elif archive_path.endswith('.zip'):
+
+ elif archive_path.endswith(".zip"):
try:
- with zipfile.ZipFile(archive_path, 'r') as zip_ref:
+ with zipfile.ZipFile(archive_path, "r") as zip_ref:
# Get file list for debugging
file_list = zip_ref.namelist()
printc(f"Archive contains {len(file_list)} files", col.OKGREEN)
-
+
# Extract all files
zip_ref.extractall(path="/home/xc_vm/")
printc("Extraction successful", col.OKGREEN)
-
+
# Check if zip contains nested tar.gz
for file in file_list:
- if file.endswith('xc_vm.tar.gz') or file.endswith('.tar.gz'):
+ if file.endswith("xc_vm.tar.gz") or file.endswith(".tar.gz"):
nested_path = os.path.join("/home/xc_vm", file)
if os.path.exists(nested_path):
- printc(f"Found nested archive: {file}, extracting...", col.OKBLUE)
+ printc(
+ f"Found nested archive: {file}, extracting...",
+ col.OKBLUE,
+ )
return extract_archive(nested_path)
return True
except Exception as e:
printc(f"Failed to extract zip: {e}", col.FAIL)
return False
-
+
else:
printc(f"Unsupported archive format: {archive_path}", col.FAIL)
return False
+
def fix_ssh2_library_issue():
"""Fix SSH2 library issue by creating proper symlinks"""
printc("Configuring SSH2 libraries for PHP", col.OKBLUE)
-
+
# List of possible libssh2.so.1 locations
libssh2_paths = [
"/usr/lib/x86_64-linux-gnu/libssh2.so.1",
@@ -990,24 +1277,24 @@ def fix_ssh2_library_issue():
"/usr/lib/x86_64-linux-gnu/libssh2.so",
"/usr/lib64/libssh2.so.1",
"/usr/lib/libssh2.so.1",
- "/usr/local/lib/libssh2.so.1"
+ "/usr/local/lib/libssh2.so.1",
]
-
+
found_lib = None
for lib_path in libssh2_paths:
if os.path.exists(lib_path):
found_lib = lib_path
printc(f"Found SSH2 library: {lib_path}", col.OKGREEN)
break
-
+
if found_lib:
# Create symlinks in common locations
symlink_targets = [
"/usr/lib/libssh2.so.1",
"/usr/local/lib/libssh2.so.1",
- "/lib/libssh2.so.1"
+ "/lib/libssh2.so.1",
]
-
+
for target in symlink_targets:
if not os.path.exists(target):
try:
@@ -1015,7 +1302,7 @@ def fix_ssh2_library_issue():
printc(f"Created symlink: {found_lib} -> {target}", col.OKGREEN)
except:
printc(f"Failed to create symlink for {target}", col.WARNING)
-
+
# Also check if we need to symlink in PHP extensions directory
php_ext_dir = "/home/xc_vm/bin/php/lib/php/extensions/"
if os.path.exists(php_ext_dir):
@@ -1026,53 +1313,67 @@ def fix_ssh2_library_issue():
printc(f"Found PHP ssh2.so at: {ssh2_so_path}", col.OKGREEN)
break
else:
- printc("Warning: libssh2.so.1 not found. SSH2 may not work properly.", col.WARNING)
-
+ printc(
+ "Warning: libssh2.so.1 not found. SSH2 may not work properly.", col.WARNING
+ )
+
# Try to find any libssh2 version
try:
- ret, out, err = run_command("find /usr -name 'libssh2.so*' 2>/dev/null", capture_output=True)
+ ret, out, err = run_command(
+ "find /usr -name 'libssh2.so*' 2>/dev/null", capture_output=True
+ )
if ret == 0 and out:
- libs = out.strip().split('\n')
+ libs = out.strip().split("\n")
if libs:
found_lib = libs[0]
printc(f"Found alternative SSH2 library: {found_lib}", col.OKGREEN)
-
+
# Create symlink
run_command(f"ln -sf {found_lib} /usr/lib/libssh2.so.1")
- printc(f"Created symlink: {found_lib} -> /usr/lib/libssh2.so.1", col.OKGREEN)
+ printc(
+ f"Created symlink: {found_lib} -> /usr/lib/libssh2.so.1",
+ col.OKGREEN,
+ )
except:
pass
-
+
# NO deshabilitar la extensión ssh2 - solo crear enlaces
+
if __name__ == "__main__":
##################################################
# START #
##################################################
-
+
printc("XC_VM Multi-Distribution Installer", col.OKGREEN, 2)
-
+
# NUEVO: Verificar si se ejecuta como root
check_root()
-
+
# Detect distribution
dist_info = detect_distribution()
- printc(f"Detected: {dist_info['id']} {dist_info['version']} ({dist_info['family']} family)", col.OKGREEN)
-
+ printc(
+ f"Detected: {dist_info['id']} {dist_info['version']} ({dist_info['family']} family)",
+ col.OKGREEN,
+ )
+
# NUEVO: Instalar prerequisitos antes de continuar
install_prerequisites(dist_info)
-
+
if not check_supported_distro(dist_info):
- printc(f"Warning: {dist_info['id']} {dist_info['version']} is not officially supported", col.WARNING)
+ printc(
+ f"Warning: {dist_info['id']} {dist_info['version']} is not officially supported",
+ col.WARNING,
+ )
response = input("Continue anyway? (Y/N): ").strip().upper()
- if response != 'Y':
+ if response != "Y":
sys.exit(1)
printc("Continuing with installation...", col.WARNING)
-
+
# Try to download XC_VM if not present
has_valid_archive = False
archive_path = None
-
+
# Check for existing valid archives
if os.path.exists("./xc_vm.tar.gz") and is_valid_tar("./xc_vm.tar.gz"):
has_valid_archive = True
@@ -1082,7 +1383,7 @@ if __name__ == "__main__":
has_valid_archive = True
archive_path = "./XC_VM.zip"
printc("Found valid XC_VM.zip", col.OKGREEN)
-
+
# Download if needed
if not has_valid_archive:
if download_xc_vm():
@@ -1093,10 +1394,15 @@ if __name__ == "__main__":
elif os.path.exists("./XC_VM.zip") and is_valid_zip("./XC_VM.zip"):
has_valid_archive = True
archive_path = "./XC_VM.zip"
-
+
if not has_valid_archive or not archive_path:
- printc("XC_VM package not found or invalid. Please download manually.", col.FAIL)
- printc("You can download from: https://github.com/Vateron-Media/XC_VM/releases", col.OKBLUE)
+ printc(
+ "XC_VM package not found or invalid. Please download manually.", col.FAIL
+ )
+ printc(
+ "You can download from: https://github.com/Vateron-Media/XC_VM/releases",
+ col.OKBLUE,
+ )
sys.exit(1)
rHost = "127.0.0.1"
@@ -1105,27 +1411,32 @@ if __name__ == "__main__":
rPassword = generate_random_password(32) # Password de 32 caracteres
rDatabase = "xc_vm"
rPort = 3306
-
+
# Ask for MariaDB root password (or generate)
printc("MariaDB Root Password Configuration", col.OKBLUE)
print("For security, you should set a strong root password for MariaDB.")
print("Leave empty to generate a random password.")
-
- root_password = input("MariaDB root password (or press Enter to generate): ").strip()
+
+ root_password = input(
+ "MariaDB root password (or press Enter to generate): "
+ ).strip()
if not root_password:
root_password = generate_root_password() # Ahora genera de 20 caracteres
printc(f"Generated root password: {root_password}", col.OKGREEN)
else:
# Si el usuario proporciona una contraseña, asegurar que tenga al menos 20 caracteres
if len(root_password) < 20:
- printc(f"Warning: Provided password is only {len(root_password)} characters. For security, consider using at least 20 characters.", col.WARNING)
+ printc(
+ f"Warning: Provided password is only {len(root_password)} characters. For security, consider using at least 20 characters.",
+ col.WARNING,
+ )
response = input("Continue with provided password? (Y/N): ").strip().upper()
- if response != 'Y':
+ if response != "Y":
root_password = generate_root_password()
printc(f"Using generated root password: {root_password}", col.OKGREEN)
else:
printc("Using provided root password", col.OKGREEN)
-
+
if os.path.exists("/home/xc_vm/"):
printc("XC_VM Directory Exists!", col.WARNING)
while True:
@@ -1134,14 +1445,14 @@ if __name__ == "__main__":
break
if rAnswer == "N":
sys.exit(1)
-
+
##################################################
# SYSTEM PREPARATION #
##################################################
-
+
printc("Preparing System", col.OKBLUE)
-
- if dist_info['family'] == 'debian':
+
+ if dist_info["family"] == "debian":
# Debian/Ubuntu
printc("Cleaning package locks", col.OKBLUE)
for rFile in [
@@ -1155,31 +1466,34 @@ if __name__ == "__main__":
os.remove(rFile)
except:
pass
-
+
printc("Updating system", col.OKBLUE)
run_command("apt-get update -y")
-
+
# Install MariaDB repository
if not install_mariadb_repo(dist_info):
printc("Using system MariaDB repository", col.WARNING)
-
+
# Stop conflicting services (like bash script)
printc("Stopping conflicting services (Apache/System Nginx)...", col.OKBLUE)
run_command("systemctl stop apache2 nginx 2>/dev/null || true")
run_command("systemctl disable apache2 nginx 2>/dev/null || true")
-
+
# Remove conflicting packages
for rPackage in rRemove:
printc(f"Removing {rPackage}", col.OKBLUE)
run_command(f"apt-get remove {rPackage} -y")
-
+
# Install packages in groups (like bash script)
printc("Installing system packages", col.OKBLUE)
-
- # Select appropriate package list based on distribution
- printc(f"DEBUG: Distribution detected: {dist_info['id']} version: {dist_info['version']}", col.OKBLUE)
- if dist_info['id'] == 'ubuntu' and dist_info['version'].startswith("20"):
+ # Select appropriate package list based on distribution
+ printc(
+ f"DEBUG: Distribution detected: {dist_info['id']} version: {dist_info['version']}",
+ col.OKBLUE,
+ )
+
+ if dist_info["id"] == "ubuntu" and dist_info["version"].startswith("20"):
printc("Using Ubuntu 20.04 compatible package installation", col.OKBLUE)
printc("Skipping OpenSSL 3 (incompatible with Ubuntu 20.04)", col.WARNING)
@@ -1190,29 +1504,44 @@ if __name__ == "__main__":
run_command("apt-get autoclean || true")
# Install all packages from PACKAGES['ubuntu20']
- ubuntu20_packages = PACKAGES.get('ubuntu20', [])
+ ubuntu20_packages = PACKAGES.get("ubuntu20", [])
if ubuntu20_packages:
packages_str = " ".join(ubuntu20_packages)
- printc(f"Installing Ubuntu 20.04 packages ({len(ubuntu20_packages)} packages)...", col.OKBLUE)
- run_command(f"DEBIAN_FRONTEND=noninteractive apt-get -yq install {packages_str} || echo 'Some packages may not be available'")
+ printc(
+ f"Installing Ubuntu 20.04 packages ({len(ubuntu20_packages)} packages)...",
+ col.OKBLUE,
+ )
+ run_command(
+ f"DEBIAN_FRONTEND=noninteractive apt-get -yq install {packages_str} || echo 'Some packages may not be available'"
+ )
# Install OpenSSL 3 compatible library for PHP binaries (Ubuntu 20.04 specific)
- printc("Installing OpenSSL 3 compatibility library for PHP binaries...", col.OKBLUE)
+ printc(
+ "Installing OpenSSL 3 compatibility library for PHP binaries...",
+ col.OKBLUE,
+ )
try:
# Download and install libssl3 from Ubuntu 22.04 repository (compatible)
run_command(
- "wget -qO /tmp/libssl3_ubuntu20.deb \"https://packages.debian.org/trixie/amd64/libssl3/download\" "
- "|| wget -qO /tmp/libssl3_ubuntu20.deb \"http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl3_3.0.2-0ubuntu1_amd64.deb\""
+ 'wget -qO /tmp/libssl3_ubuntu20.deb "https://packages.debian.org/trixie/amd64/libssl3/download" '
+ '|| wget -qO /tmp/libssl3_ubuntu20.deb "http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl3_3.0.2-0ubuntu1_amd64.deb"'
)
# Try to install with --force-depends if needed
- run_command("dpkg --force-depends -i /tmp/libssl3_ubuntu20.deb 2>/dev/null || dpkg -i /tmp/libssl3_ubuntu20.deb 2>/dev/null || true")
+ run_command(
+ "dpkg --force-depends -i /tmp/libssl3_ubuntu20.deb 2>/dev/null || dpkg -i /tmp/libssl3_ubuntu20.deb 2>/dev/null || true"
+ )
# Alternative method if library not present
if not os.path.exists("/usr/lib/x86_64-linux-gnu/libssl.so.3"):
- printc("Trying alternative OpenSSL 3 installation method...", col.OKBLUE)
+ printc(
+ "Trying alternative OpenSSL 3 installation method...",
+ col.OKBLUE,
+ )
run_command("find /usr -name 'libssl.so*' 2>/dev/null | head -5")
- run_command("ls -la /usr/lib/x86_64-linux-gnu/libssl* 2>/dev/null || ls -la /usr/lib/libssl* 2>/dev/null || true")
+ run_command(
+ "ls -la /usr/lib/x86_64-linux-gnu/libssl* 2>/dev/null || ls -la /usr/lib/libssl* 2>/dev/null || true"
+ )
run_command("rm -f /tmp/libssl3_ubuntu20.deb")
printc("OpenSSL 3 library installation completed", col.OKGREEN)
@@ -1220,10 +1549,19 @@ if __name__ == "__main__":
except Exception as e:
printc(f"OpenSSL 3 installation failed: {e}", col.WARNING)
printc("PHP binaries may not work without libssl.so.3", col.WARNING)
- printc("You may need to manually install compatible OpenSSL 3 library", col.WARNING)
+ printc(
+ "You may need to manually install compatible OpenSSL 3 library",
+ col.WARNING,
+ )
- elif dist_info['id'] == 'ubuntu' and (dist_info['version'].startswith("24") or dist_info['version'].startswith("24.04")):
- printc("Using Ubuntu 24.04 compatible package installation (t64 transition)", col.OKBLUE)
+ elif dist_info["id"] == "ubuntu" and (
+ dist_info["version"].startswith("24")
+ or dist_info["version"].startswith("24.04")
+ ):
+ printc(
+ "Using Ubuntu 24.04 compatible package installation (t64 transition)",
+ col.OKBLUE,
+ )
# Fix broken packages first (especially important for Ubuntu 24.04)
printc("Fixing broken packages from t64 transition...", col.OKBLUE)
@@ -1231,20 +1569,28 @@ if __name__ == "__main__":
run_command("apt-get autoremove -y || true")
run_command("apt-get autoclean || true")
- printc("Ubuntu 24.04 already has OpenSSL 3.13+ - no additional installation needed", col.OKGREEN)
+ printc(
+ "Ubuntu 24.04 already has OpenSSL 3.13+ - no additional installation needed",
+ col.OKGREEN,
+ )
# Install all packages from PACKAGES['ubuntu24']
- ubuntu24_packages = PACKAGES.get('ubuntu24', [])
+ ubuntu24_packages = PACKAGES.get("ubuntu24", [])
if ubuntu24_packages:
packages_str = " ".join(ubuntu24_packages)
- printc(f"Installing Ubuntu 24.04 packages ({len(ubuntu24_packages)} packages)...", col.OKBLUE)
- run_command(f"DEBIAN_FRONTEND=noninteractive apt-get -yq install {packages_str} || echo 'Some packages may not be available'")
+ printc(
+ f"Installing Ubuntu 24.04 packages ({len(ubuntu24_packages)} packages)...",
+ col.OKBLUE,
+ )
+ run_command(
+ f"DEBIAN_FRONTEND=noninteractive apt-get -yq install {packages_str} || echo 'Some packages may not be available'"
+ )
# Fix broken packages again after installation
printc("Final fix for any remaining broken packages...", col.OKBLUE)
run_command("apt --fix-broken install -y || true")
- elif dist_info['id'] == 'debian' and dist_info['version'].startswith("13"):
+ elif dist_info["id"] == "debian" and dist_info["version"].startswith("13"):
printc("Using Debian 13 compatible package installation", col.OKBLUE)
# Fix broken packages first
@@ -1253,24 +1599,32 @@ if __name__ == "__main__":
run_command("apt-get autoremove -y || true")
run_command("apt-get autoclean || true")
- printc("Debian 13 already has OpenSSL 3.x - no additional installation needed", col.OKGREEN)
+ printc(
+ "Debian 13 already has OpenSSL 3.x - no additional installation needed",
+ col.OKGREEN,
+ )
# Install packages from PACKAGES['debian']
- debian_packages = PACKAGES.get('debian13', [])
+ debian_packages = PACKAGES.get("debian13", [])
if debian_packages:
# Optionally install in batches of 10 to avoid long command lines
batch_size = 10
for i in range(0, len(debian_packages), batch_size):
- batch = debian_packages[i:i + batch_size]
+ batch = debian_packages[i : i + batch_size]
packages_str = " ".join(batch)
- printc(f"Installing package group {i // batch_size + 1} ({len(batch)} packages)...", col.OKBLUE)
- run_command(f"DEBIAN_FRONTEND=noninteractive apt-get -yq install {packages_str} || echo 'Some packages may not be available'")
+ printc(
+ f"Installing package group {i // batch_size + 1} ({len(batch)} packages)...",
+ col.OKBLUE,
+ )
+ run_command(
+ f"DEBIAN_FRONTEND=noninteractive apt-get -yq install {packages_str} || echo 'Some packages may not be available'"
+ )
# Fix broken packages again after installation
printc("Final fix for any remaining broken packages...", col.OKBLUE)
run_command("apt --fix-broken install -y || true")
- elif dist_info['id'] == 'ubuntu' and dist_info['version'].startswith("22"):
+ elif dist_info["id"] == "ubuntu" and dist_info["version"].startswith("22"):
printc("Using Ubuntu 22.04 compatible package installation", col.OKBLUE)
# Fix broken packages first
@@ -1280,20 +1634,28 @@ if __name__ == "__main__":
run_command("apt-get autoclean || true")
# Ubuntu 22.04 already has OpenSSL 3.0, no special handling needed
- printc("Ubuntu 22.04 has OpenSSL 3.0 - compatible with PHP binaries", col.OKGREEN)
+ printc(
+ "Ubuntu 22.04 has OpenSSL 3.0 - compatible with PHP binaries",
+ col.OKGREEN,
+ )
# Install all packages from PACKAGES['ubuntu22']
- ubuntu22_packages = PACKAGES.get('ubuntu22', [])
+ ubuntu22_packages = PACKAGES.get("ubuntu22", [])
if ubuntu22_packages:
packages_str = " ".join(ubuntu22_packages)
- printc(f"Installing Ubuntu 22.04 packages ({len(ubuntu22_packages)} packages)...", col.OKBLUE)
- run_command(f"DEBIAN_FRONTEND=noninteractive apt-get -yq install {packages_str} || echo 'Some packages may not be available'")
+ printc(
+ f"Installing Ubuntu 22.04 packages ({len(ubuntu22_packages)} packages)...",
+ col.OKBLUE,
+ )
+ run_command(
+ f"DEBIAN_FRONTEND=noninteractive apt-get -yq install {packages_str} || echo 'Some packages may not be available'"
+ )
# Fix broken packages again after installation
printc("Final fix for any remaining broken packages...", col.OKBLUE)
run_command("apt --fix-broken install -y || true")
- elif dist_info['id'] == 'debian' and dist_info['version'].startswith("11"):
+ elif dist_info["id"] == "debian" and dist_info["version"].startswith("11"):
printc("Using Debian 11 compatible package installation", col.OKBLUE)
# Fix broken packages first
@@ -1303,30 +1665,40 @@ if __name__ == "__main__":
run_command("apt-get autoclean || true")
# Debian 11 may need OpenSSL 3 for PHP binaries
- printc("Installing OpenSSL 3 compatibility library for PHP binaries...", col.OKBLUE)
+ printc(
+ "Installing OpenSSL 3 compatibility library for PHP binaries...",
+ col.OKBLUE,
+ )
try:
run_command(
"wget -qO /tmp/libssl3_debian11.deb "
- "\"http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl3_3.0.2-0ubuntu1_amd64.deb\""
+ '"http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl3_3.0.2-0ubuntu1_amd64.deb"'
+ )
+ run_command(
+ "dpkg --force-depends -i /tmp/libssl3_debian11.deb 2>/dev/null || dpkg -i /tmp/libssl3_debian11.deb 2>/dev/null || true"
)
- run_command("dpkg --force-depends -i /tmp/libssl3_debian11.deb 2>/dev/null || dpkg -i /tmp/libssl3_debian11.deb 2>/dev/null || true")
run_command("rm -f /tmp/libssl3_debian11.deb")
printc("OpenSSL 3 library installation completed", col.OKGREEN)
except Exception as e:
printc(f"OpenSSL 3 installation warning: {e}", col.WARNING)
# Install packages from PACKAGES['debian11']
- debian11_packages = PACKAGES.get('debian11', [])
+ debian11_packages = PACKAGES.get("debian11", [])
if debian11_packages:
packages_str = " ".join(debian11_packages)
- printc(f"Installing Debian 11 packages ({len(debian11_packages)} packages)...", col.OKBLUE)
- run_command(f"DEBIAN_FRONTEND=noninteractive apt-get -yq install {packages_str} || echo 'Some packages may not be available'")
+ printc(
+ f"Installing Debian 11 packages ({len(debian11_packages)} packages)...",
+ col.OKBLUE,
+ )
+ run_command(
+ f"DEBIAN_FRONTEND=noninteractive apt-get -yq install {packages_str} || echo 'Some packages may not be available'"
+ )
# Fix broken packages again after installation
printc("Final fix for any remaining broken packages...", col.OKBLUE)
run_command("apt --fix-broken install -y || true")
- elif dist_info['id'] == 'debian' and dist_info['version'].startswith("12"):
+ elif dist_info["id"] == "debian" and dist_info["version"].startswith("12"):
printc("Using Debian 12 compatible package installation", col.OKBLUE)
# Fix broken packages first
@@ -1335,14 +1707,22 @@ if __name__ == "__main__":
run_command("apt-get autoremove -y || true")
run_command("apt-get autoclean || true")
- printc("Debian 12 already has OpenSSL 3.x - no additional installation needed", col.OKGREEN)
+ printc(
+ "Debian 12 already has OpenSSL 3.x - no additional installation needed",
+ col.OKGREEN,
+ )
# Install packages from PACKAGES['debian'] (generic debian list works for 12)
- debian_packages = PACKAGES.get('debian', [])
+ debian_packages = PACKAGES.get("debian", [])
if debian_packages:
packages_str = " ".join(debian_packages)
- printc(f"Installing Debian 12 packages ({len(debian_packages)} packages)...", col.OKBLUE)
- run_command(f"DEBIAN_FRONTEND=noninteractive apt-get -yq install {packages_str} || echo 'Some packages may not be available'")
+ printc(
+ f"Installing Debian 12 packages ({len(debian_packages)} packages)...",
+ col.OKBLUE,
+ )
+ run_command(
+ f"DEBIAN_FRONTEND=noninteractive apt-get -yq install {packages_str} || echo 'Some packages may not be available'"
+ )
# Fix broken packages again after installation
printc("Final fix for any remaining broken packages...", col.OKBLUE)
@@ -1350,59 +1730,74 @@ if __name__ == "__main__":
else:
# Fallback для других версий Debian/Ubuntu
- debian_packages = PACKAGES.get('debian', [])
- printc(f"Using standard Debian/Ubuntu package list for {dist_info['id']} {dist_info['version']}", col.OKBLUE)
+ debian_packages = PACKAGES.get("debian", [])
+ printc(
+ f"Using standard Debian/Ubuntu package list for {dist_info['id']} {dist_info['version']}",
+ col.OKBLUE,
+ )
# Install packages
if debian_packages:
packages_str = " ".join(debian_packages)
- printc(f"Installing Debian packages ({len(debian_packages)} packages)...", col.OKBLUE)
- run_command(f"DEBIAN_FRONTEND=noninteractive apt-get -yq install {packages_str} || echo 'Some packages may not be available'")
+ printc(
+ f"Installing Debian packages ({len(debian_packages)} packages)...",
+ col.OKBLUE,
+ )
+ run_command(
+ f"DEBIAN_FRONTEND=noninteractive apt-get -yq install {packages_str} || echo 'Some packages may not be available'"
+ )
# Install SSH2 libraries specifically for unknown distributions
printc("Installing SSH2 libraries...", col.OKBLUE)
- run_command("apt-get install -y libssh2-1 libssh2-1-dev || apt-get install -y libssh2-1 libssh2-1t64 || true")
-
+ run_command(
+ "apt-get install -y libssh2-1 libssh2-1-dev || apt-get install -y libssh2-1 libssh2-1t64 || true"
+ )
+
# Fix SSH2 library issue
printc("Configuring SSH2 libraries", col.OKBLUE)
fix_ssh2_library_issue()
-
- elif dist_info['family'] == 'redhat':
+
+ elif dist_info["family"] == "redhat":
# RedHat based distributions
printc("Configuring repositories", col.OKBLUE)
-
+
# Install EPEL
run_command("yum install -y epel-release")
-
+
# Install MariaDB repository
if not install_mariadb_repo(dist_info):
printc("Using system MariaDB repository", col.WARNING)
-
+
printc("Updating system", col.OKBLUE)
run_command("yum update -y")
-
+
# Install system packages from PACKAGES['redhat']
- redhat_packages = PACKAGES.get('redhat', [])
+ redhat_packages = PACKAGES.get("redhat", [])
if redhat_packages:
packages_str = " ".join(redhat_packages)
- printc(f"Installing RedHat packages ({len(redhat_packages)} packages)...", col.OKBLUE)
- run_command(f"yum install -y {packages_str} || echo 'Some packages may not be available'")
-
+ printc(
+ f"Installing RedHat packages ({len(redhat_packages)} packages)...",
+ col.OKBLUE,
+ )
+ run_command(
+ f"yum install -y {packages_str} || echo 'Some packages may not be available'"
+ )
+
# Ensure libssh2 libraries are installed
printc("Verifying SSH2 libraries for RedHat...", col.OKBLUE)
run_command("yum install -y libssh2 libssh2-devel || true")
-
+
# Enable services
run_command("systemctl enable mariadb")
run_command("systemctl enable crond")
-
+
# Fix SSH2 library issue
printc("Configuring SSH2 libraries", col.OKBLUE)
fix_ssh2_library_issue()
else:
printc(f"Unsupported distribution family: {dist_info['family']}", col.FAIL)
sys.exit(1)
-
+
# Create user if doesn't exist (like bash script)
printc("Creating/verifying xc_vm user", col.OKBLUE)
try:
@@ -1413,12 +1808,14 @@ if __name__ == "__main__":
raise Exception("User not found")
except:
printc("Creating user xc_vm", col.OKBLUE)
- if dist_info['family'] == 'debian':
- run_command("adduser --system --shell /bin/false --no-create-home --home /nonexistent --group --disabled-login xc_vm")
+ if dist_info["family"] == "debian":
+ run_command(
+ "adduser --system --shell /bin/false --no-create-home --home /nonexistent --group --disabled-login xc_vm"
+ )
else: # redhat
run_command("groupadd -r xc_vm")
run_command("useradd -r -g xc_vm -s /bin/false -M -d /nonexistent xc_vm")
-
+
if not os.path.exists("/home/xc_vm"):
os.makedirs("/home/xc_vm", exist_ok=True)
run_command("chown xc_vm:xc_vm /home/xc_vm")
@@ -1426,86 +1823,110 @@ if __name__ == "__main__":
##################################################
# INSTALL XC_VM #
##################################################
-
+
printc("Installing XC_VM", col.OKBLUE)
-
+
# Extract the archive
if not extract_archive(archive_path):
printc("Failed to extract archive! Exiting", col.FAIL)
sys.exit(1)
-
+
# Verify extraction
- if not os.path.exists("/home/xc_vm/status"):
- printc("Extraction failed: /home/xc_vm/status not found", col.FAIL)
+ if not os.path.exists("/home/xc_vm/console.php"):
+ printc("Extraction failed: /home/xc_vm/console.php not found", col.FAIL)
sys.exit(1)
else:
printc("XC_VM extracted successfully", col.OKGREEN)
-
+
# Instalar binarios específicos para distribuciones soportadas
- dist_id = dist_info['id']
- version = dist_info['version']
+ dist_id = dist_info["id"]
+ version = dist_info["version"]
# Verificar si la distribución tiene parches disponibles
- if dist_id in ['ubuntu', 'debian']:
+ if dist_id in ["ubuntu", "debian"]:
# Ubuntu: 20, 22, 24
- if dist_id == 'ubuntu' and any(version.startswith(v) for v in ['20', '22', '24']):
+ if dist_id == "ubuntu" and any(
+ version.startswith(v) for v in ["20", "22", "24"]
+ ):
if not install_distribution_binaries(dist_id, version):
- printc(f"Warning: Failed to install {dist_id} {version} specific binaries", col.WARNING)
+ printc(
+ f"Warning: Failed to install {dist_id} {version} specific binaries",
+ col.WARNING,
+ )
# Debian: 11, 12, 13
- elif dist_id == 'debian' and any(version.startswith(v) for v in ['11', '12', '13']):
+ elif dist_id == "debian" and any(
+ version.startswith(v) for v in ["11", "12", "13"]
+ ):
if not install_distribution_binaries(dist_id, version):
- printc(f"Warning: Failed to install {dist_id} {version} specific binaries", col.WARNING)
+ printc(
+ f"Warning: Failed to install {dist_id} {version} specific binaries",
+ col.WARNING,
+ )
else:
- printc(f"No specific patches available for {dist_id} {version}, using default binaries", col.OKBLUE)
+ printc(
+ f"No specific patches available for {dist_id} {version}, using default binaries",
+ col.OKBLUE,
+ )
- elif dist_id in ['rocky', 'almalinux', 'rhel', 'centos']:
- major = version.split('.')[0]
- if major in ['8', '9']:
+ elif dist_id in ["rocky", "almalinux", "rhel", "centos"]:
+ major = version.split(".")[0]
+ if major in ["8", "9"]:
if not install_distribution_binaries(dist_id, version):
- printc(f"Warning: Failed to install {dist_id} {version} specific binaries", col.WARNING)
+ printc(
+ f"Warning: Failed to install {dist_id} {version} specific binaries",
+ col.WARNING,
+ )
else:
- printc(f"No specific patches available for {dist_id} {version}, using default binaries", col.OKBLUE)
+ printc(
+ f"No specific patches available for {dist_id} {version}, using default binaries",
+ col.OKBLUE,
+ )
else:
- printc(f"No patches available for {dist_id} {version}, using default binaries", col.OKBLUE)
+ printc(
+ f"No patches available for {dist_id} {version}, using default binaries",
+ col.OKBLUE,
+ )
##################################################
# MariaDB CONFIGURATION #
##################################################
-
+
printc("Configuring MariaDB", col.OKBLUE)
-
+
# Secure MariaDB installation (using bash script logic)
secure_mariadb_installation(root_password, dist_info)
-
+
# Get total system RAM and generate config
total_ram_mb = get_system_ram_mb()
rMySQLCnf = generate_mysql_config(total_ram_mb)
-
+
# Write MySQL configuration (like bash script)
printc("Writing MySQL performance configuration", col.OKBLUE)
- if dist_info['family'] == 'debian':
+ if dist_info["family"] == "debian":
mysql_conf_path = "/etc/mysql/mariadb.conf.d/50-server.cnf"
else:
mysql_conf_path = "/etc/my.cnf.d/server.cnf"
-
+
# Ensure directory exists
os.makedirs(os.path.dirname(mysql_conf_path), exist_ok=True)
-
+
with io.open(mysql_conf_path, "w", encoding="utf-8") as rFile:
rFile.write(rMySQLCnf)
-
+
# Restart MariaDB
run_command("systemctl restart mariadb")
time.sleep(5)
-
+
# Connect to MariaDB and configure databases
printc("Setting up databases and users", col.OKBLUE)
-
+
# Create databases
- run_command(f'mariadb -u root -p"{root_password}" -e "CREATE DATABASE IF NOT EXISTS xc_vm; CREATE DATABASE IF NOT EXISTS xc_vm_migrate;"')
-
+ run_command(
+ f'mariadb -u root -p"{root_password}" -e "CREATE DATABASE IF NOT EXISTS xc_vm; CREATE DATABASE IF NOT EXISTS xc_vm_migrate;"'
+ )
+
# Import database schema
printc("Importing database schema", col.OKBLUE)
db_schema_path = "/home/xc_vm/bin/install/database.sql"
@@ -1513,19 +1934,19 @@ if __name__ == "__main__":
run_command(f'mariadb -u root -p"{root_password}" xc_vm < "{db_schema_path}"')
else:
printc(f"Database schema not found at {db_schema_path}", col.WARNING)
-
+
# Create XC_VM user with all privileges (like bash script)
printc("Creating database user", col.OKBLUE)
-
+
# Localhost grants
commands_localhost = [
f"CREATE USER IF NOT EXISTS '{rUsername}'@'localhost' IDENTIFIED BY '{rPassword}';",
f"GRANT ALL PRIVILEGES ON xc_vm.* TO '{rUsername}'@'localhost';",
f"GRANT ALL PRIVILEGES ON xc_vm_migrate.* TO '{rUsername}'@'localhost';",
f"GRANT ALL PRIVILEGES ON mysql.* TO '{rUsername}'@'localhost';",
- f"GRANT GRANT OPTION ON xc_vm.* TO '{rUsername}'@'localhost';"
+ f"GRANT GRANT OPTION ON xc_vm.* TO '{rUsername}'@'localhost';",
]
-
+
# 127.0.0.1 grants (REQUIRED for startup.php - like bash script)
commands_127 = [
f"CREATE USER IF NOT EXISTS '{rUsername}'@'127.0.0.1' IDENTIFIED BY '{rPassword}';",
@@ -1533,29 +1954,29 @@ if __name__ == "__main__":
f"GRANT ALL PRIVILEGES ON xc_vm_migrate.* TO '{rUsername}'@'127.0.0.1';",
f"GRANT ALL PRIVILEGES ON mysql.* TO '{rUsername}'@'127.0.0.1';",
f"GRANT GRANT OPTION ON xc_vm.* TO '{rUsername}'@'127.0.0.1';",
- "FLUSH PRIVILEGES;"
+ "FLUSH PRIVILEGES;",
]
-
+
all_commands = commands_localhost + commands_127
-
+
for cmd in all_commands:
run_command(f'mariadb -u root -p"{root_password}" -e "{cmd}"')
-
+
# Write XC_VM configuration
printc("Writing XC_VM configuration", col.OKBLUE)
os.makedirs(os.path.dirname(rConfigPath), exist_ok=True)
rConfigData = rConfig % (rUsername, rPassword)
with io.open(rConfigPath, "w", encoding="utf-8") as rFile:
rFile.write(rConfigData)
-
+
printc("MariaDB configuration completed", col.OKGREEN)
##################################################
# SYSTEM CONFIGURATION #
##################################################
-
+
printc("Configuring System", col.OKBLUE)
-
+
# Configure tmpfs mounts (like bash script)
if not os.path.exists("/etc/fstab"):
printc("/etc/fstab not found", col.WARNING)
@@ -1563,32 +1984,36 @@ if __name__ == "__main__":
try:
with open("/etc/fstab", "r") as f:
fstab_content = f.read()
-
+
if "/home/xc_vm/" not in fstab_content:
printc("Adding tmpfs mounts to /etc/fstab", col.OKBLUE)
# Create directories first
run_command("mkdir -p /home/xc_vm/content/streams")
run_command("mkdir -p /home/xc_vm/tmp")
-
+
with io.open("/etc/fstab", "a", encoding="utf-8") as rFile:
- rFile.write("\ntmpfs /home/xc_vm/content/streams tmpfs defaults,noatime,nosuid,nodev,noexec,mode=1777,size=90% 0 0\ntmpfs /home/xc_vm/tmp tmpfs defaults,noatime,nosuid,nodev,noexec,mode=1777,size=6G 0 0")
-
+ rFile.write(
+ "\ntmpfs /home/xc_vm/content/streams tmpfs defaults,noatime,nosuid,nodev,noexec,mode=1777,size=90% 0 0\ntmpfs /home/xc_vm/tmp tmpfs defaults,noatime,nosuid,nodev,noexec,mode=1777,size=6G 0 0"
+ )
+
# Mount immediately
run_command("mount -a")
printc("Reloading systemd to recognize fstab changes", col.OKBLUE)
run_command("systemctl daemon-reload")
except Exception as e:
printc(f"Error updating /etc/fstab: {e}", col.WARNING)
-
+
# Remove any restrictive sudoers rules (like bash script)
sudoers_file = "/etc/sudoers.d/xc_vm"
if os.path.exists(sudoers_file):
run_command(f"rm -f {sudoers_file}")
-
+
# Configure HTTP/HTTPS ports (interactive like bash script)
printc("Port Configuration", col.OKBLUE)
- print("If you want to change the ports, enter new values, or leave empty to use the default ports")
-
+ print(
+ "If you want to change the ports, enter new values, or leave empty to use the default ports"
+ )
+
while True:
http_port = input("HTTP port (default 80): ").strip()
if not http_port:
@@ -1620,52 +2045,58 @@ if __name__ == "__main__":
rFile.write(f"listen {https_port} ssl;")
printc(f"Ports configured: HTTP - {http_port}, HTTPS - {https_port}", col.OKGREEN)
-
+
# Configure sysctl (like bash script)
printc("Configuring kernel parameters (sysctl)", col.OKBLUE)
with io.open("/etc/sysctl.conf", "w", encoding="utf-8") as rFile:
rFile.write(rSysCtl)
run_command("sysctl -p > /dev/null 2>&1")
-
+
# Configure systemd limits (like bash script)
printc("Configuring systemd file limits", col.OKBLUE)
systemd_conf = "/etc/systemd/system.conf"
if os.path.exists(systemd_conf):
with open(systemd_conf, "r") as f:
systemd_content = f.read()
-
+
if "DefaultLimitNOFILE=655350" not in systemd_content:
with open(systemd_conf, "a") as f:
f.write("\nDefaultLimitNOFILE=655350\n")
-
+
user_conf = "/etc/systemd/user.conf"
if os.path.exists(user_conf):
with open(user_conf, "r") as f:
user_content = f.read()
-
+
if "DefaultLimitNOFILE=655350" not in user_content:
with open(user_conf, "a") as f:
f.write("\nDefaultLimitNOFILE=655350\n")
-
+
# Ask for systemd service (like bash script)
printc("Systemd Service Configuration", col.OKBLUE)
while True:
- enable_systemd = input("Do you want to configure and enable XC_VM as a systemd service? (Y/N): ").strip().upper()
+ enable_systemd = (
+ input(
+ "Do you want to configure and enable XC_VM as a systemd service? (Y/N): "
+ )
+ .strip()
+ .upper()
+ )
if enable_systemd in ["Y", "N"]:
break
-
+
if enable_systemd == "Y":
printc("Configuring systemd service", col.OKBLUE)
service_path = "/etc/systemd/system/xc_vm.service"
with io.open(service_path, "w", encoding="utf-8") as rFile:
rFile.write(rSystemd)
-
+
run_command("chmod +x /etc/systemd/system/xc_vm.service")
run_command("systemctl daemon-reload")
run_command("systemctl enable xc_vm")
run_command("systemctl start xc_vm")
printc("Systemd service configured and started", col.OKGREEN)
-
+
# Configure Redis
printc("Configuring Redis", col.OKBLUE)
redis_conf_path = "/home/xc_vm/bin/redis/redis.conf"
@@ -1680,15 +2111,15 @@ if __name__ == "__main__":
##################################################
# ACCESS CODE #
##################################################
-
+
printc("Generating access code", col.OKBLUE)
rCodeDir = "/home/xc_vm/bin/nginx/conf/codes/"
-
+
# CORREGIDO: Asegurar que el directorio de códigos exista
os.makedirs(rCodeDir, exist_ok=True)
-
+
admin_code = None
-
+
if os.path.exists(rCodeDir):
for filename in os.listdir(rCodeDir):
if filename.endswith(".conf"):
@@ -1704,27 +2135,27 @@ if __name__ == "__main__":
break
except:
pass
-
+
if not admin_code:
admin_code = generate_random_password(8)
printc(f"Generated access code: {admin_code}", col.OKGREEN)
-
+
# Insert into database
- insert_cmd = f'mariadb -u root -p"{root_password}" -e "USE xc_vm; INSERT INTO access_codes(code, type, enabled, groups) VALUES(\'{admin_code}\', 0, 1, \'[1]\');"'
+ insert_cmd = f"mariadb -u root -p\"{root_password}\" -e \"USE xc_vm; INSERT INTO access_codes(code, type, enabled, groups) VALUES('{admin_code}', 0, 1, '[1]');\""
run_command(insert_cmd)
-
+
# Create nginx configuration
template_path = os.path.join(rCodeDir, "template")
if os.path.exists(template_path):
with open(template_path, "r") as f:
template_content = f.read()
-
+
# Replace placeholders (like bash script)
template_content = template_content.replace("#WHITELIST#", "")
template_content = template_content.replace("#TYPE#", "admin")
template_content = template_content.replace("#CODE#", admin_code)
template_content = template_content.replace("#BURST#", "500")
-
+
code_conf_path = os.path.join(rCodeDir, f"{admin_code}.conf")
with io.open(code_conf_path, "w", encoding="utf-8") as rFile:
rFile.write(template_content)
@@ -1742,33 +2173,37 @@ if __name__ == "__main__":
##################################################
# FINAL CONFIGURATION #
##################################################
-
+
printc("Finalizing installation", col.OKBLUE)
-
+
# Set permissions (like bash script)
run_command("chown -R xc_vm:xc_vm /home/xc_vm")
-
+
# Set executable permissions on key files
run_command("chmod +x /home/xc_vm/service")
- run_command("chmod +x /home/xc_vm/status")
+ run_command("chmod +x /home/xc_vm/console.php")
run_command("chmod +x /home/xc_vm/bin/nginx/sbin/nginx")
-
+
# Check for RTMP nginx
nginx_rtmp_path = "/home/xc_vm/bin/nginx_rtmp/sbin/nginx_rtmp"
if os.path.exists(nginx_rtmp_path):
run_command(f"chmod +x {nginx_rtmp_path}")
-
+
# Set capabilities for binding to privileged ports (like bash script)
nginx_bin = "/home/xc_vm/bin/nginx/sbin/nginx"
if os.path.exists(nginx_bin):
- run_command(f"setcap 'cap_net_bind_service=+ep' {nginx_bin} 2>/dev/null || true")
-
+ run_command(
+ f"setcap 'cap_net_bind_service=+ep' {nginx_bin} 2>/dev/null || true"
+ )
+
if os.path.exists(nginx_rtmp_path):
- run_command(f"setcap 'cap_net_bind_service=+ep' {nginx_rtmp_path} 2>/dev/null || true")
-
+ run_command(
+ f"setcap 'cap_net_bind_service=+ep' {nginx_rtmp_path} 2>/dev/null || true"
+ )
+
# Save credentials in the format you requested
printc("Saving credentials", col.OKBLUE)
-
+
# Save to /root/credentials.txt
with io.open("/root/credentials.txt", "w", encoding="utf-8") as rFile:
rFile.write("MariaDB Root \n")
@@ -1777,7 +2212,7 @@ if __name__ == "__main__":
rFile.write(f"XC_VM Username: {rUsername}\n")
rFile.write(f"XC_VM Password: {rPassword}\n")
rFile.write(f"Database: {rDatabase}\n")
-
+
# Also save to installer directory (like original install script)
local_creds_path = os.path.join(rPath, "credentials.txt")
with io.open(local_creds_path, "w", encoding="utf-8") as rFile:
@@ -1786,60 +2221,62 @@ if __name__ == "__main__":
rFile.write(f"MariaDB Password: {rPassword}\n")
rFile.write(f"Database: {rDatabase}\n")
rFile.write(f"Admin Access Code: {admin_code}\n")
-
+
# Remove the old mariadb_root_password.txt file if it exists
if os.path.exists("/root/mariadb_root_password.txt"):
os.remove("/root/mariadb_root_password.txt")
-
+
printc("Credentials saved to /root/credentials.txt", col.OKGREEN)
printc(f"Credentials also saved to {local_creds_path}", col.OKGREEN)
-
+
# Restart service if systemd was enabled
if enable_systemd == "Y":
run_command("systemctl restart xc_vm")
-
+
# Mount tmpfs filesystems (like original install script)
run_command("mount -a >/dev/null 2>&1 || true")
-
+
# Reload systemd daemon
run_command("systemctl daemon-reload")
-
+
# Post-install startup (like bash script)
printc("Starting XC_VM processes...", col.OKBLUE)
time.sleep(10)
-
+
# Run status command
- if os.path.exists("/home/xc_vm/status"):
- run_command("/home/xc_vm/status 1")
-
+ if os.path.exists("/home/xc_vm/console.php"):
+ run_command("/home/xc_vm/bin/php/bin/php /home/xc_vm/console.php status 1")
+
# Set config permissions
run_command("chown -R xc_vm:xc_vm /home/xc_vm/config/")
-
+
# Run startup command via console.php
startup_cmd = "/home/xc_vm/console.php"
if os.path.exists(startup_cmd):
- run_command(f"/home/xc_vm/bin/php/bin/php {startup_cmd} startup >/dev/null 2>&1")
-
+ run_command(
+ f"/home/xc_vm/bin/php/bin/php {startup_cmd} startup >/dev/null 2>&1"
+ )
+
time.sleep(3)
-
+
# Final restart
if enable_systemd == "Y":
run_command("systemctl restart xc_vm")
-
+
# Get server IP
server_ip = getIP()
-
+
##################################################
# FINISHED - SHOW SUMMARY #
##################################################
-
+
printc("=" * 60, col.OKGREEN)
printc("INSTALLATION COMPLETED SUCCESSFULLY!", col.OKGREEN, 1)
printc(f"Distribution: {dist_info['id']} {dist_info['version']}", col.OKGREEN)
printc(f"Continue Setup: http://{server_ip}:{http_port}/{admin_code}", col.OKBLUE)
printc(f"Total RAM: {total_ram_mb}MB", col.OKGREEN)
-
+
printc("Credentials have been saved to:", col.OKBLUE)
printc("/root/credentials.txt", col.OKGREEN)
printc(f"{local_creds_path}", col.OKGREEN)
diff --git a/tools/test-install/README.md b/tools/test-install/README.md
index 09d870cd..0ca11ca7 100644
--- a/tools/test-install/README.md
+++ b/tools/test-install/README.md
@@ -35,7 +35,7 @@ docker exec -it xcvm-test-install bash
1. Распаковывает `XC_VM.zip`
2. Запускает `python3 install` с автоматическими ответами на интерактивные вопросы
3. Проверяет наличие ключевых файлов после установки:
- - `/home/xc_vm/status`, `console.php`, `autoload.php`, `bootstrap.php`
+ - `/home/xc_vm/console.php`, `autoload.php`, `bootstrap.php`
- `/home/xc_vm/config/config.ini`
- `/home/xc_vm/bin/nginx/sbin/nginx`
- `/home/xc_vm/bin/php/bin/php`
diff --git a/tools/test-install/auto_install.sh b/tools/test-install/auto_install.sh
index 9d879755..55932c8e 100644
--- a/tools/test-install/auto_install.sh
+++ b/tools/test-install/auto_install.sh
@@ -48,7 +48,7 @@ echo ""
# Проверяем что ключевые файлы на месте
echo "==> Post-install verification:"
CHECKS=(
- "/home/xc_vm/status"
+ "/home/xc_vm/console.php"
"/home/xc_vm/console.php"
"/home/xc_vm/autoload.php"
"/home/xc_vm/bootstrap.php"