Last active
October 14, 2025 09:32
-
-
Save moosavimaleki/bc2cb36a7c09ff5a6a4d27fa7e723914 to your computer and use it in GitHub Desktop.
test.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
xfreerdp3 /v:127.0.0.1:3389 /u:MyWindowsUser /p:MyWindowsPassword /cert:ignore +clipboard /dynamic-resolution | |
python /home/h-mousavi/.local/bin/linoffice/gui/linoffice.py | |
~/.local/bin/linoffice/venv/bin/podman-compose --file ~/.local/bin/linoffice/config/compose.yaml up | |
xfreerdp3 /v:127.0.0.1:3389 /u:MyWindowsUser /p:MyWindowsPassword /cert:ignore +clipboard /dynamic-resolution /drive:linux_home,/home/h-mousavi/ | |
------------------------ | |
shell | |
---------------------- | |
#!/bin/bash | |
# نام کانتینر ویندوز شما | |
CONTAINER_NAME="LinOffice" | |
RDP_SERVER="127.0.0.1:3389" | |
TIMEOUT=120 # حداکثر زمان انتظار برای بالا آمدن ویندوز به ثانیه | |
CHECK_INTERVAL=3 # فاصله زمانی بین هر تلاش برای اتصال به RDP به ثانیه | |
# چک کردن اینکه آیا کانتینر در حال اجرا است یا نه | |
if ! podman ps --filter "name=$CONTAINER_NAME" --quiet | grep -q .; then | |
# اگر کانتینر در حال اجرا نیست، ویندوز را راهاندازی میکنیم | |
echo "Windows is not running. Starting Windows with podman-compose..." | |
~/.local/bin/linoffice/venv/bin/podman-compose --file ~/.local/bin/linoffice/config/compose.yaml up -d | |
echo "Waiting for Windows to fully start..." | |
# تلاش برای اتصال به ویندوز با استفاده از xfreerdp3 هر ۳ ثانیه | |
SECONDS=0 | |
while ! xfreerdp3 /v:$RDP_SERVER /u:MyWindowsUser /p:MyWindowsPassword /cert:ignore +clipboard /dynamic-resolution /drive:linux_home,/home/h-mousavi/; do | |
echo "RDP connection failed, retrying..." | |
sleep $CHECK_INTERVAL | |
SECONDS=$((SECONDS + CHECK_INTERVAL)) | |
# اگر از زمان مشخص شده بیشتر منتظر ماندیم، پایان میدهیم | |
if [ $SECONDS -ge $TIMEOUT ]; then | |
echo "Timeout reached. Unable to connect to RDP after $TIMEOUT seconds." | |
exit 1 | |
fi | |
done | |
else | |
# اگر کانتینر در حال اجرا است، مستقیم به اتصال میپردازیم | |
echo "Windows is already running. Attempting to connect..." | |
# تلاش برای اتصال به ویندوز با استفاده از xfreerdp3 | |
xfreerdp3 /v:$RDP_SERVER /u:MyWindowsUser /p:MyWindowsPassword /cert:ignore +clipboard /dynamic-resolution /drive:linux_home,/home/h-mousavi/ | |
fi | |
# پایان اسکریپت | |
echo "Finished the script execution." |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ممنون از جزئیات. از لاگها معلومه اصلاً «درخواستی» به ویندوز نمیرسد (وقتی روشن/خاموش بودن ویندوز هیچ فرقی در خروجی FreeRDP ندارد و در Event Viewer هم چیزی ثبت نمیشود، یعنی کانکشن قبل از رسیدن به RDP-Tcp قطع میشود). در داکر/پادمَن این معمولاً به یکی از این دو علت است: | |
1. **hostfwdِ QEMU به IP اشتباه داخل ویندوز میرود** (پیشفرض ایمیج به 20.20.20.21 فوروارد میکند؛ اگر کارت شبکهٔ ویندوز IP دیگری گرفته باشد، همهٔ کانکشنها بیاثر میشوند). | |
2. **NAT rootless (slirp4netns)** روی 3389 رفتار عجیبی دارد و کانکشن را reset میکند. | |
برو سریع طبق این چکلیست جلو؛ هر کدام حل شد، همان را نگهدار: | |
--- | |
## قدم 1) IP داخل ویندوز را ثابت کن روی 20.20.20.21 | |
داخل ویندوز (PowerShell ادمین): | |
```powershell | |
$if = Get-NetIPInterface -AddressFamily IPv4 | | |
Where-Object {$_.InterfaceAlias -like "Ethernet*"} | | |
Sort-Object ifIndex | Select-Object -First 1 | |
# حذف DHCP فعلی (اگر وجود دارد) | |
Get-NetIPAddress -InterfaceIndex $if.InterfaceIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue | | |
Remove-NetIPAddress -Confirm:$false -ErrorAction SilentlyContinue | |
# ستاتیک: 20.20.20.21/24 و دروازه 20.20.20.1 | |
New-NetIPAddress -InterfaceIndex $if.InterfaceIndex -IPAddress 20.20.20.21 -PrefixLength 24 -DefaultGateway 20.20.20.1 | |
Set-DnsClientServerAddress -InterfaceIndex $if.InterfaceIndex -ServerAddresses 1.1.1.1,8.8.8.8 | |
Restart-Service TermService -Force | |
``` | |
سپس دوباره با **TLS** تست کن (RDP-Security قدیمی را کنار بگذار): | |
```bash | |
rm -f ~/.config/freerdp/known_hosts* ~/.freerdp/known_hosts* | |
xfreerdp3 /v:127.0.0.1:3389 /u:MyWindowsUser /p:MyWindowsPassword /sec:tls /cert:ignore +clipboard /dynamic-resolution | |
``` | |
> اگر این مرحله جواب بده، مشکل دقیقاً IP بوده (hostfwd → 20.20.20.21). | |
--- | |
## قدم 2) NAT پادمَن را سالم کن (اگر قدم 1 جواب نداد) | |
فایل compose را اینطور ساده کن: | |
* **UDP را فعلاً حذف کن** (فقط TCP): | |
```yaml | |
ports: | |
- "3390:3389/tcp" # پورت میزبان را هم عوض کن که مطمئن شو چیز دیگری مزاحم 3389 نیست | |
# - "3389:3389/udp" # موقتاً حذف | |
``` | |
* شبکه را از slirp به **pasta** تغییر بده (پایدارتر برای RDP): | |
```yaml | |
services: | |
windows: | |
# ... | |
networks: | |
- default | |
networks: | |
default: | |
driver: pasta | |
``` | |
سپس: | |
```bash | |
podman stop LinOffice && podman rm LinOffice | |
~/.local/bin/linoffice/venv/bin/podman-compose --file ~/.local/bin/linoffice/config/compose.yaml up -d | |
podman port LinOffice | |
``` | |
و تست روی پورت جدید: | |
```bash | |
xfreerdp3 /v:127.0.0.1:3390 /u:MyWindowsUser /p:MyWindowsPassword /sec:tls /cert:ignore +clipboard /dynamic-resolution | |
``` | |
--- | |
## قدم 3) دو تست خیلی سریع برای تشخیص مسیر | |
1. ببین اصلاً روی میزبان، لیسنر وجود دارد: | |
```bash | |
nc -vz 127.0.0.1 3389 # یا 3390 اگر عوض کردی | |
``` | |
اگر «succeeded» گفت ولی باز هم RDP reset شد و همچنان در ویندوز لاگی نمیافتد، یعنی Reset قبل از رسیدن به ویندوز رخ میدهد (مشکل NAT/hostfwd). | |
2. لاگ بوت کانتینر را با دقت چک کن تا **خط فرمان QEMU** را ببینی (باید شامل `hostfwd=tcp::3389-20.20.20.21:3389` باشد): | |
```bash | |
podman logs --tail=200 LinOffice | sed -n '1,200p' | |
``` | |
اگر مقصد hostfwd چیز دیگری بود یا چندتا hostfwd تکراری دیدی، همان علت است. | |
--- | |
## چند نکتهٔ تکمیلی | |
* وقتی TLS/NLA را تغییر میدهی، **TermService را ریستارت** کردی (درست است)؛ اما اگر تغییری میدهی و باز هم هیچ لاگی در `RdpCoreTS` و `LocalSessionManager` نمیافتد، مطمئن باش مشکل از ویندوز نیست. | |
* اگر هنوز گیر است، یکبار فقط **TCP روی پورت دیگر** امتحان کن (مثلاً 3390) و روی کلاینت هم `/sec:tls` بزن — خیلی از resetهای FreeRDP وقتی UDP باز است رخ میدهند. | |
* اگر خواستی همهچیز را از پایه تمیز کنی: کانتینر را پاک کن، **ولوم `linoffice_data` را هم پاک کن** و از نو بالا بیاور (هشدار: ویندوز داخل کانتینر پاک میشود). این کار مطمئن میکند اسکریپتهای QEMU با تنظیمات پیشفرض سالم بالا بیایند. | |
--- | |
### اگر بخواهی، خروجی این ۳ مورد را بده تا دقیق نقطهٔ ایراد را بگویم: | |
* `ipconfig` داخل ویندوز (IPv4 کارت شبکه) | |
* `podman logs LinOffice | head -n 80` (برای دیدن hostfwd) | |
* نتیجهٔ `xfreerdp3 ... /sec:tls` روی پورت جدید 3390 | |
با این سه تا، معمولاً درجا روشن میشود که IP داخل ویندوز با hostfwd همخوان نیست یا NAT پادمَن داستان دارد. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
version: "3.8" | |
volumes: | |
data: | |
external: true | |
name: linoffice_data | |
services: | |
windows: | |
image: ghcr.io/dockur/windows:4.34 | |
container_name: LinOffice | |
privileged: true | |
environment: | |
VERSION: "11" | |
RAM_SIZE: 4G | |
CPU_CORES: "4" | |
DISK_SIZE: 64G | |
USERNAME: MyWindowsUser | |
PASSWORD: MyWindowsPassword | |
ARGUMENTS: "-cpu host,arch_capabilities=off" | |
NETWORK: "user" # پیشفرض همین است؛ صراحتاً میگذاریم | |
# USER_PORTS: "3389" # ← نذار تا دوبار نشه | |
devices: | |
- /dev/kvm | |
- /dev/net/tun | |
cap_add: | |
- NET_ADMIN | |
ports: | |
- "8006:8006" | |
- "3389:3389/tcp" # ← فقط TCP | |
volumes: | |
- "data:/storage:Z" | |
- "./oem:/oem:Z" | |
- "/home/h-mousavi/Downloads:/shared:Z" | |
restart: on-failure | |
stop_grace_period: 120s |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# LinOffice Setup Script | |
CONTAINER_NAME="LinOffice" # should match the name in the compose.yaml | |
CONTAINER_EXISTS=0 # 0 = Does not exist (default), 1 = exists | |
# Absolute filepaths | |
USER_APPLICATIONS_DIR="${HOME}/.local/share/applications" | |
APPDATA_PATH="${HOME}/.local/share/linoffice" | |
# Ensure APPDATA_PATH exists before using it | |
mkdir -p "$APPDATA_PATH" | |
SUCCESS_FILE="${APPDATA_PATH}/success" | |
PROGRESS_FILE="${APPDATA_PATH}/setup_progress.log" | |
OUTPUT_LOG="${APPDATA_PATH}/setup_output.log" | |
# Relative filepaths | |
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | |
LINOFFICE_DIR="$SCRIPT_DIR" | |
LINOFFICE="$(realpath "${SCRIPT_DIR}/linoffice.sh")" | |
COMPOSE_FILE="$(realpath "${SCRIPT_DIR}/config/compose.yaml")" | |
LINOFFICE_CONF="$(realpath "${SCRIPT_DIR}/config/linoffice.conf")" | |
OEM_DIR="$(realpath "${SCRIPT_DIR}/config/oem")" | |
LOCALE_REG_SCRIPT="$(realpath "${SCRIPT_DIR}/config/locale_reg.sh")" | |
LOCALE_LANG_SCRIPT="$(realpath "${SCRIPT_DIR}/config/locale_lang.sh")" | |
REGIONAL_REG="$(realpath "${SCRIPT_DIR}/config/oem/registry/regional_settings.reg")" | |
LOGFILE="${APPDATA_PATH}/windows_install.log" | |
APPS_DIR="$(realpath "${SCRIPT_DIR}/apps")" | |
DESKTOP_DIR="$(realpath "${APPS_DIR}/desktop")" | |
# Available and working freerdp commands | |
EXISTS_XFREERDP=false | |
EXISTS_XFREERDP3=false | |
EXISTS_FLATPAK_FREERDP=false | |
FREERDP_COMMAND="" # will be checked in the script whether it's xfreerdp, xfreerdp3, or the Flatpak version | |
FREERDP_SEC_RDP=false | |
FREERDP_NETWORK_LAN=false | |
FREERDP_NSC=false | |
FREERDP_XWAYLAND=false | |
# Progress tracking states | |
PROGRESS_REQUIREMENTS="requirements_completed" | |
PROGRESS_CONTAINER="container_created" | |
PROGRESS_OFFICE="office_installed" | |
PROGRESS_DESKTOP="desktop_files_installed" | |
# Command line arguments | |
DESKTOP_ONLY=false | |
FIRSTRUN=false | |
INSTALL_OFFICE_ONLY=false | |
HEALTHCHECK=false | |
# Colors for output | |
RED='\033[0;31m' | |
GREEN='\033[0;32m' | |
YELLOW='\033[1;33m' | |
NC='\033[0m' # No Color | |
USE_VENV=0 | |
COMPOSE_COMMAND="podman-compose" | |
# Redirect all output to the log file | |
exec > >(stdbuf -oL -eL sed -u 's/\x1b\[[0-9;]*m//g' | tee -a "$OUTPUT_LOG") 2>&1 | |
# Functions to print colored output | |
print_error() { | |
echo -e "${RED}ERROR:${NC} $1" >&2 | |
} | |
print_success() { | |
echo -e "${GREEN}SUCCESS:${NC} $1" | |
} | |
print_info() { | |
echo -e "${YELLOW}INFO:${NC} $1" | |
} | |
print_step() { | |
echo -e "\n${GREEN}Step $1:${NC} $2" | |
} | |
print_progress() { | |
echo -e "${GREEN}Progress:${NC} $1" | |
} | |
# Name: 'use_venv' | |
# Role: Activate virtual environment if available | |
use_venv() { | |
local venv_dir="$HOME/.local/bin/linoffice/venv" | |
local activate_script="$venv_dir/bin/activate" | |
print_info "Checking for virtual environment at: $venv_dir" | |
if [[ -f "$activate_script" ]]; then | |
print_info "Virtual environment found at $venv_dir" | |
source "$activate_script" | |
VENV_PATH="$venv_dir" | |
USE_VENV=1 | |
PYTHON_PATH="$venv_dir/bin/python3" | |
print_info "Virtual environment Python: $PYTHON_PATH" | |
USER_SITE_PATH=$($PYTHON_PATH -m site | grep USER_SITE | awk -F"'" '{print $2}') | |
PODMAN_COMPOSE_BIN="/home/h-mousavi/.local/bin/linoffice/venv/lib/python3.12/site-packages/podman_compose.py" | |
if [[ -f "$PODMAN_COMPOSE_BIN" ]]; then | |
print_info "Using podman-compose from virtual environment: $PODMAN_COMPOSE_BIN" | |
COMPOSE_COMMAND="$PYTHON_PATH $PODMAN_COMPOSE_BIN" | |
return 0 | |
else | |
print_info "podman-compose not found in virtual environment user packages at: $PODMAN_COMPOSE_BIN" | |
print_info "Checking if virtual environment can access system podman-compose..." | |
# Check if venv can access system podman-compose (due to --system-site-packages) | |
if command -v podman-compose &> /dev/null; then | |
print_info "Virtual environment can access system podman-compose" | |
COMPOSE_COMMAND="podman-compose" | |
return 0 | |
else | |
print_info "podman-compose not available in virtual environment or system" | |
print_info "Will check for system podman-compose instead" | |
# Don't return here - let the system check handle it | |
USE_VENV=0 # Reset to system mode since venv doesn't have podman-compose | |
return 1 | |
fi | |
fi | |
else | |
print_info "Virtual environment not found at $venv_dir, using system Python" | |
return 1 | |
fi | |
} | |
use_venv | |
# Function to display usage information | |
print_usage() { | |
print_info "Usage: $0 [--desktop] [--firstrun] [--installoffice] [--healthcheck]" | |
print_info "Options:" | |
print_info " (no flag) Run the installation script from the beginning" | |
print_info " --desktop Only recreate the desktop files (.desktop launchers)" | |
print_info " --firstrun Force RDP and Office installation checks" | |
print_info " --installoffice Only run the Office installation script script (in case the Windows installation has finished but Office is not installed)" | |
print_info " --healthcheck Check that the system requirements are met and dependencies are installed and the container is healthy" | |
exit 1 | |
} | |
# Parse command line arguments | |
while [[ $# -gt 0 ]]; do | |
case $1 in | |
--desktop) | |
DESKTOP_ONLY=true | |
shift | |
;; | |
--firstrun) | |
FIRSTRUN=true | |
shift | |
;; | |
--installoffice) | |
INSTALL_OFFICE_ONLY=true | |
shift | |
;; | |
--healthcheck) | |
HEALTHCHECK=true | |
shift | |
;; | |
--help) | |
print_usage | |
;; | |
*) | |
print_error "Unknown option: $1" | |
print_usage | |
;; | |
esac | |
done | |
# Function to exit with error | |
exit_with_error() { | |
print_error "$1" | |
exit 1 | |
} | |
# Progress tracking functions | |
function init_progress_file() { | |
mkdir -p "$(dirname "$PROGRESS_FILE")" | |
touch "$PROGRESS_FILE" | |
} | |
function mark_progress() { | |
local step=$1 | |
echo "$step" >> "$PROGRESS_FILE" | |
} | |
function check_progress() { | |
local step=$1 | |
if [ -f "$PROGRESS_FILE" ] && grep -q "^$step$" "$PROGRESS_FILE"; then | |
return 0 | |
else | |
return 1 | |
fi | |
} | |
function clear_progress() { | |
if [ -f "$PROGRESS_FILE" ]; then | |
rm "$PROGRESS_FILE" | |
fi | |
} | |
function check_linoffice_container() { | |
print_info "Checking if LinOffice container exists already" | |
if podman container exists "$CONTAINER_NAME"; then | |
print_info "Container exists already." | |
CONTAINER_EXISTS=1 | |
else | |
print_info "Container does not yet exist." | |
CONTAINER_EXISTS=0 | |
fi | |
} | |
# Function to detect and set the FreeRDP command | |
function detect_freerdp_command() { | |
# Determine availability of all FreeRDP variants and set EXISTS_* flags | |
EXISTS_XFREERDP=false | |
EXISTS_XFREERDP3=false | |
EXISTS_FLATPAK_FREERDP=false | |
if command -v xfreerdp3 &>/dev/null; then | |
EXISTS_XFREERDP3=true | |
fi | |
if command -v xfreerdp &>/dev/null; then | |
EXISTS_XFREERDP=true | |
fi | |
if command -v flatpak &>/dev/null; then | |
if flatpak list --columns=application | grep -q "^com.freerdp.FreeRDP$"; then | |
EXISTS_FLATPAK_FREERDP=true | |
fi | |
fi | |
# Set FREERDP_COMMAND to the first available in priority order: xfreerdp3 > flatpak > xfreerdp | |
if [ "$EXISTS_XFREERDP3" = true ]; then | |
FREERDP_COMMAND="xfreerdp3" | |
elif [ "$EXISTS_FLATPAK_FREERDP" = true ]; then | |
FREERDP_COMMAND="flatpak run --command=xfreerdp com.freerdp.FreeRDP" | |
elif [ "$EXISTS_XFREERDP" = true ]; then | |
FREERDP_COMMAND="xfreerdp" | |
else | |
FREERDP_COMMAND="" # Not found | |
fi | |
} | |
# Function to check if all requirements are met to run the Windows VM in Podman | |
function check_requirements() { | |
# Exit on any error | |
set -e | |
print_info "Starting LinOffice setup script..." | |
print_step "1" "Checking requirements" | |
# Check minimum RAM (8 GB) | |
print_info "Checking minimum RAM" | |
local ram_line=$((LINENO + 1)) | |
REQUIRED_RAM=7 # 8 GB shows up as 7.6 GiB so best to just set the threshold to 7 in this script | |
AVAILABLE_RAM="$(free -b | awk '/^Mem:/{print int($2/1024/1024/1024)}')" | |
if [ "$AVAILABLE_RAM" -lt "$REQUIRED_RAM" ]; then | |
exit_with_error "Insufficient RAM. Required: ${REQUIRED_RAM}GB, Available: ${AVAILABLE_RAM}GB. \ | |
Please upgrade your system memory to at least ${REQUIRED_RAM}GB. | |
The Windows VM needs 4 GB of RAM. If you still want to continue with the installation, for example if you are using zswap, you can change the minimum RAM required by editing line $ram_line in $SCRIPT_DIR/setup.sh and then run the setup again." | |
fi | |
print_success "Sufficient RAM detected: ${AVAILABLE_RAM}GB" | |
# Check minimum free storage (64 GB) | |
check_linoffice_container | |
if [ "$CONTAINER_EXISTS" -eq 1 ]; then | |
print_info "Container exists already. Skipping check if sufficient free storage is available." | |
else | |
print_info "Checking minimum free storage" | |
REQUIRED_STORAGE=64 | |
AVAILABLE_STORAGE=$(df -B1G --output=avail /home | tail -n 1 | awk '{print $1}') | |
if [ "$AVAILABLE_STORAGE" -lt "$REQUIRED_STORAGE" ]; then | |
exit_with_error "Insufficient free storage. Required: ${REQUIRED_STORAGE}GB, Available: ${AVAILABLE_STORAGE}GB \ | |
Please free up disk space or use a different storage device." | |
fi | |
print_success "Sufficient free storage detected: ${AVAILABLE_STORAGE}GB" | |
fi | |
# Check if computer supports virtualization | |
print_info "Checking virtualization support" | |
if ! command -v lscpu &> /dev/null; then | |
exit_with_error "lscpu command not found. Please install util-linux package." | |
fi | |
# Check for virtualization support | |
if lscpu | grep -qiE 'virtualization|vmx|svm'; then | |
echo "Virtualization is supported." | |
else | |
exit_with_error "CPU virtualization not supported or not enabled. | |
HOW TO FIX: | |
1. Reboot your computer and enter BIOS/UEFI settings (usually F2, F12, Del, or Esc during boot) | |
2. Look for virtualization settings: | |
- Intel: Enable 'Intel VT-x' or 'Intel Virtualization Technology' | |
- AMD: Enable 'AMD-V' or 'SVM Mode' | |
3. Save settings and reboot | |
4. If you can't find these options, your CPU may not support virtualization" | |
fi | |
# Additional check for KVM support | |
if [ ! -e /dev/kvm ]; then | |
exit_with_error "KVM device not available. Virtualization may not be enabled in BIOS. | |
HOW TO FIX: | |
1. Ensure virtualization is enabled in BIOS (see previous instructions) | |
2. Install KVM kernel modules: sudo modprobe kvm | |
3. For Intel CPUs: sudo modprobe kvm_intel | |
4. For AMD CPUs: sudo modprobe kvm_amd | |
5. Reboot if necessary" | |
fi | |
print_success "Virtualization support detected: $VIRT_SUPPORT" | |
# Check if podman is installed | |
print_info "Checking if podman is installed" | |
if ! command -v podman &> /dev/null; then | |
exit_with_error "podman is not installed. | |
HOW TO FIX: | |
Ubuntu/Debian: sudo apt update && sudo apt install podman | |
Fedora/RHEL: sudo dnf install podman | |
OpenSUSE: sudo zypper install podman | |
Arch Linux: sudo pacman -S podman | |
openSUSE: sudo zypper install podman | |
Or visit: https://podman.io/getting-started/installation" | |
fi | |
if ! podman info >/dev/null 2>&1; then | |
exit_with_error "Podman is not configured correctly or you lack sufficient permissions. Run 'podman info' to diagnose the issue." | |
fi | |
PODMAN_VERSION=$(podman --version) | |
print_success "podman is installed: $PODMAN_VERSION" | |
# Check if podman-compose is installed | |
print_info "Checking if podman-compose is installed" | |
print_info "Python environment: $(if [[ "$USE_VENV" -eq 1 ]]; then echo "Virtual environment at $VENV_PATH"; else echo "System Python"; fi)" | |
# Determine which Python to use for dependency checks | |
if [[ "$USE_VENV" -eq 1 ]]; then | |
PYTHON_CMD="$PYTHON_PATH" | |
PYTHON_ENV="virtual environment" | |
else | |
PYTHON_CMD="python3" | |
PYTHON_ENV="system" | |
fi | |
if [[ "$USE_VENV" -eq 0 ]]; then | |
# Use system podman-compose, not the one in ~/.local/bin which might be broken | |
if [[ -x "/usr/bin/podman-compose" ]]; then | |
COMPOSE_COMMAND="/usr/bin/podman-compose" | |
print_success "Using system podman-compose: /usr/bin/podman-compose" | |
elif command -v podman-compose &> /dev/null; then | |
COMPOSE_COMMAND="podman-compose" | |
print_success "Using podman-compose from PATH: $(command -v podman-compose)" | |
else | |
exit_with_error "podman-compose is not installed. | |
HOW TO FIX: | |
Option 1 - Using pip: pip3 install podman-compose | |
Option 2 - Using package manager: | |
Ubuntu/Debian: sudo apt install podman-compose | |
Fedora: sudo dnf install podman-compose | |
OpenSUSE: sudo zypper install podman-compose | |
Arch Linux: sudo pacman -S podman-compose | |
Or visit: https://github.com/containers/podman-compose" | |
fi | |
# Check if python-dotenv is installed (dependency of podman-compose) | |
if ! command -v $PYTHON_CMD &> /dev/null; then | |
exit_with_error "$PYTHON_CMD command not found. Please install Python 3." | |
fi | |
# Show which Python is being used for debugging | |
PYTHON_FULL_PATH=$(command -v $PYTHON_CMD) | |
print_info "Using $PYTHON_ENV Python: $PYTHON_FULL_PATH" | |
if $PYTHON_CMD -c "import dotenv" >/dev/null 2>&1; then | |
print_success "python-dotenv is installed in $PYTHON_ENV environment." | |
else | |
# Provide more detailed error information | |
print_error "python-dotenv is not installed or not accessible to $PYTHON_FULL_PATH" | |
print_info "Python version: $($PYTHON_CMD --version 2>&1)" | |
print_info "Python path: $PYTHON_FULL_PATH" | |
print_info "Available packages: $($PYTHON_CMD -m pip list 2>/dev/null | grep -i dotenv || echo 'No dotenv packages found')" | |
exit_with_error "python-dotenv is not installed in $PYTHON_ENV environment. | |
HOW TO FIX: | |
Using pip: $PYTHON_CMD -m pip install python-dotenv | |
If you don't have pip, you can install it with your package manager. | |
Ubuntu/Debian: sudo apt install python-dotenv | |
Fedora: sudo dnf install python-dotenv | |
OpenSUSE: sudo zypper install python-python-dotenv | |
Arch Linux: sudo pacman -S python-dotenv | |
If the package is installed but not detected, try: | |
- Check if you have multiple Python versions: which python3 | |
- Install for the specific Python: $PYTHON_FULL_PATH -m pip install python-dotenv" | |
fi | |
else | |
# When using virtual environment, check if podman-compose is installed in venv | |
if [[ -f "$PODMAN_COMPOSE_BIN" ]]; then | |
print_success "podman-compose is installed in virtual environment." | |
else | |
exit_with_error "podman-compose is not installed in virtual environment. | |
HOW TO FIX: | |
The virtual environment needs podman-compose installed. | |
Run: $PYTHON_PATH -m pip install podman-compose" | |
fi | |
# Check if python-dotenv is installed in virtual environment (dependency of podman-compose) | |
if $PYTHON_CMD -c "import dotenv" >/dev/null 2>&1; then | |
print_success "python-dotenv is installed in virtual environment." | |
else | |
print_error "python-dotenv is not installed in virtual environment." | |
print_info "Python version: $($PYTHON_CMD --version 2>&1)" | |
print_info "Python path: $PYTHON_CMD" | |
print_info "Available packages: $($PYTHON_CMD -m pip list 2>/dev/null | grep -i dotenv || echo 'No dotenv packages found')" | |
print_info "User site packages: $($PYTHON_CMD -m site --user-site 2>/dev/null || echo 'Unknown')" | |
# Check if it's available in user packages (installed with --user flag) | |
USER_SITE_PACKAGES=$($PYTHON_CMD -m site --user-site 2>/dev/null) | |
if [[ -n "$USER_SITE_PACKAGES" ]] && [[ -d "$USER_SITE_PACKAGES" ]] && find "$USER_SITE_PACKAGES" -name "*dotenv*" -type d 2>/dev/null | grep -q .; then | |
print_info "python-dotenv found in user site packages but not importable" | |
print_info "This might be due to virtual environment configuration issues" | |
fi | |
exit_with_error "python-dotenv is not installed in virtual environment. | |
HOW TO FIX: | |
The virtual environment needs python-dotenv installed. | |
Run: $PYTHON_CMD -m pip install python-dotenv | |
If packages were installed with --user flag by quickstart.sh, try: | |
$PYTHON_CMD -m pip install --user python-dotenv" | |
fi | |
fi | |
COMPOSE_VERSION=$($COMPOSE_COMMAND --version) | |
print_success "podman-compose is installed: $COMPOSE_VERSION" | |
# Check if FreeRDP is available | |
print_info "Checking if FreeRDP is available" | |
detect_freerdp_command | |
local FREERDP_MAJOR_VERSION="" | |
if [ -n "$FREERDP_COMMAND" ]; then | |
if [ "$FREERDP_COMMAND" = "xfreerdp" ]; then | |
FREERDP_MAJOR_VERSION=$(xfreerdp --version | head -n 1 | grep -o -m 1 '\b[0-9]\S*' | head -n 1 | cut -d'.' -f1) | |
elif [ "$FREERDP_COMMAND" = "xfreerdp3" ]; then | |
FREERDP_MAJOR_VERSION=$(xfreerdp3 --version | head -n 1 | grep -o -m 1 '\b[0-9]\S*' | head -n 1 | cut -d'.' -f1) | |
elif [ "$FREERDP_COMMAND" = "flatpak run --command=xfreerdp com.freerdp.FreeRDP" ]; then | |
FREERDP_MAJOR_VERSION=$(flatpak list --columns=application,version | grep "^com.freerdp.FreeRDP" | awk '{print $2}' | cut -d'.' -f1) | |
# Check if Flatpak has access to /home | |
if ! flatpak info --show-permissions com.freerdp.FreeRDP | grep -q "filesystems=.*home"; then | |
exit_with_error "Flatpak FreeRDP does not have access to /home directory. | |
HOW TO FIX: | |
1. Close any running FreeRDP instances | |
2. Run this command to grant access: | |
flatpak override --user --filesystem=home com.freerdp.FreeRDP | |
3. Run this setup script again" | |
fi | |
fi | |
if [[ ! $FREERDP_MAJOR_VERSION =~ ^[0-9]+$ ]] || ((FREERDP_MAJOR_VERSION < 3)); then | |
exit_with_error "FreeRDP version 3 or greater is required. Detected version: $FREERDP_MAJOR_VERSION" | |
fi | |
else | |
exit_with_error "FreeRDP is not installed | |
HOW TO FIX: | |
Option 1 - Using Flatpak and Flathub: flatpak install com.freerdp.FreeRDP | |
Option 2 - Using package manager: | |
Ubuntu/Debian: sudo apt install freerdp3-x11 | |
Fedora: sudo dnf install freerdp | |
OpenSUSE: sudo zypper install freerdp | |
Arch Linux: sudo pacman -S freerdp" | |
fi | |
if ! $FREERDP_COMMAND --version >/dev/null 2>&1; then | |
exit_with_error "FreeRDP command '$FREERDP_COMMAND' is not functional. Please ensure FreeRDP is correctly installed and configured." | |
fi | |
print_success "FreeRDP found. Using FreeRDP command '${FREERDP_COMMAND}'." | |
# Check if iptables modules are loaded | |
print_info "Checking iptables kernel modules" | |
if ! lsmod | grep -q ip_tables || ! lsmod | grep -q iptable_nat; then | |
print_info "WARNING: iptables kernel modules not loaded. Sharing the /home folder with the Windows VM will not work unless connected via RDP. HOW TO FIX: | |
Run the following command: | |
echo -e 'ip_tables\niptable_nat' | sudo tee /etc/modules-load.d/iptables.conf | |
Then reboot your system." | |
fi | |
print_success "iptables modules are loaded" | |
# Check if most important LinOffice files exist | |
print_info "Checking for essential setup files" | |
if [ ! -d "$OEM_DIR" ]; then | |
exit_with_error "OEM files not found | |
Please ensure the config/oem directory exists" | |
fi | |
# Check OEM directory permissions | |
if [ ! -r "$OEM_DIR" ] || [ ! -x "$OEM_DIR" ] || ! find "$OEM_DIR" -type f -readable | head -1 >/dev/null 2>&1; then | |
exit_with_error "Insufficient permissions to access OEM directory: $OEM_DIR | |
HOW TO FIX: | |
1. Check directory permissions: ls -ld $OEM_DIR | |
2. Fix permissions: chmod -R u+rwX $OEM_DIR | |
3. If using SELinux/AppArmor, you may need to adjust security contexts" | |
fi | |
# Check if compose.yaml exists | |
if [ ! -f "$COMPOSE_FILE.default" ]; then | |
exit_with_error "Compose file not found: $COMPOSE_FILE.default | |
Please ensure the file exists in the config directory." | |
fi | |
# Check if LinOffice script exists | |
if [ ! -f "$LINOFFICE_CONF.default" ]; then | |
exit_with_error "LinOffice configuration file not found: $LINOFFICE_CONF.default | |
Please ensure the file exists in the config directory." | |
fi | |
if [ ! -f "$LINOFFICE" ]; then | |
exit_with_error "File not found: $LINOFFICE" | |
fi | |
print_success "Files found." | |
# Make scripts executable | |
print_info "Making scripts executable" | |
if [ ! -f "$LINOFFICE" ]; then | |
exit_with_error "File not found: $LINOFFICE | |
Please ensure the script is in the same directory as this setup script." | |
fi | |
if [ ! -f "$LOCALE_REG_SCRIPT" ]; then | |
exit_with_error "File not found: $LOCALE_REG_SCRIPT | |
Please ensure the config directory and locale_reg.sh script exist." | |
fi | |
if [ ! -f "$LOCALE_LANG_SCRIPT" ]; then | |
exit_with_error "File not found: $LOCALE_LANG_SCRIPT | |
Please ensure the config directory and local_compose.sh script exist." | |
fi | |
chmod +x "$LINOFFICE" || exit_with_error "Failed to make $LINOFFICE executable" | |
chmod +x "$LOCALE_REG_SCRIPT" || exit_with_error "Failed to make $LOCALE_REG_SCRIPT executable" | |
chmod +x "$LOCALE_LANG_SCRIPT" || exit_with_error "Failed to make $LOCALE_LANG_SCRIPT executable" | |
print_success "Made scripts executable" | |
# Check for various potential Podman problems | |
# Check subUID/subGID mappings as some users had problems here | |
print_info "Checking subUID/subGID mappings" | |
if ! grep -q "^$(whoami):" /etc/subuid || ! grep -q "^$(whoami):" /etc/subgid; then | |
exit_with_error "Missing subUID/subGID mappings for the user. | |
HOW TO FIX: | |
1. Run: sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $(whoami) | |
2. Refresh Podman configuration: podman system migrate | |
3. Verify mappings in /etc/subuid and /etc/subgid" | |
fi | |
print_success "subUID/subGID mappings verified." | |
# Check Podman storage configuration and whether overlay storage driver is used, as some users had problems here | |
print_info "Checking Podman storage configuration" | |
driver=$(podman info --format '{{.Store.GraphDriverName}}' 2>/dev/null) | |
error_msg="Podman is not using overlay storage driver or there's a configuration issue. | |
HOW TO FIX: | |
mkdir -p ~/.config/containers | |
cat > ~/.config/containers/storage.conf <<EOF | |
[storage] | |
driver = \"overlay\" | |
runroot = \"/run/user/\$(id -u)/containers\" | |
graphroot = \"\$HOME/.local/share/containers/storage\" | |
EOF | |
podman system migrate | |
Then verify with: | |
podman info --format '{{.Store.GraphDriverName}}' | |
Expected: overlay" | |
if ! echo "$driver" | grep -qE "overlay|btrfs"; then | |
if [ -z "$driver" ]; then | |
print_info "Podman storage driver is not set. Setting up with overlay storage driver" | |
mkdir -p ~/.config/containers | |
cat > ~/.config/containers/storage.conf <<EOF | |
[storage] | |
driver = "overlay" | |
runroot = "/run/user/\$(id -u)/containers" | |
graphroot = "\$HOME/.local/share/containers/storage" | |
EOF | |
# Check running containers before migrate | |
running_containers=$(podman ps --format '{{.Names}}' 2>/dev/null) | |
if [ -n "$running_containers" ] && ! [[ "$running_containers" == "LinOffice" ]]; then | |
# Multiple or non-LinOffice: Prompt user | |
echo "PROMPT:PODMAN_MIGRATE" | |
while true; do | |
read -p "WARNING: podman system migrate will stop ALL running containers and reconfigure storage. This may disrupt other workloads. Continue? (y/n): " -n 1 -r | |
echo | |
if [[ $REPLY =~ ^[Yy]$ ]]; then | |
break | |
elif [[ $REPLY =~ ^[Nn]$ ]] || [ -z "$REPLY" ]; then | |
exit_with_error "Setup aborted by user. Please run the script again when ready to migrate." | |
fi | |
done | |
fi | |
podman system migrate | |
new_driver=$(podman info --format '{{.Store.GraphDriverName}}' 2>/dev/null) | |
if ! echo "$new_driver" | grep -qE "overlay|btrfs"; then | |
exit_with_error "$error_msg" | |
fi | |
else | |
exit_with_error "$error_msg" | |
fi | |
fi | |
# Determine if running rootless or rootful | |
if podman info --format '{{.Host.Security.Rootless}}' | grep -q true; then | |
IS_ROOTLESS=true | |
STORAGE_DIR="$HOME/.local/share/containers/storage" | |
else | |
IS_ROOTLESS=false | |
STORAGE_DIR="/var/lib/containers/storage" | |
fi | |
print_info "Podman running in $( $IS_ROOTLESS && echo 'rootless' || echo 'rootful' ) mode" | |
# Set network directory paths based on rootless status first | |
if [ "$IS_ROOTLESS" = true ]; then | |
NETAVARK_DIR="$HOME/.local/share/containers/networks" | |
CNI_DIR="$HOME/.config/cni/net.d" | |
else | |
NETAVARK_DIR="/var/lib/containers/networks" | |
CNI_DIR="/etc/cni/net.d" | |
fi | |
# Now select the correct directory based on the network backend | |
if [ "$NETWORK_BACKEND" = "netavark" ]; then | |
NETWORK_DIR="$NETAVARK_DIR" | |
else | |
# Default to CNI backend | |
NETWORK_DIR="$CNI_DIR" | |
fi | |
print_info "Podman network directory: $NETWORK_DIR" | |
# Check if storage directory is accessible | |
if [ ! -d "$STORAGE_DIR" ] || [ ! -w "$STORAGE_DIR" ]; then | |
exit_with_error "Podman storage directory inaccessible: $STORAGE_DIR | |
1. Check if directory exists: ls -ld \"$STORAGE_DIR\" | |
2. If it exists, fix permissions: $( $IS_ROOTLESS && echo "chmod -R u+rwX \"$STORAGE_DIR\"" || echo "sudo chmod -R u+rwX \"$STORAGE_DIR\"" ) | |
3. If it does not exist, initialize Podman: podman info" | |
fi | |
print_success "Podman storage directory verified: $STORAGE_DIR" | |
# Check which networking backend is in use | |
print_info "Checking Podman networking is working" | |
NETWORK_BACKEND=$(podman info --format '{{.Host.NetworkBackend}}' 2>/dev/null) | |
if [ -z "$NETWORK_BACKEND" ]; then | |
exit_with_error "Failed to detect Podman's network backend. Make sure Podman is correctly installed and accessible to your user. Run 'podman info' to diagnose." | |
fi | |
print_info "Podman is using network backend: $NETWORK_BACKEND" | |
# Test network creation for all backends | |
TEST_NET_NAME="linoffice_net_test_$(date +%s)" | |
print_info "Testing network creation with backend: $NETWORK_BACKEND" | |
if ! podman network create "$TEST_NET_NAME" >/dev/null 2>&1; then | |
exit_with_error "Failed to create test network '$TEST_NET_NAME'. | |
HOW TO FIX: | |
1. Check Podman logs: journalctl -u podman | |
2. $( $IS_ROOTLESS && echo 'Ensure user has sufficient permissions.' || echo 'Run as root or check sudo permissions.' ) | |
3. Reinstall network backend: | |
- For netavark: $( $IS_ROOTLESS && echo 'podman system reset && podman info' || echo 'sudo dnf reinstall netavark || sudo apt install netavark' ) | |
- For CNI: Ensure CNI plugins are installed (e.g., sudo dnf install containernetworking-plugins) | |
4. Verify SELinux/AppArmor settings if enabled." | |
fi | |
print_success "Test network '$TEST_NET_NAME' created successfully." | |
# Check that network directory exists | |
if [ ! -d "$NETWORK_DIR" ]; then | |
print_info "WARNING: Network directory does not exist: $NETWORK_DIR | |
This might lead to errors." | |
fi | |
if [ ! -w "$NETWORK_DIR" ]; then | |
print_info "WARNING: Network directory not writable: $NETWORK_DIR | |
This might lead to errors." | |
fi | |
# Clean up test network | |
if podman network exists "$TEST_NET_NAME" >/dev/null 2>&1; then | |
podman network rm "$TEST_NET_NAME" >/dev/null 2>&1 || print_info "Note: Failed to remove test network '$TEST_NET_NAME', you may remove it manually." | |
fi | |
print_success "Podman networking check completed." | |
# Test basic container creation to catch storage issues early | |
print_info "Testing basic container functionality..." | |
if ! timeout 60 podman run --rm alpine:latest echo "test" >/dev/null 2>&1; then | |
exit_with_error "Basic container test failed. This could indicate storage driver issues. | |
HOW TO FIX: | |
1. Check Podman logs: journalctl --user -u podman | |
2. Try: podman system reset (WARNING: removes all containers/images) | |
3. Ensure /run/containers and storage directories have correct permissions | |
4. Check if your filesystem supports overlay mounts" | |
fi | |
print_success "Podman test container created and removed successfully." | |
# Check connectivity to microsoft.com | |
print_info "Checking connectivity to Microsoft" | |
if ! curl -s --head --request GET --max-time 10 -L https://www.microsoft.com | grep -q "200"; then | |
# Alternative method: curl to a reliable fallback endpoint | |
if ! curl -s --head --request GET --max-time 10 -L https://www.office.com | grep -q "200"; then | |
exit_with_error "Unable to connect to microsoft.com. | |
HOW TO FIX: | |
1. Check your internet connection | |
2. Verify DNS settings: Ensure you can resolve microsoft.com (try: nslookup microsoft.com) | |
3. Check firewall settings: Ensure outbound connections to microsoft.com are allowed | |
4. Try again or contact your network administrator" | |
else | |
print_success "Successfully connected to Microsoft" | |
fi | |
else | |
print_success "Successfully connected to Microsoft" | |
fi | |
# Run locale scripts | |
print_step "2" "Detecting region and language settings" | |
print_info "Running locale configuration scripts" | |
print_info "Executing: $LOCALE_REG_SCRIPT" | |
if ! "$LOCALE_REG_SCRIPT"; then | |
exit_with_error "Failed to execute $LOCALE_REG_SCRIPT (exit code: $?)" | |
fi | |
print_info "Executing: $LOCALE_LANG_SCRIPT" | |
if ! "$LOCALE_LANG_SCRIPT"; then | |
exit_with_error "Failed to execute $LOCALE_LANG_SCRIPT (exit code: $?)" | |
fi | |
print_success "Locale script executed successfully" | |
# Check if newly created regional.reg exists | |
print_info "Checking for regional_settings.reg file" | |
if [ ! -f "$REGIONAL_REG" ]; then | |
exit_with_error "Required file not found: $REGIONAL_REG | |
Please ensure the config/oem/registry directory exists and contains regional_settings.reg" | |
fi | |
print_success "Found regional_settings.reg file" | |
} | |
function setup_logfile() { | |
# Check if the logfile already exists, if yes rename old one with its last modified date and start with a fresh logfile | |
mkdir -p "$(dirname "$LOGFILE")" | |
echo "Logfile: $LOGFILE" | |
if [ -e "$LOGFILE" ]; then | |
MODIFIED_DATE=$(stat -c %y "$LOGFILE" | sed 's/[: ]/_/g' | cut -d '.' -f 1) | |
mv "$LOGFILE" "${LOGFILE%.log}_$MODIFIED_DATE.log" | |
fi | |
} | |
function create_container() { | |
print_step "3" "Setting up the LinOffice container" | |
local bootcount=0 | |
local required_boots=4 # Accept 4 as the minimum, but allow for 5 if it happens (see comment below) | |
# The Windows/Office install process may show 4 or 5 reboots depending on version and script details. | |
# Sometimes the reboot between install.bat and InstallOffice.ps1 is not a full UEFI reboot, so only 4 are seen. | |
# We proceed if we see at least 4 reboots, and print a note if more are detected. | |
# this is how many times the Windows VM needs to boot to be ready | |
# the string to look for is "BdsDxe: starting Boot0004" | |
# 3 reboots will be logged during initial Windows until you can see the desktop for the first time | |
# 1 reboot at the end of install.bat (this is the one that is not always logged for some reason) | |
# 1 reboot at the end of the InstallOffice.ps1 | |
local result=1 # 0 = success, 1 = failure (assume failure by default) | |
local download_started=false | |
local download_finished=false | |
local install_started=false | |
local timeout_counter=0 | |
local max_timeout=3600 # 60 minutes maximum wait time between podman-compose log output | |
local last_activity_time=$(date +%s) | |
local windows_version="" | |
# Start podman-compose in the background with unbuffered output and strip ANSI codes | |
print_info "Starting podman-compose in detached mode..." | |
# If the compose file doesn't exist yet, initialize it from the default template | |
if [ ! -f "$COMPOSE_FILE" ]; then | |
if [ -f "$COMPOSE_FILE.default" ]; then | |
print_info "Creating $COMPOSE_FILE from default template" | |
cp "$COMPOSE_FILE.default" "$COMPOSE_FILE" || exit_with_error "Failed to copy $COMPOSE_FILE.default to $COMPOSE_FILE" | |
else | |
exit_with_error "Compose file missing: $COMPOSE_FILE and $COMPOSE_FILE.default not found" | |
fi | |
fi | |
if ! $COMPOSE_COMMAND --file "$COMPOSE_FILE" up -d >>"$LOGFILE" 2>&1; then | |
exit_with_error "Failed to start containers. Check $LOGFILE for details." | |
fi | |
# Check if container was actually created | |
sleep 5 | |
if ! podman ps -a --filter "name=$CONTAINER_NAME" --format "{{.Names}}" | grep -q "^$CONTAINER_NAME$"; then | |
exit_with_error "Container $CONTAINER_NAME was not created successfully. Check $LOGFILE for detailed error messages." | |
fi | |
print_info "Tailing logs from container: $CONTAINER_NAME" | |
podman logs -f --timestamps "$CONTAINER_NAME" 2>&1 | \ | |
stdbuf -oL -eL sed -u 's/\x1b\[[0-9;]*m//g' >> "$LOGFILE" & | |
log_pid=$! | |
print_info "Monitoring container setup progress..." | |
# Monitor the logfile for progress | |
while true; do | |
local current_time=$(date +%s) | |
if [ $((current_time - last_activity_time)) -gt $max_timeout ]; then | |
result=1 | |
exit_with_error "Container setup timed out after $((max_timeout/60)) minutes." | |
fi | |
# Read the logfile if it exists | |
if [ -f "$LOGFILE" ]; then | |
# Get file size to detect new activity | |
local current_size=$(stat -c%s "$LOGFILE" 2>/dev/null || echo "0") | |
local previous_size=${previous_size:-0} | |
if [ "$current_size" -gt "$previous_size" ]; then | |
last_activity_time=$current_time | |
previous_size=$current_size | |
fi | |
# Check for download progress | |
if ! $download_started && grep -q "Downloading Windows" "$LOGFILE"; then | |
print_step "4" "Starting Windows download (about 5 GB). This will take a while depending on your Internet speed." | |
download_started=true | |
last_activity_time=$current_time | |
windows_version=$(grep "Downloading Windows" "$LOGFILE" | tail -1 | grep -oE '10|11') | |
fi | |
# Output download progress at each percent | |
if $download_started && ! $download_finished; then | |
# Parse wget progress line: percent and speed | |
last_percent=${last_percent:--1} | |
progress_line=$(grep -E "%" "$LOGFILE" | grep -E "[0-9.]+[MK]" | tail -1) | |
pct=$(echo "$progress_line" | grep -oE "[ ]{1,3}[0-9]{1,3}%" | tail -1 | tr -d ' %') | |
speed=$(echo "$progress_line" | grep -oE "[0-9.]+[MK]" | tail -1) | |
if [[ "$pct" =~ ^[0-9]+$ ]] && [ "$pct" -gt "$last_percent" ] && [ "$pct" -le 100 ]; then | |
if [ -n "$windows_version" ]; then | |
print_progress "Downloading Windows ${windows_version}: ${pct}% | Speed: ${speed}B/s" | |
else | |
print_progress "Downloading Windows: ${pct}% | Speed: ${speed}B/s" | |
fi | |
last_percent=$pct | |
fi | |
fi | |
# Check for download completion | |
if $download_started && ! $download_finished && grep -q "100%" "$LOGFILE"; then | |
print_success "Windows download finished" | |
download_finished=true | |
last_activity_time=$current_time | |
# Monitor for either "Windows started" or "Shutdown completed" after download | |
print_info "Waiting for Windows to start after download..." | |
local monitor_timeout=300 # 5 minutes | |
local monitor_elapsed=0 | |
local monitor_interval=5 | |
while [ $monitor_elapsed -lt $monitor_timeout ]; do | |
if grep -q "Windows started" "$LOGFILE"; then | |
print_step "5" "Installing Windows. This will take a while." | |
install_started=true | |
last_activity_time=$(date +%s) | |
break | |
fi | |
if grep -q "Shutdown completed" "$LOGFILE"; then | |
exit_with_error "Windows installation failed: Detected shutdown before Windows started. Check $LOGFILE for details and see https://github.com/dockur/windows/issues for troubleshooting." | |
fi | |
sleep $monitor_interval | |
monitor_elapsed=$((monitor_elapsed + monitor_interval)) | |
done | |
if [ $monitor_elapsed -ge $monitor_timeout ]; then | |
exit_with_error "Timeout waiting for Windows to start after download. Check $LOGFILE for details." | |
fi | |
fi | |
# Check for error conditions | |
if grep -iq "error\|failed\|cannot\|timeout" "$LOGFILE" | tail -10 | grep -q "FATAL\|ERROR"; then | |
print_error "Error detected in container logs. Check $LOGFILE for details." | |
# Don't exit immediately, but log the concern | |
fi | |
# Check for boot progress | |
local current_boots=0 | |
current_boots=$(grep -c "BdsDxe: starting Boot0004" "$LOGFILE" 2>/dev/null) || current_boots=0 | |
if [ "$current_boots" -gt "$bootcount" ]; then | |
bootcount=$current_boots | |
print_success "Reboot $bootcount of $required_boots completed" | |
if [ "$bootcount" -eq 3 ]; then | |
print_success "Windows installation finished" | |
print_step "6" "Downloading and installing Office (about 3 GB). This will take a while." | |
fi | |
if [ "$bootcount" -gt 4 ]; then | |
print_info "More than 4 reboots detected ($bootcount). This is expected in some cases. Proceeding." | |
fi | |
last_activity_time=$current_time | |
if [ "$bootcount" -ge "$required_boots" ]; then | |
result=0 | |
break | |
fi | |
fi | |
fi | |
# Sleep briefly to avoid high CPU usage | |
sleep 5 | |
done | |
# Stop the background log tailing process | |
if kill -0 "$log_pid" 2>/dev/null; then | |
kill "$log_pid" 2>/dev/null || true | |
fi | |
# Then check success/failure | |
if [ "$result" -eq 0 ]; then | |
sleep 5 | |
if ! podman ps -q --filter "name=$CONTAINER_NAME" | grep -q .; then | |
exit_with_error "Container setup completed but container is not running. Check $LOGFILE for details." | |
else | |
print_success "Container setup completed successfully" | |
return 0 | |
fi | |
else | |
exit_with_error "Container setup failed. Check $LOGFILE for details or visit 127.0.0.1:8006 in your web browser." | |
fi | |
} | |
function verify_container_health() { | |
print_info "Verifying container health..." | |
# Ensure container exists, otherwise create it | |
if ! podman container exists "$CONTAINER_NAME" 2>/dev/null; then | |
print_info "Container does not exist. Creating it now with podman-compose up -d..." | |
if ! $COMPOSE_COMMAND --file "$COMPOSE_FILE" up -d; then | |
print_error "Failed to create container via compose up -d" | |
return 1 | |
fi | |
print_info "Waiting for container to boot..." | |
sleep 20 | |
fi | |
# Check if container is running, otherwise start it | |
if ! podman ps -q --filter "name=$CONTAINER_NAME" | grep -q .; then | |
print_info "Container is not running. Attempting to start it..." | |
if ! $COMPOSE_COMMAND --file "$COMPOSE_FILE" start; then | |
print_error "Failed to start container" | |
print_info "Container may be in an improper state. Try these commands to fix it: | |
1. podman rm -f LinOffice | |
2. $COMPOSE_COMMAND --file config/compose.yaml up -d" | |
return 1 | |
fi | |
print_info "Waiting for container to boot..." | |
sleep 20 | |
fi | |
# Check container logs for any obvious errors | |
local container_logs=$(podman logs --tail 50 "$CONTAINER_NAME" 2>/dev/null || echo "") | |
if echo "$container_logs" | grep -iq "error\|failed\|fatal"; then | |
print_error "Container logs show potential issues" | |
print_info "If the container is in an improper state, try these commands to fix it: | |
1. podman rm -f LinOffice | |
2. $COMPOSE_COMMAND --file config/compose.yaml up -d" | |
return 1 | |
fi | |
return 0 | |
} | |
# Update the lines FREERDP_CONFIG="", RDP_FLAGS="", and XWAYLAND="" in linoffice.conf | |
function update_config_file() { | |
local conf_file="$LINOFFICE_CONF" | |
if [ ! -f "$LINOFFICE_CONF" ]; then | |
exit_with_error "LinOffice configuration file not found: $LINOFFICE_CONF | |
Please ensure the file exists in the config directory." | |
fi | |
print_info "Updating config file" | |
# Update XWAYLAND line | |
sed -i "/^XWAYLAND=/ s/.*/XWAYLAND=\"${FREERDP_XWAYLAND}\"/" "$conf_file" | |
# Handle RDP_FLAGS: get current value, modify based on conditions, then update line | |
local rdp_flags | |
if grep -q "^RDP_FLAGS=" "$conf_file"; then | |
rdp_flags=$(grep "^RDP_FLAGS=" "$conf_file" | cut -d= -f2- | sed 's/^"//; s/"$//') | |
else | |
rdp_flags="" | |
fi | |
# Remove flags if corresponding var is false | |
if [[ "$FREERDP_NSC" != "true" ]]; then | |
rdp_flags=$(echo "$rdp_flags" | sed 's| /gfx:off /nsc||g; s|^/gfx:off /nsc ||; s| /gfx:off /nsc$||') | |
fi | |
if [[ "$FREERDP_NETWORK_LAN" != "true" ]]; then | |
rdp_flags=$(echo "$rdp_flags" | sed 's| /network:lan||g; s|^/network:lan ||; s| /network:lan$||') | |
fi | |
# if [[ "$FREERDP_SEC_RDP" != "true" ]]; then | |
# rdp_flags=$(echo "$rdp_flags" | sed 's| /sec:rdp||g; s|^/sec:rdp ||; s| /sec:rdp$||') | |
# fi | |
# Add flags if corresponding var is true (trim and add if not already present, but per spec, just add) | |
if [[ "$FREERDP_NSC" == "true" ]]; then | |
if ! echo "$rdp_flags" | grep -q "/gfx:off /nsc"; then | |
rdp_flags="$rdp_flags /gfx:off /nsc" | |
fi | |
fi | |
if [[ "$FREERDP_NETWORK_LAN" == "true" ]]; then | |
if ! echo "$rdp_flags" | grep -q "/network:lan"; then | |
rdp_flags="$rdp_flags /network:lan" | |
fi | |
fi | |
# if [[ "$FREERDP_SEC_RDP" == "true" ]]; then | |
# if ! echo "$rdp_flags" | grep -q "/sec:rdp"; then | |
# rdp_flags="$rdp_flags /sec:rdp" | |
# fi | |
# fi | |
# Trim leading/trailing spaces and update the line | |
rdp_flags=$(echo "$rdp_flags" | sed 's|^ ||; s| $||') | |
if [[ -n "$rdp_flags" ]]; then | |
# Escape sed replacement specials in flags | |
local rdp_flags_escaped | |
rdp_flags_escaped=$(printf '%s' "$rdp_flags" | sed 's/[&|]/\\&/g') | |
sed -i "/^RDP_FLAGS=/ s|.*|RDP_FLAGS=\"$rdp_flags_escaped\"|" "$conf_file" | |
else | |
sed -i "/^RDP_FLAGS=/ s|.*|RDP_FLAGS=\"\"|" "$conf_file" | |
fi | |
# Update FREERDP_COMMAND line (escape sed replacement specials) | |
local freerdp_command_escaped | |
freerdp_command_escaped=$(printf '%s' "$FREERDP_COMMAND" | sed 's/[&|]/\\&/g') | |
sed -i "/^FREERDP_COMMAND=/ s|\"[^\"]*\"|\"${freerdp_command_escaped}\"|" "$conf_file" | |
} | |
function check_available() { | |
if [ -z "$FREERDP_COMMAND" ]; then | |
detect_freerdp_command | |
fi | |
print_step "7" "Checking if everything is set up correctly" | |
print_info "Checking if RDP server is available" | |
# Prepare candidate list based on availability | |
local candidates=() | |
if [ "$EXISTS_XFREERDP3" = true ]; then candidates+=("xfreerdp3"); fi | |
if [ "$EXISTS_FLATPAK_FREERDP" = true ]; then candidates+=("flatpak run --command=xfreerdp com.freerdp.FreeRDP"); fi | |
if [ "$EXISTS_XFREERDP" = true ]; then candidates+=("xfreerdp"); fi | |
# Helper to run one attempt and detect success | |
_run_attempt() { | |
local cmd_str="$1"; shift | |
local extra_flags=("$@") | |
local output="" | |
local use_xwayland=false | |
local arg_flags=() | |
for f in "${extra_flags[@]}"; do | |
case "$f" in | |
XWAYLAND) | |
use_xwayland=true | |
;; | |
*) | |
arg_flags+=("$f") | |
;; | |
esac | |
done | |
# Build command arguments | |
local cmd_args=( | |
/cert:ignore | |
/u:MyWindowsUser | |
/p:MyWindowsPassword | |
/v:127.0.0.1 | |
/port:3389 | |
"${arg_flags[@]}" | |
/app:program:cmd.exe,cmd:'/c tsdiscon' | |
) | |
# Execute command based on type | |
if [[ "$cmd_str" == flatpak* ]]; then | |
local full_cmd="$cmd_str" | |
for arg in "${cmd_args[@]}"; do | |
full_cmd="$full_cmd $arg" | |
done | |
print_info "trying command: timeout 30 bash -c '$full_cmd'" | |
if [ "$use_xwayland" = true ]; then | |
output=$(timeout 30 bash -c "WAYLAND_DISPLAY= $full_cmd" 2>&1) | |
else | |
output=$(timeout 30 bash -c "$full_cmd" 2>&1) | |
fi | |
else | |
print_info "trying command: timeout 30 $cmd_str ${cmd_args[*]}" | |
if [ "$use_xwayland" = true ]; then | |
output=$(timeout 30 env WAYLAND_DISPLAY= "$cmd_str" "${cmd_args[@]}" 2>&1) | |
else | |
output=$(timeout 30 "$cmd_str" "${cmd_args[@]}" 2>&1) | |
fi | |
fi | |
# Log everything, print ERROR lines only to terminal | |
echo "$output" >>"$LOGFILE" | |
local error_lines | |
error_lines=$(echo "$output" | grep -F "ERROR" || true) | |
if [ -n "$error_lines" ]; then echo "$error_lines"; fi | |
# Success when user logoff detected | |
if echo "$output" | grep -q "ERRINFO_LOGOFF_BY_USER"; then | |
print_success "RDP server is available (user logoff detected)" | |
print_info "Used command: $cmd_str ${cmd_args[*]}" | |
return 0 | |
fi | |
return 1 | |
} | |
# Helper to run full test sequence | |
_run_test_sequence() { | |
local c | |
# 1) Minimal command with detected FREERDP_COMMAND | |
if [ -n "$FREERDP_COMMAND" ]; then | |
if _run_attempt "$FREERDP_COMMAND"; then | |
return 0 | |
fi | |
fi | |
# 2) Minimal command with all available candidates | |
for c in "${candidates[@]}"; do | |
if _run_attempt "$c"; then | |
FREERDP_COMMAND="$c" | |
update_config_file | |
return 0 | |
fi | |
done | |
# Utility: are we on Wayland? | |
local on_wayland=false | |
if [ "$XDG_SESSION_TYPE" = "wayland" ] || [ -n "$WAYLAND_DISPLAY" ]; then | |
on_wayland=true | |
fi | |
# 3) Xwayland variant (Wayland sessions only) | |
if [ "$on_wayland" = true ]; then | |
for c in "${candidates[@]}"; do | |
if _run_attempt "$c" "XWAYLAND"; then | |
FREERDP_COMMAND="$c" | |
FREERDP_XWAYLAND=true | |
update_config_file | |
return 0 | |
fi | |
done | |
fi | |
# 4) /sec:rdp variant | |
# for c in "${candidates[@]}"; do | |
# if _run_attempt "$c" "/sec:rdp"; then | |
# FREERDP_COMMAND="$c" | |
# update_config_file | |
# return 0 | |
# fi | |
# done | |
# 5) /network:lan variant | |
for c in "${candidates[@]}"; do | |
if _run_attempt "$c" "/network:lan"; then | |
FREERDP_COMMAND="$c" | |
FREERDP_NETWORK_LAN=true | |
update_config_file | |
return 0 | |
fi | |
done | |
# 6) /nsc variant | |
for c in "${candidates[@]}"; do | |
if _run_attempt "$c" "/gfx:off /nsc"; then | |
FREERDP_COMMAND="$c" | |
FREERDP_NSC=true | |
update_config_file | |
return 0 | |
fi | |
done | |
# 7) All together (xwayland, /sec:rdp, /network:lan, /gfx:off /nsc) | |
if [ "$on_wayland" = true ]; then | |
for c in "${candidates[@]}"; do | |
if _run_attempt "$c" "XWAYLAND" "/network:lan" "/nsc"; then | |
FREERDP_COMMAND="$c" | |
FREERDP_XWAYLAND=true | |
FREERDP_NETWORK_LAN=true | |
FREERDP_NSC=true | |
update_config_file | |
return 0 | |
fi | |
done | |
else | |
for c in "${candidates[@]}"; do | |
if _run_attempt "$c" "/network:lan" "/nsc"; then | |
FREERDP_COMMAND="$c" | |
FREERDP_NETWORK_LAN=true | |
FREERDP_NSC=true | |
update_config_file | |
return 0 | |
fi | |
done | |
fi | |
return 1 | |
} | |
# Ensure container is up before testing | |
if ! verify_container_health; then | |
print_error "Container is not healthy; cannot perform RDP checks." | |
return 1 | |
fi | |
# Run initial test sequence | |
if _run_test_sequence; then | |
update_config_file | |
return 0 | |
fi | |
# 8) Reboot container and retry | |
print_info "Rebooting Windows VM container and retrying checks..." | |
"$COMPOSE_COMMAND" --file "$COMPOSE_FILE" restart >>"$LOGFILE" 2>&1 || true | |
sleep 10 | |
if ! verify_container_health; then | |
print_error "Problem with container after reboot." | |
else | |
if _run_test_sequence; then | |
update_config_file | |
return 0 | |
fi | |
fi | |
# 9) Prompt user to log out via VNC and retry once | |
print_error "Unable to connect via RDP automatically." | |
echo | |
print_info "What to do now:" | |
print_info "1) Open your browser to 127.0.0.1:8006" | |
print_info "2) Login with password: MyWindowsPassword" | |
print_info "3) In Windows, sign out the user (do not shutdown)." | |
echo | |
echo "PROMPT:VNC_SIGN_OUT_AND_RETRY" | |
local answer | |
read -r -p "Try again now? [Y/n]: " answer | |
answer=${answer:-Y} | |
if [[ "$answer" =~ ^[Yy]$ ]]; then | |
if _run_test_sequence; then | |
update_config_file | |
return 0 | |
fi | |
fi | |
return 1 | |
} | |
function check_success() { | |
if [ -z "$FREERDP_COMMAND" ]; then | |
detect_freerdp_command | |
fi | |
print_info "Checking if Office is installed" | |
local freerdp_pid="" | |
local elapsed_time=0 | |
local retry_count=0 | |
local max_retries=10 | |
local check_interval=10 # Try again after 10 seconds | |
local installation_timeout=900 # 15 minutes timeout for Office download and installation | |
# Function to cleanup FreeRDP process | |
cleanup_freerdp() { | |
if [ -n "$freerdp_pid" ] && kill -0 "$freerdp_pid" 2>/dev/null; then | |
print_info "Cleaning up FreeRDP process (PID: $freerdp_pid)" | |
kill -TERM "$freerdp_pid" 2>/dev/null || true | |
sleep 3 | |
kill -KILL "$freerdp_pid" 2>/dev/null || true | |
fi | |
} | |
# Register cleanup function to run on script exit | |
trap cleanup_freerdp EXIT | |
# Clear any existing success file once before attempting connections | |
rm -f "$SUCCESS_FILE" | |
# Build command arguments based on successful availability check | |
local cmd_args=( | |
/cert:ignore | |
+home-drive | |
/u:MyWindowsUser | |
/p:MyWindowsPassword | |
/v:127.0.0.1 | |
/port:3389 | |
) | |
# Add flags from successful connection test | |
# if [ "$FREERDP_SEC_RDP" = true ]; then | |
# cmd_args+=("/sec:rdp") | |
# fi | |
if [ "$FREERDP_NETWORK_LAN" = true ]; then | |
cmd_args+=("/network:lan") | |
fi | |
cmd_args+=(/app:program:powershell.exe,cmd:'-ExecutionPolicy Bypass -File C:\\OEM\\FirstRDPRun.ps1') | |
# Retry loop for FreeRDP connection | |
while [ $retry_count -lt $max_retries ]; do | |
retry_count=$((retry_count + 1)) | |
print_info "Starting FreeRDP connection to mount home directory (Attempt $retry_count of $max_retries)..." | |
# Start FreeRDP in the background with home-drive enabled | |
if [[ "$FREERDP_COMMAND" == flatpak* ]]; then | |
local full_cmd="$FREERDP_COMMAND" | |
for arg in "${cmd_args[@]}"; do | |
full_cmd="$full_cmd $arg" | |
done | |
if [ "$FREERDP_XWAYLAND" = true ]; then | |
bash -c "WAYLAND_DISPLAY= $full_cmd" >>"$LOGFILE" 2>&1 & | |
else | |
bash -c "$full_cmd" >>"$LOGFILE" 2>&1 & | |
fi | |
else | |
if [ "$FREERDP_XWAYLAND" = true ]; then | |
env WAYLAND_DISPLAY= "$FREERDP_COMMAND" "${cmd_args[@]}" >>"$LOGFILE" 2>&1 & | |
else | |
"$FREERDP_COMMAND" "${cmd_args[@]}" >>"$LOGFILE" 2>&1 & | |
fi | |
fi | |
freerdp_pid=$! | |
# Wait briefly and check if FreeRDP started successfully | |
sleep 5 | |
if kill -0 "$freerdp_pid" 2>/dev/null; then | |
print_success "FreeRDP connection established successfully (PID: $freerdp_pid)" | |
break | |
else | |
wait $freerdp_pid 2>/dev/null | |
local exit_code=$? | |
print_error "FreeRDP failed to start or exited immediately (exit code: $exit_code)" | |
if [ $retry_count -lt $max_retries ]; then | |
print_info "Retrying in 10 seconds..." | |
sleep 10 | |
else | |
print_error "Max retries ($max_retries) reached. Check log file at $LOGFILE for details." | |
return 1 | |
fi | |
fi | |
done | |
# Reset elapsed time for installation monitoring | |
elapsed_time=0 | |
print_info "Waiting for Office installation to complete (timeout: $((installation_timeout/60)) minutes)..." | |
# Monitor for success file creation | |
while [ $elapsed_time -lt $installation_timeout ]; do | |
# Check if success file exists | |
if [ -f "$SUCCESS_FILE" ]; then | |
print_success "Success file detected - Office installation is complete!" | |
cleanup_freerdp | |
return 0 | |
fi | |
# Check if FreeRDP process is still running | |
if ! kill -0 "$freerdp_pid" 2>/dev/null; then | |
wait $freerdp_pid 2>/dev/null | |
local exit_code=$? | |
# Check if success file was created before process ended | |
if [ -f "$SUCCESS_FILE" ]; then | |
print_success "Success file detected - Office installation is complete!" | |
return 0 | |
fi | |
print_error "FreeRDP connection terminated (exit code: $exit_code)" | |
print_info "Checking if success file was created..." | |
sleep 2 | |
if [ -f "$SUCCESS_FILE" ]; then | |
print_success "Success file found - Office installation completed successfully!" | |
return 0 | |
else | |
print_error "Success file not found. Installation may have failed." | |
print_info "Check log file at $LOGFILE for details" | |
return 1 | |
fi | |
fi | |
sleep $check_interval | |
elapsed_time=$((elapsed_time + check_interval)) | |
done | |
# Timeout reached | |
print_error "Timeout waiting for Office installation to complete after $((installation_timeout / 60)) minutes" | |
print_info "Check log file at $LOGFILE for details" | |
# Final check for success file | |
if [ -f "$SUCCESS_FILE" ]; then | |
print_success "Success file found during cleanup - Office installation completed!" | |
cleanup_freerdp | |
return 0 | |
fi | |
cleanup_freerdp | |
return 1 | |
} | |
function desktop_files() { | |
print_step "8" "Installing .desktop files (app launchers)" | |
# Check if required directories exist | |
if [ ! -d "$DESKTOP_DIR" ]; then | |
exit_with_error "Error: Desktop directory not found: $DESKTOP_DIR" | |
fi | |
if [ ! -d "$USER_APPLICATIONS_DIR" ]; then | |
mkdir -p "$USER_APPLICATIONS_DIR" || exit_with_error "Failed to create $USER_APPLICATIONS_DIR" | |
fi | |
if [ ! -w "$USER_APPLICATIONS_DIR" ]; then | |
exit_with_error "No write permissions for $USER_APPLICATIONS_DIR" | |
fi | |
# List of Office apps | |
local apps=("excel" "word" "powerpoint" "onenote" "outlook" "linoffice") | |
local INSTALLED_COUNT=0 | |
print_info "Processing .desktop files..." | |
echo "Number of apps found: ${#apps[@]}" | |
echo "Apps are: ${apps[*]}" | |
for app in "${apps[@]}"; do | |
echo "Starting to process app: $app" | |
local desktop_file="$DESKTOP_DIR/$app.desktop" | |
echo "Processing: $app.desktop" | |
# Check if source file exists | |
if [ ! -f "$desktop_file" ]; then | |
echo " Error: $app.desktop not found" | |
continue | |
fi | |
# Create corrected .desktop file with absolute paths | |
temp_file=$(mktemp) || { | |
echo " Error: Failed to create temporary file" | |
continue | |
} | |
# Replace /PATH/ with LINOFFICE_DIR and write to temp file | |
if ! sed "s|/PATH/|$LINOFFICE_DIR/|g" "$desktop_file" > "$temp_file"; then | |
echo " Error: sed command failed" | |
rm -f "$temp_file" | |
continue | |
fi | |
# Copy to user applications directory | |
if ! cp "$temp_file" "${USER_APPLICATIONS_DIR}/$app.desktop"; then | |
echo " Error: Failed to copy to applications directory" | |
rm -f "$temp_file" | |
continue | |
fi | |
# Make it executable | |
if ! chmod +x "${USER_APPLICATIONS_DIR}/$app.desktop"; then | |
echo " Error: Failed to make executable" | |
rm -f "$temp_file" | |
continue | |
fi | |
# Clean up temp file | |
rm -f "$temp_file" | |
echo " Installed: ${USER_APPLICATIONS_DIR}/$app.desktop" | |
((INSTALLED_COUNT++)) | |
echo "Debug: Finished processing $app" | |
done | |
print_info "App launchers installed: $INSTALLED_COUNT" | |
if [ $INSTALLED_COUNT -gt 0 ]; then | |
print_info "Updating desktop database" | |
if command -v update-desktop-database >/dev/null 2>&1; then | |
update-desktop-database "$USER_APPLICATIONS_DIR" 2>/dev/null || true | |
print_success "Desktop database updated" | |
else | |
print_info "Note: update-desktop-database not found, skipping database update" | |
fi | |
print_success "Installation complete! The applications should now appear in your application menu." | |
print_info "Installed applications:" | |
for app in "${apps[@]}"; do | |
if [ -f "${USER_APPLICATIONS_DIR}/$app.desktop" ]; then | |
display_name=$(grep "^Name=" "$DESKTOP_DIR/$app.desktop" | cut -d'=' -f2) | |
echo " - $display_name" | |
fi | |
done | |
print_info "To uninstall, remove the files from: $USER_APPLICATIONS_DIR" | |
print_info "To recreate them, run the script with the --desktop flag." | |
else | |
print_error "No files were installed." | |
return 1 | |
fi | |
} | |
# Function to run InstallOffice.ps1 via FreeRDP | |
try_install_office() { | |
if [ -z "$FREERDP_COMMAND" ]; then | |
detect_freerdp_command | |
fi | |
print_info "Running Office installation script via FreeRDP..." | |
# Connection timeout in milliseconds (e.g. 10000 = 10 seconds) | |
local connection_timeout=10000 | |
# Build command arguments | |
local cmd_args=( | |
/cert:ignore | |
+home-drive | |
/u:MyWindowsUser | |
/p:MyWindowsPassword | |
/v:127.0.0.1 | |
/port:3389 | |
/timeout:$connection_timeout | |
/app:program:powershell.exe,cmd:'-ExecutionPolicy Bypass -File C:\\OEM\\InstallOffice.ps1' | |
) | |
# Add flags from successful connection test | |
if [ "$FREERDP_NETWORK_LAN" = true ]; then | |
cmd_args+=("/network:lan") | |
fi | |
# Run FreeRDP (handle flatpak vs system binary, Xwayland if needed) | |
if [[ "$FREERDP_COMMAND" == flatpak* ]]; then | |
local full_cmd=("$FREERDP_COMMAND" "${cmd_args[@]}") | |
if [ "$FREERDP_XWAYLAND" = true ]; then | |
print_info "Running via Flatpak with Xwayland disabled (forcing X11 fallback)" | |
bash -c "WAYLAND_DISPLAY= ${full_cmd[*]}" >>"$LOGFILE" 2>&1 | |
else | |
bash -c "${full_cmd[*]}" >>"$LOGFILE" 2>&1 | |
fi | |
else | |
if [ "$FREERDP_XWAYLAND" = true ]; then | |
print_info "Running system FreeRDP with Xwayland disabled (forcing X11 fallback)" | |
env WAYLAND_DISPLAY= "$FREERDP_COMMAND" "${cmd_args[@]}" >>"$LOGFILE" 2>&1 | |
else | |
"$FREERDP_COMMAND" "${cmd_args[@]}" >>"$LOGFILE" 2>&1 | |
fi | |
fi | |
local exit_code=$? | |
if [ $exit_code -eq 0 ]; then | |
print_success "Office installation script executed successfully via FreeRDP." | |
return 0 | |
else | |
print_error "FreeRDP failed to run Office installation script (exit code: $exit_code)" | |
return 1 | |
fi | |
} | |
# Main logic | |
# If --healthcheck flag is set, only run these tests without writing to progress file | |
if [ "$HEALTHCHECK" = true ]; then | |
check_requirements | |
check_linoffice_container | |
verify_container_health | |
if check_available; then | |
print_success "Everything seems fine" | |
else | |
print_error "Unable to connect via RDP" | |
fi | |
exit 0 | |
fi | |
init_progress_file | |
# If --installoffice flag is set, only run InstallOffice.ps1 via FreeRDP | |
if [ "$INSTALL_OFFICE_ONLY" = true ]; then | |
print_info "Running InstallOffice.ps1 via FreeRDP (--installoffice mode)..." | |
if try_install_office; then | |
print_success "Office installation script executed successfully!" | |
else | |
exit_with_error "Failed to run Office installation script via FreeRDP." | |
fi | |
exit 0 | |
fi | |
# If --desktop flag is set, only run desktop_files | |
if [ "$DESKTOP_ONLY" = true ]; then | |
print_info "Recreating desktop files..." | |
if desktop_files; then | |
mark_progress "$PROGRESS_DESKTOP" | |
print_success "App launchers (.desktop files) created successfully!" | |
else | |
exit_with_error "Failed to create app launchers (.desktop files)" | |
fi | |
exit 0 | |
fi | |
# If --firstrun is set, remove the office_installed progress marker | |
if [ "$FIRSTRUN" = true ]; then | |
print_info "--firstrun specified: Forcing RDP and Office install checks." | |
if [ -f "$PROGRESS_FILE" ]; then | |
sed -i "/$PROGRESS_OFFICE/d" "$PROGRESS_FILE" | |
fi | |
fi | |
# Check requirements if not already completed | |
if ! check_progress "$PROGRESS_REQUIREMENTS"; then | |
if check_requirements; then | |
mark_progress "$PROGRESS_REQUIREMENTS" | |
else | |
echo "Requirements check failed. Cannot proceed with container setup." | |
exit 1 | |
fi | |
else | |
print_info "Requirements check already completed, skipping..." | |
fi | |
# Check container status and create if needed | |
check_linoffice_container | |
if [ "$CONTAINER_EXISTS" -eq 0 ] && ! check_progress "$PROGRESS_CONTAINER"; then | |
print_info "Container does not exist, proceeding with setup and creation." | |
setup_logfile | |
if create_container; then | |
mark_progress "$PROGRESS_CONTAINER" | |
else | |
exit_with_error "Container creation failed" | |
fi | |
else | |
if check_progress "$PROGRESS_CONTAINER"; then | |
print_info "Container already created, skipping creation step." | |
else | |
print_info "Skipping container creation as LinOffice container already exists." | |
fi | |
fi | |
# Wait for RDP and check Office installation if not already completed or if --firstrun is set | |
if ! check_progress "$PROGRESS_OFFICE" || [ "$FIRSTRUN" = true ]; then | |
# If --firstrun, ensure the container is running before checking RDP | |
if [ "$FIRSTRUN" = true ]; then | |
if ! podman ps -q --filter "name=$CONTAINER_NAME" | grep -q .; then | |
print_info "Container is not running. Starting LinOffice container for --firstrun..." | |
if ! $COMPOSE_COMMAND --file "$COMPOSE_FILE" start; then | |
exit_with_error "Failed to start LinOffice container for --firstrun." | |
fi | |
print_info "Waiting 20 seconds for container to boot..." | |
sleep 20 | |
fi | |
fi | |
if ! check_available; then | |
exit_with_error "Failed to connect to RDP server" | |
fi | |
if ! check_success; then | |
exit_with_error "Office installation failed or timed out" | |
fi | |
mark_progress "$PROGRESS_OFFICE" | |
else | |
print_info "Office installation already completed, skipping..." | |
fi | |
# Install desktop files if not already completed | |
if ! check_progress "$PROGRESS_DESKTOP"; then | |
if desktop_files; then | |
mark_progress "$PROGRESS_DESKTOP" | |
else | |
exit_with_error "Failed to install desktop files" | |
fi | |
else | |
print_info "Desktop files already installed, skipping this step. To recreate them, run the script with the --desktop flag." | |
fi | |
# Clean up success file | |
rm -f "$SUCCESS_FILE" | |
print_success "LinOffice setup completed successfully!" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment