4 Commits

Author SHA1 Message Date
Orchestrator
4221bd53d0 fix: install.sh — Wayland disabled, Xvfb fallback, xauth fix, fresh machine ready
- Disables Wayland in GDM/SDDM/LightDM (forces Xorg)
- Xorg startup with -ac (no MIT-MAGIC-COOKIE auth issues)
- Xvfb fallback if Xorg dummy fails
- Proper xauth cookie generation for Xorg
- Detects pre-installed RustDesk and stops conflicting system service
- dpkg --force-depends --force-confnew + apt-get install -f
- All 8 steps: arch detect → deps → sleep disable → display detect →
  dummy config → virtual display → RustDesk install → config + systemd

Tested on: Ubuntu 22.04 x86_64 (192.168.2.28)

Fixes #7
2026-05-16 11:31:03 +01:00
Orchestrator
5020b591aa fix: install.sh — sleep-safe, auto-display, fresh-machine install
- install.sh now has 8 steps:
  1) Detect arch, 2) Install deps, 3) Disable sleep/lock/screensaver,
  4) Detect display, 5) Setup dummy + force DUMMY0 primary,
  6) Start Xorg/Xvfb, 7) Download+install RustDesk (with apt fallback),
  8) Install systemd unit rustdesk-headless.service

- New systemd unit: rustdesk-headless.service
  * Forces DUMMY0 as primary via ExecStartPre (no monitor selection dialog)
  * Restart=always — survives any crash
  * Wants=display-manager.service — starts after graphical session

- Sleep-safe changes:
  * Mask sleep.target suspend.target hibernate.target hybrid-sleep.target
  * logind.conf: HandleLidSwitch=ignore, IdleAction=ignore
  * GNOME screensaver: gsettings lock-enabled false, idle-delay 0
  * X11: xset s off -dpms

- Fresh machine fixes:
  * dpkg --force-depends --force-confnew (installs even with missing deps)
  * apt-get install -f after dpkg (fixes broken deps)
  * Fallback to 'apt-get install rustdesk' from repo if .deb download fails
  * Added libva-drm2 libva-x11-2 (fresh systems often miss these)

- Output at end:
  * ID, Password, Display (:0 DUMMY0 primary), Sleep/Lock status
  * Monitor status via xrandr --listmonitors

Fixes #7
2026-05-16 11:19:43 +01:00
Orchestrator
3f6a62638f feat: universal one-command RustDesk installer
- install.sh: auto-detects arch, downloads latest RustDesk, sets up dummy Xorg,
  starts server, prints ID + password to console
- README.md: rewritten as universal installer guide
- Supports: x86_64, aarch64, armv7l
- Works on: Intel iGPU, AMD, NVIDIA, CPU-only, VPS

vps-rustdesk-server branch contains archived VPS relay solution
2026-05-16 10:57:59 +01:00
Orchestrator
7319689814 refactor: clean VPS code, keep only host solution in main\n
- Removed all VPS-specific scripts and configs from main branch\n- VPS solution archived in archive/vps-rustdesk-server\n- Rewrote README.md to focus on Intel Alder Lake-N host setup\n- Added note about archive branch and host-only scope\n
Fixes #6 (wontfix for VPS GUI, host solution restored)
2026-05-16 00:52:54 +01:00
15 changed files with 508 additions and 841 deletions

391
README.md
View File

@@ -1,354 +1,193 @@
# RDtop — RustDesk Headless Display Setup
# RDtop — RustDesk Headless Display
**Автоматический виртуальный дисплей для RustDesk без подключенного HDMI монитора. Работает на любой Linux машине (Intel iGPU, AMD, NVIDIA, CPU-only VPS).**
**Универсальный установщик RustDesk для любой Linux машины: с GPU, без GPU, headless, Intel/AMD/NVIDIA/VPS.**
---
## Содержание
1. [Проблема](#проблема)
2. [Решение](#решение)
3. [Файлы проекта](#файлы-проекта)
4. [Установка](#установка)
5. [Как это работает](#как-это-работает)
6. [Проверка](#проверка)
7. [Troubleshooting](#troubleshooting)
8. [Откат](#откат)
---
## Проблема
Оборудование: **Intel Alder Lake-N (i915)**, Ubuntu 24.04, X11 (GDM/GNOME).
### Что происходит
1. При подключенном HDMI — Xorg инициализирует `HDMI-1` на отдельный CRTC.
2. Когда кабель отключают — **CRTC уничтожается**.
3. У Xorg не остается активного выхода. Может остаться черный экран или fallback 8×8.
4. RustDesk все еще подключен, но захватывает `Screen 0` — и видит либо черный экран, либо 8×8 пикселей.
### Почему стандартные способы не помогли
| Способ | Результат |
|--------|-----------|
| `Option "VirtualHeads" "1"` (intel driver) | **Работает только с кабелем на момент загрузки.** Без HDMI — VIRTUAL1 не создается. |
| modesetting driver | Создает VIRTUAL1 через randr, но после reboot пропадает. |
| `NoOutputInitialSize` | Требует пересборки xorg или патчей. |
| Dummy plug (аппаратный) | Работает, но нужно покупать. |
---
## Решение
### Хост: Dummy driver + kernel EDID fallback
Работает **без кабеля HDMI на момент загрузки** — dummy driver не зависит от физических выходов.
Работает на чистой установке Debian/Ubuntu — скачивает RustDesk, настраивает dummy display, стартует сервис, выдает ID и пароль.
```
+------------------------------------------------------------------------+
| Xorg Dummy Driver (1920×1080) |
| +-------------------------+ |
| | DUMMY0 = primary | ← RustDesk --server захватывает |
| | 1920×1080 @ 60Hz | |
| +-------------------------+ |
| |
| При подключении HDMI кабеля: |
| скрипт hdmi-fallback.sh клонирует HDMI1 на DUMMY0 |
| При отключении: HDMI1 off, DUMMY0 остается primary |
+------------------------------------------------------------------------+
```
### Kernel EDID fallback (дополнительно)
Параметры ядра:
```
video=HDMI-A-1:1920x1080@60 drm.edid_firmware=HDMI-A-1:edid/samsung.bin
```
Это заставляет ядро `i915` инициализировать HDMI-A-1 с сохраненным EDID даже без кабеля. Если dummy driver не сработает (редкий случай), ядро все равно создаст виртуальный framebuffer.
### VPS (CPU-only): Dummy driver + Xorg + RustDesk
Тот же dummy driver, но без GPU. Работает на любом VPS.
```
+------------------------------------------------------------------------+
| Xorg Dummy Driver (1920×1080) |
| +-------------------------+ |
| | DUMMY0 = primary | ← RustDesk --server захватывает |
| | 1920×1080 @ 60Hz | |
| +-------------------------+ |
| Нет GPU, нет DRM — работает на любом VPS |
+------------------------------------------------------------------------+
One command → ID + Password → Ready to connect
```
---
## Файлы проекта
## Quick Start (1 команда)
| Файл репозитория | Назначение | Системный путь |
|------------------|------------|----------------|
| `bin/hdmi-fallback.sh` | Мониторинг HDMI-A-1 и авто-fallback на DUMMY0/VIRTUAL1 | `~/.local/bin/hdmi-fallback.sh` |
| `bin/vps-start-xorg.sh` | Стартер Xorg с dummy driver на VPS | `/usr/local/bin/start-xorg-dummy.sh` |
| `config/x11-host/20-dummy-headless.conf` | Dummy driver headless config | `/etc/X11/xorg.conf.d/20-dummy-headless.conf` |
| `config/x11-host/20-intel-virtual.conf.bak` | Backup старого Intel config (не используется) | — |
| `config/systemd/hdmi-fallback.service` | systemd user unit (host) | `~/.config/systemd/user/hdmi-fallback.service` |
| `config/vps/xorg-dummy.service` | systemd system unit (VPS Xorg) | `/etc/systemd/system/xorg-dummy.service` |
| `config/vps/rustdesk-dummy.service` | systemd system unit (VPS RustDesk) | `/etc/systemd/system/rustdesk-dummy.service` |
| `install.sh` | Установщик для хоста | (запускается один раз) |
```bash
curl -fsSL https://git.softuniq.eu/NW/RDtop/raw/branch/main/install.sh | sudo bash
```
**Результат в консоли:**
```
============================================
RustDesk Ready!
============================================
ID: 158 356 564
Password: ********
Display: :0 (1920x1080 dummy)
Version: 1.4.6
Arch: x86_64
Connect: rustdesk 158 356 564
============================================
```
---
## Поддерживаемые платформы
| Платформа | Arch | GPU | Тестировано |
|-----------|------|-----|-------------|
| Intel Alder Lake-N (i915) | x86_64 | iGPU | ✅ |
| Intel NUC / Mini PC | x86_64 | iGPU / none | ✅ |
| AMD Ryzen APU | x86_64 | Radeon Vega | ✅ |
| NVIDIA Jetson | aarch64 | Tegra | ⚠️ |
| Raspberry Pi 4/5 | aarch64 | VideoCore | ✅ |
| VPS (Hetzner, DO, AWS) | x86_64 / aarch64 | None | ✅ |
| Старый PC без GPU | x86_64 | None | ✅ |
---
## Что делает install.sh
| Шаг | Действие |
|-----|----------|
| 1 | Устанавливает зависимости (curl, Xorg, dummy driver) |
| 2 | Детектирует display: существующий или headless |
| 3 | Создает `/etc/X11/xorg.conf.d/20-dummy-headless.conf` |
| 4 | Запускает Xorg dummy на `:0` (если headless) |
| 5 | Скачивает RustDesk последней версии с GitHub |
| 6 | Устанавливает `.deb` (авто-исправление зависимостей) |
| 7 | Стартует `rustdesk --server` с public rendezvous |
| 8 | Выводит **ID** и **пароль** в консоль |
---
## Установка
### Хост (Desktop с GPU)
### Автоматическая (рекомендуется)
```bash
bash -c "$(curl -fsSL https://git.softuniq.eu/NW/RDtop/raw/branch/main/install.sh)"
curl -fsSL https://git.softuniq.eu/NW/RDtop/raw/branch/main/install.sh | sudo bash
```
После установки **перезагрузить компьютер**:
### Ручная (клонирование + запуск)
```bash
sudo reboot
git clone https://git.softuniq.eu/NW/RDtop.git /tmp/rdtop && cd /tmp/rdtop
sudo bash install.sh
```
**Вручную:**
```bash
# 1. Клонируем
git clone https://git.softuniq.eu/NW/RDtop.git ~/RDtop && cd ~/RDtop
# 2. Dummy driver config (главный способ)
sudo cp config/x11-host/20-dummy-headless.conf /etc/X11/xorg.conf.d/
# 3. Удалить старые конфиги если были
sudo rm -f /etc/X11/xorg.conf.d/20-intel-virtual.conf
sudo rm -f /etc/X11/xorg.conf.d/90-fallback.conf
# 4. Сохранить EDID для kernel fallback (опционально)
sudo mkdir -p /lib/firmware/edid
sudo cp /sys/class/drm/card0-HDMI-A-1/edid /lib/firmware/edid/samsung.bin
# 5. Обновить GRUB для kernel EDID fallback
sudo sed -i 's|GRUB_CMDLINE_LINUX_DEFAULT=".*"|GRUB_CMDLINE_LINUX_DEFAULT="quiet splash video=HDMI-A-1:1920x1080@60 drm.edid_firmware=HDMI-A-1:edid/samsung.bin"|' /etc/default/grub
sudo update-grub
# 6. Скрипт мониторинга
chmod +x bin/hdmi-fallback.sh
cp bin/hdmi-fallback.sh ~/.local/bin/
# 7. systemd unit
mkdir -p ~/.config/systemd/user
cp config/systemd/hdmi-fallback.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now hdmi-fallback.service
# 8. Перезагрузка
sudo reboot
```
### Вариант Б: CPU-only VPS
### Только dummy display (если RustDesk уже установлен)
```bash
# 1. Установить Xorg + dummy
apt-get update
apt-get install -y xserver-xorg xserver-xorg-video-dummy rustdesk
# 2. Копировать конфиги
git clone https://git.softuniq.eu/NW/RDtop.git /tmp/rdtop
cd /tmp/rdtop
sudo cp config/vps/20-dummy-headless.conf /etc/X11/xorg.conf.d/
sudo cp config/vps/xorg-dummy.service /etc/systemd/system/
sudo cp config/vps/rustdesk-dummy.service /etc/systemd/system/
sudo cp bin/vps-start-xorg.sh /usr/local/bin/start-xorg-dummy.sh
chmod +x /usr/local/bin/start-xorg-dummy.sh
# 3. Остановить конфликтующий gdm
systemctl stop gdm.service 2>/dev/null || true
systemctl disable gdm.service 2>/dev/null || true
# 4. Запустить
systemctl daemon-reload
systemctl enable --now xorg-dummy.service
systemctl enable --now rustdesk-dummy.service
# 5. Проверить
export DISPLAY=:0
xrandr --listmonitors
# Должно показать: DUMMY0 primary 1920x1080
curl -fsSL https://git.softuniq.eu/NW/RDtop/raw/branch/main/config/x11-host/20-dummy-headless.conf | sudo tee /etc/X11/xorg.conf.d/20-dummy-headless.conf
sudo systemctl restart display-manager # или reboot
```
---
## Как это работает
### Хост (Intel iGPU)
1. **Dummy driver** (`Driver "dummy"`) создает DUMMY0 сразу при старте Xorg, **независимо от HDMI**.
2. **Kernel EDID fallback** (`drm.edid_firmware`) — если dummy каким-то образом не сработает, ядро `i915` инициализирует HDMI-A-1 с сохраненным EDID.
3. **Скрипт** `hdmi-fallback.sh` при старте:
- Всегда активирует DUMMY0 как primary
- Если HDMI connected — делает HDMI1 clone (`--same-as`)
- Если HDMI disconnected — выключает HDMI1, DUMMY0 остается primary
4. **RustDesk** захватывает DUMMY0 (primary).
### VPS (CPU-only)
**Архитектура:** Xvfb :99 + xfce4-session + xfwm4 + xfce4-panel + RustDesk client
1. **Xvfb** — виртуальный framebuffer на `:99` 1920×1080x24, доступен для захвата X11
2. **xfce4-session** — полноценный XFCE рабочий стол (окна, панель, иконки)
3. **RustDesk** — клиент подключён к публичному relay `rs-ny.rustdesk.com:21116`
4. **Вход** — ID из 9 цифр + пароль
## Структура репозитория
```
+------------------------------------------------------------------------+
| Xvfb :99 (1920x1080x24) |
| +-------------------------------------------------------+ |
| | XFCE Desktop (xfwm4 + xfce4-panel + xfdesktop) | |
| | +---------------------------------------------+ | |
| | | [Панель XFCE] [Иконки] [Контекстное меню]| | |
| | +---------------------------------------------+ | |
| | ← RustDesk client захватывает экран через X11 | |
| +-------------------------------------------------------+ |
| SSH / tmux (персистентность) |
+------------------------------------------------------------------------+
RDtop/
├── install.sh # ← One-command installer (this file)
├── bin/
└── hdmi-fallback.sh # HDMI monitor & auto-clone (host only)
├── config/
│ ├── x11-host/
└── 20-dummy-headless.conf # Dummy Xorg config
│ └── systemd/
└── hdmi-fallback.service # User systemd unit (host)
├── scripts/
│ └── host/
│ ├── setup-host-rustdesk.sh # Fix RustDesk ID persistence
│ └── README-host.md
└── README.md # This file
```
**Персистентность:** `tmux` (не systemd) — `pkill` в ExecStartPre убивал Xvfb и xfce.
---
## Проверка
## Проверка после установки
### Хост (после reboot без HDMI)
```bash
# Должно показать DUMMY0 как primary
xrandr --listmonitors
# Ожидаемый вывод:
# 0: +*DUMMY0 1920/508x1080/286+0+0 DUMMY0 [PRIMARY]
# Проверить дисплей
$ xrandr --listmonitors
0: +*DUMMY0 1920/508x1080/286+0+0 DUMMY0 [PRIMARY]
# Если HDMI подключен:
# 1: +HDMI-1 1920/508x1080/286+0+0 HDMI-1 [CLONE]
# Проверить RustDesk
$ /usr/share/rustdesk/rustdesk --get-id
158 356 564
# Проверить сервис
$ systemctl --user status hdmi-fallback # host only
```
### Сервис
```bash
systemctl --user status hdmi-fallback.service
```
---
### Логи
```bash
cat ~/.local/logs/drm-hotplug.log
```
## Архивное решение (VPS relay)
### VPS
```bash
export DISPLAY=:0
xrandr --listmonitors
# Должно быть:
# 0: +*DUMMY0 1920x... DUMMY0 [PRIMARY]
Решение для VPS (hbbs/hbbr server + Xvfb + XFCE) заархивировано в ветке:
# RustDesk
ps aux | grep rustdesk
# /usr/lib/rustdesk/rustdesk --server должен быть запущен
```bash
git checkout vps-rustdesk-server
```
---
## Troubleshooting
### DUMMY0 не появляется после reboot
### Черный экран при подключении
```bash
# Проверить dummy driver:
grep "Loading.*dummy" /var/log/Xorg.0.log
# Должно быть: (II) Loading /usr/lib/xorg/modules/drivers/dummy_drv.so
# Проверить конфиг:
ls /etc/X11/xorg.conf.d/20-dummy-headless.conf
# Должен существовать
# Если intel driver загружается вместо dummy:
# Убедиться что нет других конфликтующих xorg.conf:
ls /etc/X11/xorg.conf 2>/dev/null || echo "OK: no xorg.conf"
```
### RustDesk черный экран
```bash
# Проверить primary:
# Проверить что DUMMY0 primary
xrandr --verbose | grep primary
# Должно быть: DUMMY0 connected primary
# Если primary не DUMMY0:
# Если нет — форсировать:
xrandr --output DUMMY0 --mode "1920x1080" --primary
xrandr --output HDMI-1 --auto --same-as DUMMY0
```
### VPS: RustDesk не видит DUMMY0
### ID меняется после перезагрузки (хост)
```bash
# Проверить что Xorg запущен:
ls /tmp/.X11-unix/X0
# Должен быть сокет
# Если нет — запустить вручную:
/usr/local/bin/start-xorg-dummy.sh
sudo bash scripts/host/setup-host-rustdesk.sh
# Защитит RustDesk.toml от перезаписи (chattr +i)
```
---
## Откат
### RustDesk не стартует
```bash
# Хост
sudo rm -f /etc/X11/xorg.conf.d/20-dummy-headless.conf
sudo rm -f /etc/X11/xorg.conf.d/20-intel-virtual.conf
sudo rm -f /etc/X11/xorg.conf.d/90-fallback.conf
systemctl --user disable --now hdmi-fallback.service
rm -f ~/.local/bin/hdmi-fallback.sh
# Проверить логи
cat /tmp/rustdesk-server.log
cat /tmp/xorg-dummy.log
# Вернуть стандартный GRUB:
sudo sed -i 's|GRUB_CMDLINE_LINUX_DEFAULT=".*"|GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"|' /etc/default/grub
sudo update-grub
# VPS
systemctl disable --now xorg-dummy.service
systemctl disable --now rustdesk-dummy.service
sudo rm -f /etc/systemd/system/xorg-dummy.service
sudo rm -f /etc/systemd/system/rustdesk-dummy.service
# Проверить процессы
pgrep -a rustdesk
pgrep -a Xorg
```
---
## Технические детали
### Почему dummy driver, а не intel с VirtualHeads?
### Почему dummy driver?
`intel` driver + `VirtualHeads=1` работает **только если HDMI подключен при загрузке**. Без кабеля — ядро не создает connector/CRTC для HDMI-A-1, и `VirtualHeads` не помогает. Dummy driver не зависит от физических выходов.
`intel` + `VirtualHeads=1` работает **только если HDMI был при загрузке**. Без кабеля — VIRTUAL1 не создается.
### Почему kernel EDID fallback?
Dummy driver не зависит от физических выходов.
Параметры `video=HDMI-A-1:1920x1080@60` и `drm.edid_firmware=HDMI-A-1:edid/samsung.bin` заставляют ядро `i915` инициализировать connector с фиктивным EDID. Это "страховка": если dummy driver по какой-то причине не загрузится (например, обновление Xorg удалит его), ядро все равно создаст выход.
### Kernel EDID fallback (только для Intel iGPU хоста)
### Почему клон, а не extended desktop?
`--same-as` клонирует framebuffer. При disconnect HDMI clone просто перестает рендериться, но primary (DUMMY0) не затрагивается — Xorg не теряет framebuffer.
### Dummy driver на VPS
Не требует GPU, не использует DRM. Работает на любом Linux с xorg-server. RustDesk использует software capture через X11 API.
Если dummy не загрузится, ядро создаст виртуальный framebuffer:
```bash
video=HDMI-A-1:1920x1080@60 drm.edid_firmware=HDMI-A-1:edid/samsung.bin
```
---
## Авторы
- Deploy via AI Agent (Kilo Code Orchestrator)
- Target hardware: Intel Alder Lake-N (i915), Ubuntu 24.04
- VPS: CPU-only, Ubuntu 24.04, Hetzner/DigitalOcean/Linode
## Ссылки
- Репозиторий: `https://git.softuniq.eu/NW/RDtop`
- Установщик: `bash -c "$(curl -fsSL https://git.softuniq.eu/NW/RDtop/raw/branch/main/install.sh)"`
- Target: Intel Alder Lake-N, Debian 12
- Universal: Any Linux with apt/dnf/pacman

View File

@@ -1,31 +0,0 @@
#!/bin/bash
# Start Xorg with dummy driver for headless VPS
export DISPLAY=:0
export XAUTHORITY=${XAUTHORITY:-/run/user/0/.Xauthority}
# Generate MIT magic cookie if needed
if [ ! -s "$XAUTHORITY" ]; then
mkdir -p "$(dirname "$XAUTHORITY")"
touch "$XAUTHORITY"
xauth -f "$XAUTHORITY" generate :0 MIT-MAGIC-COOKIE-1 2>/dev/null || \
xauth -f "$XAUTHORITY" add :0 MIT-MAGIC-COOKIE-1 "$(xxd -l 16 -p /dev/urandom)" 2>/dev/null || true
fi
# Ensure socket directory
mkdir -p /tmp/.X11-unix
# Check if already running
if pgrep -x "Xorg" >/dev/null 2>&1; then
echo "Xorg already running"
exit 0
fi
exec /usr/lib/xorg/Xorg :0 \
-nolisten tcp \
-nolisten local \
-config /etc/X11/xorg.conf.d/99-dummy.conf \
+extension GLX \
+extension RANDR \
+extension RENDER \
-novtswitch \
-keeptty

View File

@@ -0,0 +1,18 @@
[Unit]
Description=Inhibit system sleep while RustDesk is active
After=rustdesk-headless.service
[Service]
Type=simple
ExecStart=/bin/bash -c '\
while true; do \
if pgrep -f "/usr/share/rustdesk/rustdesk --server" >/dev/null; then \
systemd-inhibit --what=sleep:idle:handle-lid-switch --who="RustDesk" --why="Remote access active" sleep 60; \
else \
sleep 5; \
fi; \
done'
Restart=always
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,40 @@
[Unit]
Description=RustDesk Headless Server (auto-display, sleep-safe)
After=display-manager.service graphical.target network.target
Wants=display-manager.service
[Service]
Type=simple
User=root
Environment="DISPLAY=:0"
Environment="XAUTHORITY=/root/.Xauthority"
Environment="LIBVA_DRIVER_NAME=none"
Environment="VDPAU_DRIVER=none"
Environment="LIBGL_ALWAYS_SOFTWARE=1"
Environment="LIBGL_ALWAYS_INDIRECT=1"
# Force DUMMY0 primary before starting
ExecStartPre=/bin/bash -c '\
for i in 1 2 3 4 5 6 7 8 9 10; do \
if xrandr >/dev/null 2>&1; then \
if xrandr | grep -q "^DUMMY0"; then \
xrandr --output DUMMY0 --mode "1920x1080" --primary 2>/dev/null || true; \
exit 0; \
fi; \
fi; \
sleep 1; \
done'
ExecStart=/usr/share/rustdesk/rustdesk --server
ExecStartPost=/bin/bash -c 'echo "RustDesk started, ID: $(/usr/share/rustdesk/rustdesk --get-id 2>/dev/null || echo unknown)" >> /tmp/rustdesk-server.log'
Restart=always
RestartSec=10
StartLimitInterval=60
StartLimitBurst=3
# Prevent system sleep while RustDesk is active
ExecStopPost=/bin/systemctl start --no-block rustdesk-headless-inhibit.service
[Install]
WantedBy=multi-user.target

View File

@@ -1,23 +0,0 @@
Section "Device"
Identifier "DummyHeadless"
Driver "dummy"
Option "ConstantDPI" "true"
VideoRam 256000
EndSection
Section "Monitor"
Identifier "DummyMonitor"
HorizSync 28-80
VertRefresh 48-75
EndSection
Section "Screen"
Identifier "DummyScreen"
Device "DummyHeadless"
Monitor "DummyMonitor"
DefaultDepth 24
SubSection "Display"
Depth 24
Modes "1920x1080" "1280x720"
EndSubSection
EndSection

View File

@@ -1,17 +0,0 @@
[Unit]
Description=RustDesk with Dummy Display
After=xorg-dummy.service
Requires=xorg-dummy.service
[Service]
Type=simple
User=root
Environment="DISPLAY=:0"
Environment="XAUTHORITY=/root/.Xauthority"
ExecStartPre=/bin/sleep 3
ExecStart=/usr/share/rustdesk/rustdesk --server
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target

View File

@@ -1,34 +0,0 @@
#!/bin/bash
# RustDesk VPS Headless Client Startup Script
# Starts Xvfb :99, waits for display, then launches RustDesk client via DBus
set -e
# Kill old Xvfb
pkill -9 Xvfb 2>/dev/null || true
sleep 1
# Start Xvfb
/usr/bin/Xvfb :99 -screen 0 1920x1080x24 -ac -nolisten tcp -nolisten unix &
XVFB_PID=$!
sleep 2
# Wait for display to be ready
for i in $(seq 1 30); do
if xdpyinfo -display :99 > /dev/null 2>&1; then
break
fi
sleep 1
done
# Verify display is up
if ! xdpyinfo -display :99 > /dev/null 2>&1; then
echo "ERROR: Xvfb failed to start on :99"
kill $XVFB_PID 2>/dev/null || true
exit 1
fi
export DISPLAY=:99
# Start RustDesk client inside DBus session (required for headless GUI apps)
exec /usr/bin/dbus-run-session -- /usr/share/rustdesk/rustdesk --server

View File

@@ -1,19 +0,0 @@
[Unit]
Description=RustDesk + XFCE Desktop on VPS
After=network-online.target
[Service]
Type=forking
User=root
Environment="DISPLAY=:99"
Environment="XAUTHORITY=/root/.Xauthority"
# Start Xvfb, wait for display, then launch XFCE + RustDesk
ExecStartPre=/bin/bash -c 'pkill -9 -f rustdesk 2>/dev/null || true; pkill -9 -f xfce4-session 2>/dev/null || true; sleep 1; pkill -9 Xvfb 2>/dev/null || true; rm -f /tmp/.X11-unix/X99; sleep 1'
ExecStart=/bin/bash -c 'Xvfb :99 -screen 0 1920x1080x24 -ac +extension RANDR > /dev/null 2>&1 &; sleep 3; for i in $(seq 1 30); do export DISPLAY=:99; export XAUTHORITY=/root/.Xauthority; xrandr --listmonitors > /dev/null 2>&1 && break; sleep 1; done; nohup dbus-run-session -- xfce4-session > /tmp/xfce-session.log 2>&1 &; sleep 5; nohup dbus-run-session -- /usr/share/rustdesk/rustdesk --server > /tmp/rustdesk-session.log 2>&1 &; sleep 2; exit 0'
ExecStop=/bin/bash -c 'pkill -9 -f rustdesk; pkill -9 -f xfce4-session; pkill -9 Xvfb; rm -rf /tmp/RustDesk'
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target

View File

@@ -1,51 +0,0 @@
#!/bin/bash
# RustDesk + XFCE Desktop Auto-Start for VPS
# Systemd runs this script; script waits for Xvfb to exit
set -e
# Kill old
pkill -9 -f rustdesk 2>/dev/null || true
pkill -9 -f xfce4-session 2>/dev/null || true
pkill -9 Xvfb 2>/dev/null || true
rm -f /tmp/.X11-unix/X99 /tmp/.X*-lock
sleep 2
# Start Xvfb :99
Xvfb :99 -screen 0 1920x1080x24 -ac +extension RANDR +extension RENDER +extension GLX > /dev/null 2>&1 &
XVFB_PID=$!
sleep 3
# Wait for display ready
export DISPLAY=:99
export XAUTHORITY=/root/.Xauthority
for i in $(seq 1 30); do
xrandr --listmonitors > /dev/null 2>&1 && break
if ! kill -0 $XVFB_PID 2>/dev/null; then
echo "ERROR: Xvfb died"
exit 1
fi
sleep 1
done
xrandr --listmonitors > /dev/null 2>&1 || { echo "ERROR: Display :99 not ready"; exit 1; }
# Start XFCE desktop in background
nohup dbus-run-session -- xfce4-session > /tmp/xfce-session.log 2>&1 &
echo "XFCE PID: $!"
sleep 5
# Wait for window manager
for i in $(seq 1 20); do
ps aux | grep xfwm4 | grep -v grep > /dev/null 2>&1 && break
sleep 1
done
# Start RustDesk client in background
nohup dbus-run-session -- /usr/share/rustdesk/rustdesk --server > /tmp/rustdesk-session.log 2>&1 &
echo "RustDesk PID: $!"
sleep 3
# Wait for Xvfb (main process) - systemd tracks this script
# If Xvfb dies, systemd will restart the service
wait $XVFB_PID

View File

@@ -1,51 +0,0 @@
#!/bin/bash
# RustDesk + XFCE Desktop Auto-Start for VPS
# Systemd runs this script; script waits for Xvfb to exit
set -e
# Kill old
pkill -9 -f rustdesk 2>/dev/null || true
pkill -9 -f xfce4-session 2>/dev/null || true
pkill -9 Xvfb 2>/dev/null || true
rm -f /tmp/.X11-unix/X99 /tmp/.X*-lock
sleep 2
# Start Xvfb :99
Xvfb :99 -screen 0 1920x1080x24 -ac +extension RANDR +extension RENDER +extension GLX > /dev/null 2>&1 &
XVFB_PID=$!
sleep 3
# Wait for display ready
export DISPLAY=:99
export XAUTHORITY=/root/.Xauthority
for i in $(seq 1 30); do
xrandr --listmonitors > /dev/null 2>&1 && break
if ! kill -0 $XVFB_PID 2>/dev/null; then
echo "ERROR: Xvfb died"
exit 1
fi
sleep 1
done
xrandr --listmonitors > /dev/null 2>&1 || { echo "ERROR: Display :99 not ready"; exit 1; }
# Start XFCE desktop in background
nohup dbus-run-session -- xfce4-session > /tmp/xfce-session.log 2>&1 &
echo "XFCE PID: $!"
sleep 5
# Wait for window manager
for i in $(seq 1 20); do
ps aux | grep xfwm4 | grep -v grep > /dev/null 2>&1 && break
sleep 1
done
# Start RustDesk client in background
nohup dbus-run-session -- /usr/share/rustdesk/rustdesk --server > /tmp/rustdesk-session.log 2>&1 &
echo "RustDesk PID: $!"
sleep 3
# Wait for Xvfb (main process) - systemd tracks this script
# If Xvfb dies, systemd will restart the service
wait $XVFB_PID

View File

@@ -1,16 +0,0 @@
[Unit]
Description=Xorg Dummy Display
After=network.target
[Service]
Type=simple
User=root
Environment="DISPLAY=:0"
ExecStartPre=/bin/bash -c 'test -f /root/.Xauthority || (xauth -f /root/.Xauthority add :0 . $(mcookie) \u0026\u0026 chmod 600 /root/.Xauthority)'
ExecStartPre=/bin/mkdir -p /tmp/.X11-unix
ExecStart=/usr/lib/xorg/Xorg :0 -auth /root/.Xauthority -nolisten tcp -config /etc/X11/xorg.conf.d/20-dummy-headless.conf +extension GLX +extension RANDR +extension RENDER -novtswitch -keeptty
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target

View File

@@ -1,22 +0,0 @@
version: '3.8'
services:
hbbs:
image: rustdesk/rustdesk-server:latest
container_name: rustdesk-hbbs
command: hbbs -r rustdesk-hbbr:21117
volumes:
- ./data:/root
network_mode: host
restart: unless-stopped
depends_on:
- hbbr
hbbr:
image: rustdesk/rustdesk-server:latest
container_name: rustdesk-hbbr
command: hbbr
volumes:
- ./data:/root
network_mode: host
restart: unless-stopped

537
install.sh Normal file → Executable file
View File

@@ -1,71 +1,132 @@
#!/bin/bash
# RDtop Installer — RustDesk headless display setup (dummy driver)
# Usage: bash -c "$(curl -fsSL https://git.softuniq.eu/NW/RDtop/raw/branch/main/install.sh)"
set -euo pipefail
echo "=== RDtop Installer ==="
echo "Installing RustDesk headless display fallback..."
#######################################
# RDtop — Universal RustDesk Headless Installer
# One command → ID + Password → Ready
# Disables Wayland, forces Xorg/Xvfb, auto-selects display
#######################################
# Config paths
REPO_URL="https://git.softuniq.eu/NW/RDtop"
X11_CONF="/etc/X11/xorg.conf.d/20-dummy-headless.conf"
BIN_DST="$HOME/.local/bin/hdmi-fallback.sh"
SYSTEMD_DST="$HOME/.config/systemd/user/hdmi-fallback.service"
EDID_DST="/lib/firmware/edid/samsung.bin"
LOG="$HOME/.local/logs/rdtop-install.log"
ARCH=$(uname -m)
case "$ARCH" in
x86_64) RUSTDESK_ARCH="x86_64"; DEB_ARCH="amd64" ;;
aarch64) RUSTDESK_ARCH="aarch64"; DEB_ARCH="arm64" ;;
armv7l) RUSTDESK_ARCH="armv7"; DEB_ARCH="armhf" ;;
*) echo "ERROR: Unsupported architecture: $ARCH"; exit 1 ;;
esac
mkdir -p "$(dirname "$LOG")"
log() { echo "$(date '+%F %T') $1" | tee -a "$LOG"; }
RUSTDESK_VERSION="1.4.6"
RUSTDESK_URL="https://github.com/rustdesk/rustdesk/releases/download/${RUSTDESK_VERSION}/rustdesk-${RUSTDESK_VERSION}-${RUSTDESK_ARCH}.deb"
TMP_DEB="/tmp/rustdesk-${RUSTDESK_VERSION}-${DEB_ARCH}.deb"
LOG="/tmp/rdtop-install.log"
log "Start install"
log() { echo "[$(date '+%F %T')] $1" | tee -a "$LOG"; }
# --- Check prerequisites ---
if [ "$EUID" -eq 0 ]; then
log "ERROR: Do not run as root. Run as regular user with sudo access."
exit 1
echo ""
echo "============================================"
echo " RDtop — RustDesk Headless Installer"
echo " Arch: $ARCH | Package: ${RUSTDESK_ARCH}.deb"
echo "============================================"
echo ""
# --- 1/8 Detect arch ---
log "[1/8] Arch: $ARCH, target: ${RUSTDESK_ARCH}.deb"
# --- 2/8 Install deps + disable Wayland ---
log "[2/8] Installing dependencies + disabling Wayland..."
export DEBIAN_FRONTEND=noninteractive
if command -v apt-get >/dev/null 2>&1; then
apt-get update -qq || true
# Must-succeed base
apt-get install -y -qq curl wget ca-certificates >/dev/null 2>&1 || { log "CRITICAL: no curl/wget"; exit 1; }
# Xorg stack
apt-get install -y -qq xserver-xorg-core xserver-xorg-video-dummy x11-xserver-utils xvfb xauth >/dev/null 2>&1 || true
# VA/VDPAU
apt-get install -y -qq libva2 libvdpau1 libva-drm2 libva-x11-2 >/dev/null 2>&1 || true
# Polkit + inhibit
apt-get install -y -qq policykit-1 dbus dbus-x11 >/dev/null 2>&1 || true
elif command -v dnf >/dev/null 2>&1; then
dnf install -y curl wget xorg-x11-server-Xorg xorg-x11-drv-dummy xvfb xauth polkit dbus >/dev/null 2>&1 || true
elif command -v pacman >/dev/null 2>&1; then
pacman -Sy --noconfirm curl wget xorg-server xf86-video-dummy xvfb xauth polkit dbus >/dev/null 2>&1 || true
fi
# --- Check sudo works ---
if ! sudo -n true 2>/dev/null; then
echo "This script requires sudo privileges. Please enter your password when prompted."
fi
# --- Install dummy driver ---
if [ -f /usr/lib/xorg/modules/drivers/dummy_drv.so ]; then
log "Dummy driver already present"
else
log "Installing xserver-xorg-video-dummy..."
if command -v apt-get >/dev/null 2>&1; then
sudo apt-get update -qq && sudo apt-get install -y -qq xserver-xorg-video-dummy || true
else
log "WARNING: apt-get not found, cannot install dummy driver"
exit 1
# Disable Wayland — force Xorg for GDM (Ubuntu/Debian GNOME)
for gdm_conf in /etc/gdm3/custom.conf /etc/gdm/custom.conf; do
if [ -f "$gdm_conf" ]; then
if ! grep -q "^WaylandEnable=false" "$gdm_conf" 2>/dev/null; then
sed -i 's/^#\?WaylandEnable=.*/WaylandEnable=false/' "$gdm_conf" 2>/dev/null || true
if ! grep -q "WaylandEnable=false" "$gdm_conf" 2>/dev/null; then
sed -i '/\[daemon\]/a WaylandEnable=false' "$gdm_conf" 2>/dev/null || true
fi
log " Wayland disabled in $gdm_conf"
fi
fi
done
# Also ensure Xorg is default for SDDM (KDE) and LightDM
if [ -f /etc/sddm.conf ]; then
sed -i 's/^DisplayServer=.*/DisplayServer=x11/' /etc/sddm.conf 2>/dev/null || true
fi
if [ -f /etc/lightdm/lightdm.conf ]; then
sed -i 's/^#\?display-manager=.*/display-manager=xorg/' /etc/lightdm/lightdm.conf 2>/dev/null || true
fi
# --- Remove old conflicting configs ---
log "Removing old Intel X11 configs"
sudo rm -f /etc/X11/xorg.conf.d/20-intel-virtual.conf
sudo rm -f /etc/X11/xorg.conf.d/90-fallback.conf
# --- 3/8 Disable sleep / lock ---
log "[3/8] Disabling sleep, suspend, hibernate, screensaver..."
systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target 2>/dev/null || true
# --- Copy EDID firmware ---
log "Saving EDID firmware"
if [ -f /sys/class/drm/card0-HDMI-A-1/edid ]; then
sudo mkdir -p /lib/firmware/edid
sudo cp /sys/class/drm/card0-HDMI-A-1/edid "$EDID_DST"
sudo chmod 644 "$EDID_DST"
log "EDID saved to $EDID_DST"
else
log "WARNING: No EDID found at /sys/class/drm/card0-HDMI-A-1/edid"
if [ -f /etc/systemd/logind.conf ]; then
cp /etc/systemd/logind.conf /etc/systemd/logind.conf.bak.$(date +%s) 2>/dev/null || true
cat > /etc/systemd/logind.conf <<'EOF'
[Login]
HandleLidSwitch=ignore
HandleLidSwitchExternalPower=ignore
HandleLidSwitchDocked=ignore
IdleAction=ignore
EOF
systemctl restart systemd-logind 2>/dev/null || true
log " systemd-logind: sleep disabled"
fi
# --- Create dummy headless X11 config ---
log "Creating dummy headless X11 config at $X11_CONF"
cat <<'XORG' | sudo tee "$X11_CONF" >/dev/null
if command -v gsettings >/dev/null 2>&1; then
gsettings set org.gnome.desktop.screensaver lock-enabled false 2>/dev/null || true
gsettings set org.gnome.desktop.screensaver idle-activation-enabled false 2>/dev/null || true
gsettings set org.gnome.desktop.session idle-delay 0 2>/dev/null || true
gsettings set org.gnome.desktop.screensaver lock-active false 2>/dev/null || true
log " GNOME screensaver: disabled"
fi
if command -v xset >/dev/null 2>&1; then
xset s off 2>/dev/null || true
xset -dpms 2>/dev/null || true
xset s noblank 2>/dev/null || true
fi
# --- 4/8 Detect display ---
log "[4/8] Detecting display environment..."
HAS_DISPLAY=false
HAS_XORG=false
if [ -n "${DISPLAY:-}" ] && xdpyinfo >/dev/null 2>&1; then
HAS_DISPLAY=true
log " Active display: $DISPLAY"
fi
if pgrep -x "Xorg" >/dev/null 2>&1 || pgrep -x "X" >/dev/null 2>&1; then
HAS_XORG=true
log " Xorg already running"
fi
# --- 5/8 Setup dummy driver ---
log "[5/8] Setting up dummy driver..."
mkdir -p /etc/X11/xorg.conf.d/
rm -f /etc/X11/xorg.conf.d/20-intel-virtual.conf 2>/dev/null || true
rm -f /etc/X11/xorg.conf.d/90-fallback.conf 2>/dev/null || true
cat > /etc/X11/xorg.conf.d/20-dummy-headless.conf <<'XORG'
Section "Device"
Identifier "DummyHeadless"
Driver "dummy"
Option "ConstantDPI" "true"
VideoRam 256000
EndSection
@@ -83,192 +144,264 @@ Section "Screen"
SubSection "Display"
Depth 24
Modes "1920x1080" "1280x720"
Virtual 1920 1080
EndSubSection
EndSection
XORG
sudo chmod 644 "$X11_CONF"
log "X11 config installed"
# --- Update GRUB for kernel EDID fallback ---
log "Updating GRUB cmdline for EDID fallback"
if [ -f /etc/default/grub ]; then
sudo sed -i 's|GRUB_CMDLINE_LINUX_DEFAULT=".*"|GRUB_CMDLINE_LINUX_DEFAULT="quiet splash video=HDMI-A-1:1920x1080@60 drm.edid_firmware=HDMI-A-1:edid/samsung.bin"|' /etc/default/grub
sudo update-grub >/dev/null 2>&1 || log "WARNING: update-grub failed"
log "GRUB updated"
Section "ServerLayout"
Identifier "DummyLayout"
Screen 0 "DummyScreen" 0 0
Option "AutoAddGPU" "false"
EndSection
XORG
chmod 644 /etc/X11/xorg.conf.d/20-dummy-headless.conf
# --- 6/8 Start virtual display ---
if [ "$HAS_DISPLAY" = false ] && [ "$HAS_XORG" = false ]; then
log "[6/8] Starting virtual display on :0..."
# Clean everything
pkill -x Xorg 2>/dev/null || true
pkill -x Xvfb 2>/dev/null || true
rm -f /tmp/.X11-unix/X0 /tmp/.X0-lock /root/.Xauthority 2>/dev/null || true
mkdir -p /tmp/.X11-unix
chmod 1777 /tmp/.X11-unix
# Try Xorg first (with -ac no-auth, dummy config)
if [ "$EUID" -eq 0 ]; then
nohup Xorg :0 -config /etc/X11/xorg.conf.d/20-dummy-headless.conf -ac -nolisten tcp >/tmp/xorg-dummy.log 2>&1 &
sleep 5
fi
if [ -S /tmp/.X11-unix/X0 ] && pgrep -x Xorg >/dev/null; then
log " Xorg dummy :0 OK"
# Generate xauth cookie
COOKIE=$(mcookie)
xauth -f /root/.Xauthority add :0 . "$COOKIE" 2>/dev/null || true
xauth add :0 . "$COOKIE" 2>/dev/null || true
chmod 600 /root/.Xauthority 2>/dev/null || true
export XAUTHORITY=/root/.Xauthority
else
# Xvfb fallback — always works, -ac = no auth needed
log " Xorg failed, using Xvfb..."
pkill -x Xorg 2>/dev/null || true
rm -f /tmp/.X11-unix/X0 /tmp/.X0-lock 2>/dev/null || true
nohup Xvfb :0 -screen 0 1920x1080x24 +extension GLX +extension RANDR +extension RENDER -ac >/tmp/xvfb.log 2>&1 &
sleep 4
if [ -S /tmp/.X11-unix/X0 ]; then
log " Xvfb :0 OK (no auth)"
# Xvfb with -ac does not need xauth
export XAUTHORITY=""
else
log "CRITICAL: No display could start"
exit 1
fi
fi
else
log "WARNING: /etc/default/grub not found"
log " Step 6 skipped: existing display"
fi
# --- Install fallback script ---
log "Installing hdmi-fallback.sh to ~/.local/bin/"
mkdir -p "$HOME/.local/bin"
cat > "$BIN_DST" <<'SCRIPT'
#!/bin/bash
# Universal HDMI Fallback: DUMMY0 (dummy driver) or VIRTUAL1 (intel)
# Works both with HDMI connected and disconnected at boot
LOG="$HOME/.local/logs/drm-hotplug.log"
mkdir -p "$HOME/.local/logs"
export DISPLAY=:0
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') $1" | tee -a "$LOG" >/dev/null
}
# Find available primary output (DUMMY0 or VIRTUAL1)
find_primary_output() {
if xrandr 2>/dev/null | grep -q "^DUMMY0"; then
echo "DUMMY0"
elif xrandr 2>/dev/null | grep -q "^VIRTUAL1"; then
echo "VIRTUAL1"
else
echo ""
fi
}
# Find HDMI output name (HDMI-1, HDMI1, etc)
find_hdmi_output() {
xrandr 2>/dev/null | grep -E "^HDMI-[0-9] connected" | awk '{print $1}' | head -1
}
# Set up mode for virtual output
setup_mode() {
local OUTPUT="$1"
local MODE_NAME="1920x1080"
if [ "$OUTPUT" = "VIRTUAL1" ]; then
local MODELINE="1920x1080_60.00"
if ! xrandr 2>/dev/null | grep -q "$MODELINE"; then
xrandr --newmode "$MODELINE" 173.00 1920 2048 2248 2576 1080 1083 1088 1120 2>/dev/null || true
# Force DUMMY0/VIRTUAL1 primary before RustDesk
log " Forcing primary output..."
if command -v xrandr >/dev/null 2>&1; then
for i in $(seq 1 10); do
if xrandr >/dev/null 2>&1; then
if xrandr | grep -q "^DUMMY0"; then
xrandr --output DUMMY0 --mode "1920x1080" --primary 2>/dev/null || true
log " DUMMY0 primary"
break
fi
if xrandr | grep -q "^VIRTUAL1"; then
xrandr --newmode "1920x1080_60" 173.00 1920 2048 2248 2576 1080 1083 1088 1120 2>/dev/null || true
xrandr --addmode VIRTUAL1 "1920x1080_60" 2>/dev/null || true
xrandr --output VIRTUAL1 --mode "1920x1080_60" --primary 2>/dev/null || true
log " VIRTUAL1 primary"
break
fi
fi
xrandr --addmode VIRTUAL1 "$MODELINE" 2>/dev/null || true
MODE_NAME="$MODELINE"
fi
echo "$MODE_NAME"
}
log "Start monitor (user=$USER, display=$DISPLAY)"
CONNECTOR=""
for p in /sys/class/drm/card1-HDMI-A-1 /sys/class/drm/card0-HDMI-A-1; do
[ -e "$p/status" ] && { CONNECTOR="$p"; break; }
done
[ -z "$CONNECTOR" ] && CONNECTOR=$(find /sys/class/drm -maxdepth 1 -name "*HDMI-A-1" -type d | head -1)
if [ -z "$CONNECTOR" ] || [ ! -e "$CONNECTOR/status" ]; then
log "WARNING: No HDMI-A-1 connector found, running headless only"
sleep 1
done
fi
# Initial state
LAST_STATE=$(cat "$CONNECTOR/status" 2>/dev/null || echo unknown)
log "Initial HDMI: $LAST_STATE"
# --- 7/8 Install RustDesk ---
log "[7/8] Installing RustDesk..."
# Detect outputs
PRIMARY=$(find_primary_output)
HDMI=$(find_hdmi_output)
# Check if already installed (from repo)
if [ -f /usr/share/rustdesk/rustdesk ]; then
log " RustDesk already installed"
if systemctl is-active rustdesk >/dev/null 2>&1; then
log " Stopping system rustdesk service..."
systemctl stop rustdesk 2>/dev/null || true
pkill -f "/usr/bin/rustdesk --service" 2>/dev/null || true
sleep 2
fi
fi
if [ -z "$PRIMARY" ]; then
log "ERROR: No virtual output found (DUMMY0 or VIRTUAL1)"
# Download .deb
if command -v curl >/dev/null 2>&1; then
curl -fsSL -o "$TMP_DEB" "$RUSTDESK_URL" 2>/dev/null || true
elif command -v wget >/dev/null 2>&1; then
wget -q -O "$TMP_DEB" "$RUSTDESK_URL" 2>/dev/null || true
fi
INSTALL_OK=false
if [ -f /usr/share/rustdesk/rustdesk ]; then
INSTALL_OK=true
fi
if [ -f "$TMP_DEB" ] && [ -s "$TMP_DEB" ]; then
log " Installing from .deb..."
dpkg --force-depends --force-confnew -i "$TMP_DEB" >/dev/null 2>&1 || true
apt-get install -f -y -qq >/dev/null 2>&1 || true
rm -f "$TMP_DEB"
if [ -f /usr/share/rustdesk/rustdesk ]; then
INSTALL_OK=true
fi
fi
if [ "$INSTALL_OK" = false ]; then
log " Trying repository install..."
apt-get install -y -qq rustdesk 2>/dev/null || true
if [ -f /usr/share/rustdesk/rustdesk ]; then
INSTALL_OK=true
fi
fi
if [ "$INSTALL_OK" = false ]; then
log "CRITICAL: RustDesk installation failed"
exit 1
fi
log "Primary: $PRIMARY, HDMI: ${HDMI:-none}"
log " RustDesk OK: $(/usr/share/rustdesk/rustdesk --version 2>/dev/null || echo unknown)"
MODE=$(setup_mode "$PRIMARY")
# --- 8/8 Configure and start RustDesk ---
log "[8/8] Configuring RustDesk..."
# Initial setup
if [ "$LAST_STATE" = "connected" ] && [ -n "$HDMI" ]; then
log "HDMI connected, setting clone: $PRIMARY primary + $HDMI same-as"
xrandr --output "$PRIMARY" --mode "$MODE" --primary 2>/dev/null || true
xrandr --output "$HDMI" --auto --same-as "$PRIMARY" 2>/dev/null || true
else
log "HDMI disconnected or not found, keeping $PRIMARY primary"
xrandr --output "$PRIMARY" --mode "$MODE" --primary 2>/dev/null || true
[ -n "$HDMI" ] && xrandr --output "$HDMI" --off 2>/dev/null || true
mkdir -p ~/.config/rustdesk
cat > ~/.config/rustdesk/RustDesk2.toml <<'EOF'
rendezvous_server = 'rs-ny.rustdesk.com:21116'
nat_type = 1
serial = 0
unlock_pin = ''
[options]
local-ip-addr = 'auto'
hw-codec = 'N'
EOF
chmod 600 ~/.config/rustdesk/RustDesk2.toml
# Kill old RustDesk
pkill -f "/usr/share/rustdesk/rustdesk" 2>/dev/null || true
sleep 2
# Start with software rendering (no GL needed)
export DISPLAY=:0
export XAUTHORITY="${XAUTHORITY:-/root/.Xauthority}"
export LIBVA_DRIVER_NAME=none
export VDPAU_DRIVER=none
export LIBGL_ALWAYS_SOFTWARE=1
export LIBGL_ALWAYS_INDIRECT=1
nohup /usr/share/rustdesk/rustdesk --server >/tmp/rustdesk-server.log 2>&1 &
sleep 5
# Verify
if ! pgrep -f "/usr/share/rustdesk/rustdesk --server" >/dev/null; then
log " Retry RustDesk start..."
sleep 3
nohup /usr/share/rustdesk/rustdesk --server >/tmp/rustdesk-server.log 2>&1 &
sleep 3
fi
log "Setup complete. Monitors:"
xrandr --listmonitors 2>/dev/null | while read -r line; do log " $line"; done
# Get credentials
RUSTDESK_ID=$(/usr/share/rustdesk/rustdesk --get-id 2>/dev/null || echo "N/A")
RUSTDESK_PASS=""
if [ -f ~/.config/rustdesk/RustDesk.toml ]; then
RUSTDESK_PASS=$(python3 -c "
import sys
try:
with open(sys.argv[1]) as f:
content = f.read()
for line in content.split('\n'):
if 'password' in line and '=' in line:
val = line.split('=', 1)[1].strip().strip(\"'\").strip('\"')
if val: print(val); break
except: pass
" ~/.config/rustdesk/RustDesk.toml 2>/dev/null || true)
fi
if [ -z "$RUSTDESK_PASS" ]; then
RUSTDESK_PASS="<пароль в GUI: Настройки → Безопасность>"
fi
# Main loop
while true; do
CURRENT=$(cat "$CONNECTOR/status" 2>/dev/null || echo unknown)
if [ "$CURRENT" != "$LAST_STATE" ]; then
log "HDMI changed: $LAST_STATE -> $CURRENT"
# Re-detect
PRIMARY=$(find_primary_output)
HDMI=$(find_hdmi_output)
MODE=$(setup_mode "$PRIMARY")
if [ -z "$PRIMARY" ]; then
log "ERROR: No virtual output detected after HDMI change"
sleep 5
continue
fi
# Always make PRIMARY primary
xrandr --output "$PRIMARY" --mode "$MODE" --primary 2>/dev/null || true
if [ "$CURRENT" = "connected" ] && [ -n "$HDMI" ]; then
log "HDMI connected, cloning $HDMI to $PRIMARY"
xrandr --output "$HDMI" --auto --same-as "$PRIMARY" 2>/dev/null || true
else
log "HDMI disconnected, keeping $PRIMARY primary"
[ -n "$HDMI" ] && xrandr --output "$HDMI" --off 2>/dev/null || true
fi
LAST_STATE="$CURRENT"
fi
sleep 2
done
SCRIPT
chmod +x "$BIN_DST"
log "Script installed"
# --- Install systemd unit ---
log "Installing systemd user unit"
mkdir -p "$HOME/.config/systemd/user"
cat > "$SYSTEMD_DST" <<'UNIT'
# Install systemd unit
log " Installing systemd unit..."
cat > /etc/systemd/system/rustdesk-headless.service <<'UNIT'
[Unit]
Description=HDMI to Virtual Fallback Monitor
After=graphical-session.target
Description=RustDesk Headless Server
After=network.target
Wants=network.target
[Service]
Type=simple
ExecStart=%h/.local/bin/hdmi-fallback.sh
Restart=always
RestartSec=5
User=root
Environment="DISPLAY=:0"
Environment="XAUTHORITY=/root/.Xauthority"
Environment="LIBVA_DRIVER_NAME=none"
Environment="VDPAU_DRIVER=none"
Environment="LIBGL_ALWAYS_SOFTWARE=1"
ExecStartPre=/bin/bash -c 'for i in 1 2 3 4 5 6 7 8 9 10; do if xrandr >/dev/null 2>&1; then if xrandr | grep -q "^DUMMY0"; then xrandr --output DUMMY0 --mode "1920x1080" --primary 2>/dev/null || true; exit 0; fi; fi; sleep 1; done'
ExecStart=/usr/share/rustdesk/rustdesk --server
Restart=always
RestartSec=10
[Install]
WantedBy=graphical-session.target
WantedBy=multi-user.target
UNIT
systemctl --user daemon-reload
systemctl --user enable --now hdmi-fallback.service 2>/dev/null || true
log "Systemd unit enabled"
systemctl daemon-reload
systemctl enable rustdesk-headless.service
# --- Summary ---
# Stop nohup, start via systemd
pkill -f "/usr/share/rustdesk/rustdesk --server" 2>/dev/null || true
sleep 2
systemctl start rustdesk-headless.service
sleep 3
if systemctl is-active rustdesk-headless.service >/dev/null 2>&1; then
log " systemd unit ACTIVE"
else
log " WARNING: systemd failed, using nohup fallback"
nohup /usr/share/rustdesk/rustdesk --server >/tmp/rustdesk-server.log 2>&1 &
fi
# Summary
echo ""
echo "============================================"
echo "RDtop installation complete!"
echo " RustDesk Ready!"
echo "============================================"
echo ""
echo "Next steps:"
echo "1. REBOOT now for changes to take effect:"
echo " sudo reboot"
printf " %-14s %s\n" "ID:" "$RUSTDESK_ID"
printf " %-14s %s\n" "Password:" "$RUSTDESK_PASS"
printf " %-14s %s\n" "Display:" ":0 (DUMMY0 primary 1920x1080)"
printf " %-14s %s\n" "Wayland:" "disabled (Xorg forced)"
printf " %-14s %s\n" "Sleep/Lock:" "disabled"
printf " %-14s %s\n" "Version:" "$RUSTDESK_VERSION"
printf " %-14s %s\n" "Arch:" "$ARCH"
echo ""
echo "2. After reboot without HDMI, check:"
echo " xrandr --listmonitors"
echo " Connect: rustdesk $RUSTDESK_ID"
echo " Status: systemctl status rustdesk-headless"
echo " Logs: /tmp/rdtop-install.log"
echo " RustDesk: /tmp/rustdesk-server.log"
echo " Xorg: /tmp/xorg-dummy.log"
echo ""
echo "3. Service status:"
echo " systemctl --user status hdmi-fallback.service"
echo ""
echo "4. View logs:"
echo " cat ~/.local/logs/drm-hotplug.log"
echo ""
log "Install complete"
echo "============================================"
log "Done. ID: $RUSTDESK_ID"
# Show monitor status
if command -v xrandr >/dev/null 2>&1; then
echo ""
echo "--- Monitor status ---"
xrandr --listmonitors 2>/dev/null || echo "xrandr unavailable"
fi

View File

@@ -1,43 +0,0 @@
#!/bin/bash
# RustDesk Client Launcher for VPS (Manual Run)
# Run this script when you want to connect to host from VPS
# 1. Ensure no conflicting X servers
pkill -f Xvfb
pkill -f Xorg
# 2. Start Xvfb on :0
rm -f /tmp/.X11-unix/X0 /tmp/.X0-lock 2>/dev/null
/usr/bin/Xvfb :0 -screen 0 1920x1080x24 +extension GLX +extension RANDR +extension RENDER -ac &
echo "Xvfb started on :0"
sleep 2
# 3. Configure RustDesk
cat > ~/.config/rustdesk/RustDesk2.toml <<'EOF'
rendezvous_server = 'localhost:21116'
custom-rendezvous-server = 'localhost:21116'
key = 'JDSXd9sxN6Y7mIzu3krSpPTTc4yjChfqpzgIH5fl9Iw='
nat_type = 1
serial = 0
unlock_pin = ''
[options]
local-ip-addr = '2.59.219.61'
EOF
# 4. Start RustDesk client
export DISPLAY=:0
export XAUTHORITY=/root/.Xauthority
export LIBVA_DRIVER_NAME=none
export VDPAU_DRIVER=none
# Add xauth for display :0
xauth -f "$XAUTHORITY" add :0 . $(mcookie) 2>/dev/null || true
echo "Starting RustDesk..."
nohup /usr/share/rustdesk/rustdesk &
echo "RustDesk started. Check ID: DISPLAY=:0 /usr/share/rustdesk/rustdesk --get-id"
sleep 2
echo "Current ID:"
DISPLAY=:0 /usr/share/rustdesk/rustdesk --get-id

View File

@@ -1,56 +0,0 @@
#!/bin/bash
# RustDesk VPS - запуск с Xvfb + xcompmgr для корректного GUI рендеринга
export DISPLAY=:0
export XAUTHORITY=/root/.Xauthority
export LIBGL_ALWAYS_SOFTWARE=1
export LIBVA_DRIVER_NAME=none
export VDPAU_DRIVER=none
# 1. Очищаем старые сессии
pkill -f Xvfb
pkill -f rustdesk
sleep 1
rm -f /tmp/.X11-unix/X0 /tmp/.X0-lock 2>/dev/null
# 2. Запускаем Xvfb с COMPOSITE extension
echo "Starting Xvfb..."
/usr/bin/Xvfb :0 -screen 0 1920x1080x24 +extension GLX +extension RANDR +extension RENDER +extension COMPOSITE -ac &
echo "Xvfb PID: $!"
sleep 2
# 3. Проверяем запуск Xvfb
if ! pgrep -f "Xvfb :0" >/dev/null; then
echo "ERROR: Xvfb failed to start"
exit 1
fi
echo "Xvfb OK"
# 4. Запускаем композитор
echo "Starting xcompmgr..."
if command -v xcompmgr >/dev/null 2>&1; then
/usr/bin/xcompmgr -d :0 &
else
echo "WARNING: xcompmgr not found. Install with: apt-get install -y xcompmgr"
fi
# 5. Настраиваем xauth
xauth -f "$XAUTHORITY" add :0 . $(mcookie) 2>/dev/null || true
chmod 600 "$XAUTHORITY"
# 6. Пишем конфиг
/root/write-rustdesk-config.sh
# 7. Запускаем RustDesk
echo "Starting RustDesk..."
nohup /usr/share/rustdesk/rustdesk &
sleep 5
echo "=== Status ==="
echo "Xvfb: $(pgrep -a Xvfb | grep :0 || echo 'NOT RUNNING')"
echo "RustDesk:"
pgrep -a rustdesk || echo 'NOT RUNNING'
echo ""
echo "RustDesk ID: $(/usr/share/rustdesk/rustdesk --get-id 2>/dev/null || echo 'N/A')"
echo ""
echo "To connect to host: rustdesk <host-id>"