diff options
| -rw-r--r-- | README.md | 40 | ||||
| -rw-r--r-- | assets/brightness.svg | 39 | ||||
| -rw-r--r-- | assets/corner.png | bin | 1118 -> 0 bytes | |||
| -rw-r--r-- | assets/mute.png | bin | 5911 -> 0 bytes | |||
| -rw-r--r-- | assets/trans.png | bin | 169 -> 0 bytes | |||
| -rw-r--r-- | assets/vol.png | bin | 5607 -> 0 bytes | |||
| -rwxr-xr-x | clear-screen | 3 | ||||
| -rwxr-xr-x | hotspots | 121 | ||||
| -rwxr-xr-x | layer.sh | 24 | ||||
| -rwxr-xr-x | lockscreen-wallpaper | 5 | ||||
| -rwxr-xr-x | low-bat-notifier | 66 | ||||
| -rwxr-xr-x | move-qemu.sh | 74 | ||||
| -rwxr-xr-x | opacity-change.sh | 34 | ||||
| -rwxr-xr-x | plank.sh | 5 | ||||
| -rwxr-xr-x | powermenu | 38 | ||||
| -rwxr-xr-x | qemu-helper.sh | 172 | ||||
| -rwxr-xr-x | random-wall | 128 | ||||
| -rwxr-xr-x | random_data.py | 153 | ||||
| -rwxr-xr-x | root.sh | 54 | ||||
| -rwxr-xr-x | screenshot | 38 | ||||
| -rwxr-xr-x | session | 21 | ||||
| -rwxr-xr-x | spec | 73 | ||||
| -rwxr-xr-x | systray | 17 | ||||
| -rw-r--r-- | test/.editorconfig | 14 | ||||
| -rwxr-xr-x | test/unhidewindow | 10 | ||||
| -rwxr-xr-x | title-bar | 137 | ||||
| -rwxr-xr-x | toggle-control | 53 | ||||
| -rwxr-xr-x | unix/sys/battery_alert.sh | 43 | ||||
| -rwxr-xr-x | unix/sys/gsettings.sh (renamed from gsettings.sh) | 13 | ||||
| -rwxr-xr-x | unix/sys/keep_guest_awake.sh | 84 | ||||
| -rwxr-xr-x | unix/sys/low-bat-notifier | 79 | ||||
| -rwxr-xr-x | unix/sys/session_manager.sh (renamed from session_manager.sh) | 0 | ||||
| -rwxr-xr-x | unix/sys/shutdown_watchdog | 11 | ||||
| -rwxr-xr-x | unix/utils/backlight_default.sh | 32 | ||||
| -rwxr-xr-x | unix/utils/colors.sh (renamed from colors.sh) | 0 | ||||
| -rwxr-xr-x | unix/utils/cryptocheck (renamed from cryptocheck) | 0 | ||||
| -rwxr-xr-x | unix/utils/cryptonotify (renamed from cryptonotify) | 0 | ||||
| -rwxr-xr-x | unix/utils/disable_meta_key_guest.sh | 94 | ||||
| -rwxr-xr-x | unix/utils/heads-up-display (renamed from heads-up-display) | 8 | ||||
| -rwxr-xr-x | unix/utils/hex2rgb.sh | 3 | ||||
| -rwxr-xr-x | unix/utils/kill-notify (renamed from kill-notify) | 0 | ||||
| -rwxr-xr-x | unix/utils/kill-process (renamed from kill-process) | 0 | ||||
| -rwxr-xr-x | unix/utils/mnt (renamed from mnt) | 0 | ||||
| -rwxr-xr-x | unix/utils/move_terminal | 65 | ||||
| -rwxr-xr-x | unix/utils/neovim.sh (renamed from neovim.sh) | 19 | ||||
| -rwxr-xr-x | unix/utils/pack (renamed from scratchpad) | 50 | ||||
| -rwxr-xr-x | unix/utils/rofi-network-manager.sh (renamed from rofi-network-manager.sh) | 0 | ||||
| -rwxr-xr-x | unix/utils/root.sh | 152 | ||||
| -rwxr-xr-x | unix/utils/run_with_display.sh | 19 | ||||
| -rwxr-xr-x | unix/utils/scratchpad | 147 | ||||
| -rwxr-xr-x | unix/utils/screenshot | 102 | ||||
| -rwxr-xr-x | unix/utils/sendkeys.awk | 86 | ||||
| -rwxr-xr-x | unix/utils/sext | 47 | ||||
| -rwxr-xr-x | unix/utils/track-books.sh (renamed from track-books.sh) | 0 | ||||
| -rwxr-xr-x | unix/utils/umnt (renamed from umnt) | 0 | ||||
| -rwxr-xr-x | unix/utils/waypipe_app | 16 | ||||
| -rwxr-xr-x | unix/utils/wayvnc_session | 10 | ||||
| -rwxr-xr-x | unix/utils/window_manager_name.sh | 29 | ||||
| -rwxr-xr-x | unix/utils/xtouch (renamed from xtouch) | 0 | ||||
| -rwxr-xr-x | unix/virt/checksum.sh | 36 | ||||
| -rwxr-xr-x | unix/virt/dos.sh | 998 | ||||
| -rwxr-xr-x | unix/virt/server.sh | 128 | ||||
| -rwxr-xr-x | unix/virt/ubuntu | 222 | ||||
| -rwxr-xr-x | unix/virt/windows.sh | 1156 | ||||
| -rwxr-xr-x | update-title | 26 | ||||
| -rw-r--r-- | win-nvim.bat | 37 | ||||
| -rw-r--r-- | win-nvim.ps1 | 39 |
67 files changed, 3662 insertions, 1408 deletions
@@ -1 +1,41 @@ # scripts + +``` +.scripts/ +├── assets/ # Images, icons, templates, and other resource files +├── env/ # OS-specific environment setups +│ ├── linux/ # Linux-specific configurations +│ │ ├── autorun/ # Auto-start and background services +│ │ ├── storage/ # Disk operations, file management, backups +│ │ └── utils/ # Linux utility scripts +│ ├── unix/ # macOS/BSD-specific configurations +│ ├── virt/ # Virtualization/Containerization (Docker, KVM, LXC) +│ └── windows/ # Windows-specific configurations +│ ├── autorun/ # Auto-start and background services +│ ├── storage/ # Disk operations, file management, backups +│ └── utils/ # Windows utility scripts +├── media/ # Gaming, social media, video, streaming automation +├── network/ # Networking-related scripts +│ ├── dns/ # DNS configuration, resolver tools +│ ├── monitoring/ # Network monitoring, packet capture, alerts +│ ├── ssh/ # SSH key management, tunnels, jump hosts +│ └── vpn/ # VPN configuration and scripts +├── security/ # Security-related scripts +│ ├── audit/ # Security auditing, log analysis, compliance checks +│ ├── defensive/ # Hardening, honeypots, firewall rules +│ ├── forensics/ # Incident response, digital forensics tools +│ └── offensive/ # Exploitation, penetration testing +├── sysadmin/ # Administrative tasks +│ ├── active_directory/ # Active Directory management +│ ├── automation/ # Automated processes (cron, schedule tasks, startup scripts) +│ ├── databases/ # Database-related scripts (backups, migrations) +│ └── sysinfo/ # System monitoring, hardware info/diagnostics, logs, device management +├── test/ # Testing, debugging, script validation +├── tools/ # General utilities and helper scripts +├── .editorconfig # Define consistent editing/formatting styles +├── .gitignore # Specify files to ignore/not shared +├── .gitmodules # Track external repositories +├── LICENSE # Usage and distribution terms +└── README.md # Documentation + +``` diff --git a/assets/brightness.svg b/assets/brightness.svg deleted file mode 100644 index 85c44c1..0000000 --- a/assets/brightness.svg +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version="1.0" encoding="iso-8859-1"?>
-<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
-<svg height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
- viewBox="0 0 512 512" xml:space="preserve">
-<g>
- <path style="fill:#F2D23D;" d="M256.007,423.724c-0.004,0-0.004,0-0.007,0c-14.626,0-26.483,11.857-26.483,26.483v35.31
- C229.517,500.143,241.374,512,256,512c0.004,0,0.004,0,0.007,0c14.622-0.004,26.476-11.859,26.476-26.483v-35.31
- C282.483,435.583,270.629,423.728,256.007,423.724z"/>
- <path style="fill:#F2D23D;" d="M256.007,0c-0.004,0-0.004,0-0.007,0c-14.626,0-26.483,11.857-26.483,26.483v35.31
- c0,14.626,11.857,26.483,26.483,26.483c0.004,0,0.004,0,0.007,0c14.622-0.004,26.476-11.859,26.476-26.483v-35.31
- C282.483,11.859,270.629,0.004,256.007,0z"/>
- <path style="fill:#F2D23D;" d="M256.007,141.241h-0.004c-63.276,0-114.755,51.481-114.755,114.759s51.479,114.759,114.755,114.759
- h0.004c63.278-0.002,114.759-51.481,114.759-114.759C370.766,192.724,319.285,141.243,256.007,141.241z"/>
-</g>
-<g>
- <path style="fill:#EEBF00;" d="M256.004,141.241c-63.276,0-114.755,51.481-114.755,114.76c0,63.278,51.479,114.757,114.755,114.757
- h0.004V141.241H256.004z"/>
- <path style="fill:#EEBF00;" d="M256.007,88.276V0c-0.004,0-0.004,0-0.007,0c-14.626,0-26.483,11.857-26.483,26.483v35.31
- c0,14.626,11.857,26.483,26.483,26.483C256.004,88.276,256.004,88.276,256.007,88.276z"/>
- <path style="fill:#EEBF00;" d="M256,423.724c-14.626,0-26.483,11.857-26.483,26.483v35.31C229.517,500.143,241.374,512,256,512
- c0.004,0,0.004,0,0.007,0v-88.276C256.004,423.724,256.004,423.724,256,423.724z"/>
-</g>
-<path style="fill:#F2D23D;" d="M423.724,256c0,14.626,11.857,26.483,26.483,26.483h35.31C500.143,282.483,512,270.626,512,256
- c0-14.626-11.857-26.483-26.483-26.483h-35.31C435.581,229.517,423.724,241.374,423.724,256z"/>
-<path style="fill:#EEBF00;" d="M26.483,282.483h35.31c14.626,0,26.483-11.857,26.483-26.483c0-14.626-11.857-26.483-26.483-26.483
- h-35.31C11.857,229.517,0,241.374,0,256C0,270.626,11.857,282.483,26.483,282.483z"/>
-<path style="fill:#F2D23D;" d="M399.567,74.98l-24.97,24.968c-10.342,10.342-10.342,27.11,0,37.452
- c5.171,5.171,11.949,7.758,18.725,7.758s13.556-2.586,18.725-7.756l24.97-24.968c10.342-10.342,10.342-27.11,0-37.452
- C426.68,64.637,409.911,64.637,399.567,74.98z"/>
-<path style="fill:#EEBF00;" d="M99.949,374.597l-24.968,24.968c-10.342,10.342-10.342,27.109,0,37.452
- c5.171,5.171,11.949,7.756,18.725,7.756s13.556-2.586,18.725-7.756l24.97-24.968c10.342-10.342,10.342-27.11,0-37.452
- C127.061,364.256,110.292,364.254,99.949,374.597z"/>
-<path style="fill:#F2D23D;" d="M399.567,437.018c5.171,5.171,11.949,7.756,18.725,7.756c6.778,0,13.556-2.585,18.727-7.756
- c10.342-10.342,10.342-27.11,0-37.452l-24.97-24.968c-10.341-10.341-27.11-10.341-37.452,0.002
- c-10.342,10.342-10.342,27.109,0,37.452L399.567,437.018z"/>
-<path style="fill:#EEBF00;" d="M99.949,137.401c5.171,5.171,11.949,7.756,18.727,7.756c6.778,0,13.556-2.586,18.725-7.758
- c10.342-10.342,10.342-27.11,0-37.452L112.433,74.98c-10.341-10.341-27.11-10.341-37.452,0c-10.342,10.342-10.342,27.11,0,37.452
- L99.949,137.401z"/>
-</svg>
\ No newline at end of file diff --git a/assets/corner.png b/assets/corner.png Binary files differdeleted file mode 100644 index c0239d8..0000000 --- a/assets/corner.png +++ /dev/null diff --git a/assets/mute.png b/assets/mute.png Binary files differdeleted file mode 100644 index 41cf959..0000000 --- a/assets/mute.png +++ /dev/null diff --git a/assets/trans.png b/assets/trans.png Binary files differdeleted file mode 100644 index 56c8a67..0000000 --- a/assets/trans.png +++ /dev/null diff --git a/assets/vol.png b/assets/vol.png Binary files differdeleted file mode 100644 index 7e388f6..0000000 --- a/assets/vol.png +++ /dev/null diff --git a/clear-screen b/clear-screen deleted file mode 100755 index 90debb0..0000000 --- a/clear-screen +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env sh - -tput reset diff --git a/hotspots b/hotspots deleted file mode 100755 index 8454bac..0000000 --- a/hotspots +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/env bash -# -# Hotspots for Xorg -# - Corners: BL, BR, TL, TR -# - Edges: B, T, L, R -# - Events: onEnter, onExit -# -# Dependencies: xdotool -# -# TODO: -# - better multimonitor support - -# Hotspot size -SIZE=2 - -# Active hotspot dimensions (width, height) -B=(1100 140) -T=(700 "$SIZE") -L=(460 700) -R=(140 700) -BL=(400 400) -BR=("$SIZE" "$SIZE") -TL=(400 300) -TR=("$SIZE" "$SIZE") - -# Variables to store the timestamp when the cursor entered a zone -ENTER_TIMESTAMP=0 - -# Function to reset the timestamp -function resetTimestamp() { - ENTER_TIMESTAMP=0 -} - -# Function to handle onEnter with a delay -function onEnterWithDelay() { - local zone=$1 - local current_time=$(date +%s) - - if ((ENTER_TIMESTAMP == 0)); then - # Cursor has just entered the zone, store the timestamp - ENTER_TIMESTAMP=$current_time - elif ((current_time - ENTER_TIMESTAMP >= 2)); then - # Cursor has been in the zone for 2 seconds or more, trigger onEnter - onEnter "$zone" - resetTimestamp - fi -} - -# Function to handle onEnter event -function onEnter() { - ZONE=$1 - case "$1" in - 'T') eww open status &>/dev/null ;; - #'R') eww open bar &>/dev/null ;; - esac -} - -# Function to handle onExit event -function onExit() { - ZONE='' - case "$1" in - 'T') eww close status &>/dev/null ;; - 'R') eww close bar &>/dev/null ;; - esac -} - -# Get screen dimensions (returns: WIDTH, HEIGHT) -eval "$(xdotool getdisplaygeometry --shell)" - -# Calculate coordinates -BLY=$((HEIGHT - BL[1])) -BRX=$((WIDTH - BR[0])) -BRY=$((HEIGHT - BR[1])) -TRX=$((WIDTH - TR[0])) -BX1=$(((WIDTH - B[0]) / 2)) -BX2=$(((WIDTH + B[0]) / 2)) -BY=$((HEIGHT - B[1])) -LY1=$(((HEIGHT - L[1]) / 2)) -LY2=$(((HEIGHT + L[1]) / 2)) -RX=$((WIDTH - R[0])) -RY1=$(((HEIGHT - R[1]) / 2)) -RY2=$(((HEIGHT + R[1]) / 2)) -H=$((HEIGHT - SIZE)) -W=$((WIDTH - SIZE)) - -# Watch mouse -while :; do - sleep 0.1 - - # Get mouse location (returns: X, Y) - eval "$(xdotool getmouselocation --shell)" - - if [ "$ZONE" = "" ]; then - if [[ "$Y" -ge "$H" ]]; then - if [[ "$X" -lt "$SIZE" ]]; then - onEnterWithDelay BL - elif [[ "$X" -gt "$BX1" && "$X" -lt "$BX2" ]]; then - onEnterWithDelay B - elif [[ "$X" -gt "$W" ]]; then - onEnterWithDelay BR - fi - elif [[ "$Y" -lt "$SIZE" && "$X" -gt $((WIDTH / 3)) && "$X" -lt $((2 * WIDTH / 3)) ]]; then - onEnterWithDelay T - elif [[ "$X" -lt "$SIZE" && "$Y" -gt "$LY1" && "$Y" -lt "$LY2" ]]; then - onEnterWithDelay L - elif [[ "$X" -gt "$W" && "$Y" -gt "$RY1" && "$Y" -lt "$RY2" ]]; then - onEnterWithDelay R - fi - else - case "$ZONE" in - 'B') [[ "$X" -lt "$BX1" || "$X" -gt "$BX2" || "$Y" -lt "$BY" ]] && onExit B ;; - 'L') [[ "$X" -ge "${L[0]}" || "$Y" -lt "$LY1" || "$Y" -gt "$LY2" ]] && onExit L ;; - 'R') [[ "$X" -le "$RX" || "$Y" -lt "$RY1" || "$Y" -gt "$RY2" ]] && onExit R ;; - 'T') [[ "$X" -lt "$BX1" || "$X" -gt "$BX2" || "$Y" -gt "${T[1]}" ]] && onExit T ;; - 'BL') [[ "$X" -ge "${BL[0]}" || "$Y" -lt "$BLY" ]] && onExit BL ;; - 'BR') [[ "$X" -le "$BRX" || "$Y" -lt "$BRY" ]] && onExit BR ;; - 'TL') [[ "$X" -ge "${TL[0]}" || "$Y" -ge "${TL[1]}" ]] && onExit TL ;; - 'TR') [[ "$X" -le "$TRX" || "$Y" -ge "${TR[1]}" ]] && onExit TR ;; - esac - fi -done diff --git a/layer.sh b/layer.sh deleted file mode 100755 index 4b17ed1..0000000 --- a/layer.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -current_layer="$(bspc query -T -n | jq -r '.client.layer')" -case $1 in - +|-) - declare -A _layers=( [below]=0 [normal]=1 [above]=2 ) - layers=( below normal above ) - maxl=$(( ${#layers[@]} - 1 )) - current_layer="$(bspc query -T -n | jq -r '.client.layer')" - i=$(( ${_layers[$current_layer]} $1 1 )) - if [[ $i -lt 0 ]]; then - i=0 - elif [[ $i -gt $maxl ]]; then - i=$maxl - fi - #cycle? nah - #i=$(( (${_layers[$current_layer]} + ${#layers[@]} ${1} 1) % ${#layers[@]} )) - new_layer="${layers[$i]}" - ;; - *) - new_layer="$(bspc query -T -n | jq -r '.client.lastLayer')" - ;; -esac -[[ "$current_layer" != "$new_layer" ]] && bspc node -l "$new_layer" diff --git a/lockscreen-wallpaper b/lockscreen-wallpaper deleted file mode 100755 index f191a7a..0000000 --- a/lockscreen-wallpaper +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -# use nitrogen wallpaper. -wallpaper=$(cat ~/.config/nitrogen/bg-saved.cfg | grep file= | sed 's/file=//') -betterlockscreen -u $wallpaper diff --git a/low-bat-notifier b/low-bat-notifier deleted file mode 100755 index be346f4..0000000 --- a/low-bat-notifier +++ /dev/null @@ -1,66 +0,0 @@ -#!/bin/bash - -### VARIABLES - -POLL_INTERVAL=120 # seconds at which to check battery level -LOW_BAT=33 # lesser than this is considered low battery - -# If BAT0 doesn't work for you, check available devices with command below -# -# $ ls -1 /sys/class/power_supply/ -# -BAT_PATH=/sys/class/power_supply/BAT0 -BAT_STAT=$BAT_PATH/status - -if [[ -f $BAT_PATH/charge_full ]] -then - BAT_FULL=$BAT_PATH/charge_full - BAT_NOW=$BAT_PATH/charge_now -elif [[ -f $BAT_PATH/energy_full ]] -then - BAT_FULL=$BAT_PATH/energy_full - BAT_NOW=$BAT_PATH/energy_now -else - exit -fi - -### END OF VARIABLES - -kill_running() { # stop older instances to not get multiple notifications - local mypid=$$ - - declare pids=($(pgrep -f ${0##*/})) - - for pid in ${pids[@]/$mypid/}; do - kill $pid - sleep 1 - done -} - -launched=0 - -# Run only if battery is detected -if ls -1qA /sys/class/power_supply/ | grep -q BAT -then - - kill_running - - while true - do - bf=$(cat $BAT_FULL) - bn=$(cat $BAT_NOW) - bs=$(cat $BAT_STAT) - - bat_percent=$(( 100 * $bn / $bf )) - - if [[ $bat_percent -lt $LOW_BAT && "$bs" = "Discharging" && $launched -lt 3 ]] - then - notify-send --urgency=critical "$bat_percent% : Low Battery!" - launched=$((launched+1)) - elif [[ "$bs" = "Charging" ]] - then - launched=0 - fi - sleep $POLL_INTERVAL - done -fi diff --git a/move-qemu.sh b/move-qemu.sh deleted file mode 100755 index 7583cff..0000000 --- a/move-qemu.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/bash - -# -#function move_qemu_window { -# find_window_title() { -# hyprctl clients | grep -q "QEMU (nixos) - noVNC — Mozilla Firefox" -# } -# -# # Function to move the window to workspace 3 -# move_window() { -# local window_title="$1" -# if [[ -n $window_title ]]; then -# hyprctl dispatch movetoworkspace 3,title:"$window_title" -# echo "Moved window to workspace 3." -# else -# echo "Failed to find window title." -# fi -# } -# -# # Function to handle socket input -# handle_socket() { -# while read -r line; do -# case "$line" in -# *"QEMU (nixos) - noVNC — Mozilla Firefox"*) -# echo "Socket message received: "$line -# if find_window_title; then -# window_title="^QEMU \(.*\) - noVNC — Mozilla Firefox*" -# move_window ""$window_title -# else -# echo "Failed to find window title." -# fi -# ;; -# *) -# echo "Ignoring socket message: "$line -# ;; -# esac -# done -# } -# -# # Wait for the socket and handle messages -# echo "Waiting for socket messages..." -# socat - "UNIX-CONNECT:/tmp/hypr/"$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock | handle_socket -#} -# -#move_qemu_window - -function move_qemu_window { - find_window_title() { - hyprctl clients | grep -q "QEMU (nixos) - noVNC — Mozilla Firefox" - } - - # Function to move the window to workspace 3 - move_window() { - local window_title="$1" - if [[ -n $window_title ]]; then - hyprctl dispatch movetoworkspace 3,title:"$window_title" - echo "Moved window to workspace 3." - else - echo "Failed to find window title." - fi - } - - # Wait for the window to appear - echo "Waiting for window..." - while true; do - if find_window_title; then - window_title="^QEMU \(.*\) - noVNC — Mozilla Firefox*" - move_window "$window_title" - fi - sleep 1 # Check every second - done -} - -move_qemu_window diff --git a/opacity-change.sh b/opacity-change.sh deleted file mode 100755 index b509936..0000000 --- a/opacity-change.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash -set -eu -[[ -n ${DEBUG:-} ]] && set -x - -#### Example alacritty.yml usage -#key_bindings: -# - { key: N, mods: Control|Shift, action: SpawnNewInstance } -# - { key: O, mods: Control|Shift, command: { program: "opacity-change.sh", args: ["-"] } } -# - { key: P, mods: Control|Shift, command: { program: "opacity-change.sh", args: ["+"] } } - - -operation="${1:-}${2:-}" # Arg #1 & #2 (in case the user misinterpreted a space in the usage), Default '' -step="${operation:1}" # Substring from char index 1 -step="${step:-1}" # Default '1' -operation="${operation:0:1}" # Substring from char index 0 length of 1 -config_file="$HOME/.config/alacritty/alacritty.yml" -config_field="opacity" -tmp_file="/tmp/$(basename $config_file).$(date +%s)" -current_value=$(sed 's/#.*//g; /\b'"$config_field"':/!d; s/.*: \?//' < "$config_file") - -case $operation in -"-") - verb="Decreasing" ;; -"+") - verb="Increasing" ;; -*) - echo "Usage: ${BASH_SOURCE[0]} (-|+)[int]"; exit 255 ;; -esac - -new_value="$(awk '{n=$1+$2/10; print (n<0 ? 0 : n>1 ? 1 : n)}' <<<"$current_value $operation$step")" -echo "$verb $config_field from $current_value to $new_value" >&2 -cp "$config_file" "$tmp_file" -sed "s/\b$config_field:.*/$config_field: $new_value/" "$tmp_file" > "$config_file" - diff --git a/plank.sh b/plank.sh deleted file mode 100755 index f389c3a..0000000 --- a/plank.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -bspc subscribe node_add | while read -r _; do - xdo raise -N Plank -done diff --git a/powermenu b/powermenu deleted file mode 100755 index 7bd913e..0000000 --- a/powermenu +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -# display a power menu to: shutdown, reboot, -# lock, logout, and suspend. This script can be -# executed by clicking on the polybar powermenu module -# or with a keyboard shortcut - -# options to be displayed -shutdown=" Shutdown" -reboot=" Reboot" -lock=" Lock" -logout=" Logout" -suspend=" Suspend" - -uptime=$(uptime -p | sed -e 's/up //g') - -# options passed into variable -options="$shutdown\n$reboot\n$lock\n$logout\n$suspend" - -chosen="$(echo -e "$options" | rofi -theme ~/.config/rofi/styles/powermenu.rasi -lines 5 -dmenu -p "$uptime")" - -case $chosen in -$shutdown) - systemctl poweroff - ;; -$reboot) - systemctl reboot - ;; -$lock) - betterlockscreen --lock dimblur - ;; -$logout) - bspc quit - ;; -$suspend) - systemctl suspend - ;; -esac diff --git a/qemu-helper.sh b/qemu-helper.sh deleted file mode 100755 index 0d38aba..0000000 --- a/qemu-helper.sh +++ /dev/null @@ -1,172 +0,0 @@ -#!/bin/bash - -# Created By: srdusr -# Created On: Wed 02 Aug 2023 16:16:21 PM CAT -# Project: QEMU setup/opener helper wrapper script - -# Set global variables for VM parameters -ram_size="4G" - -# Function to prompt user for VM parameters -function get_vm_parameters() { - read -p "Enter VM name (default: vm): " vm_name - vm_name=${vm_name:-vm} - - # Set the default ISO file path to ~/machines/images - default_iso_path="$HOME/machines/images" - - # Generate completions for ISO and IMG files in the images directory - COMPREPLY=() - local files=$(compgen -G "$default_iso_path/*.{iso,img}" -o plusdirs) - for file in $files; do - COMPREPLY+=("$file") - done - - # Use read with -i and -e options for tab-completion - read -ep "Enter ISO file path (default: $default_iso_path): " -i "$default_iso_path" iso_path - - # Manually expand the ~ to the user's home directory - iso_path=$(eval echo "$iso_path") - - # Validate the user input - while [ ! -f "$iso_path" ]; do - read -ep "Invalid file path. Enter a valid ISO file path: " iso_path - done - - # Check if the selected file is an IMG file - if [[ "$iso_path" == *.img ]]; then - guest_os="windows" - else - guest_os="linux" - fi - - # Show available disk space before asking for disk image size - echo "Available disk space:" - df -h "$vm_images_path" - - read -p "Enter disk image size in GB (default: 10G): " disk_size - disk_size=${disk_size:-10G} - - read -p "Enter RAM size in GB (default: 4G): " ram_size - ram_size=${ram_size:-4G} - - # Check if the RAM size is in the correct format (e.g., "4G") - while ! [[ $ram_size =~ ^[0-9]+[kKmMgGtTpPeE]$ ]]; do - read -p "Invalid RAM size format. Enter RAM size in GB (e.g., 4G): " ram_size - done - - read -p "Enter number of CPU cores (default: 2): " cpu_cores - cpu_cores=${cpu_cores:-2} -} - - -# Function to list available VMs -function list_vms() { - echo "Available VMs:" - for vm_file in "$vm_images_path"/*.qcow2; do - vm=$(basename "$vm_file" .qcow2) - echo " - $vm" - done -} - -# Function to list available ISO and IMG files in the images directory -function list_iso_img_files() { - echo "Available ISO and IMG files in $iso_images_path:" - iso_img_files=() - while IFS= read -r -d $'\0' file; do - iso_img_files+=("$file") - done < <(find "$iso_images_path" -type f \( -iname \*.iso -o -iname \*.img \) -print0) - - for ((i = 0; i < ${#iso_img_files[@]}; i++)); do - echo " $((i + 1)). ${iso_img_files[i]##*/}" - done -} - -# Function to check if VM is already running -function is_vm_running() { - vm_name=$1 - if ps aux | grep -v grep | grep -q "[q]emu-system-x86_64.*$vm_name"; then - return 0 - else - return 1 - fi -} - -# Function to start VM -function start_vm() { - vm_name=$1 - is_vm_running "$vm_name" - if [ $? -eq 0 ]; then - echo "VM '$vm_name' is already running." - return - fi - - # VM parameters - qemu_cmd="qemu-system-x86_64 -enable-kvm -machine type=q35 -m $ram_size -cpu host -smp 2 -vga virtio" - qemu_cmd+=" -device qemu-xhci -device usb-tablet -device usb-kbd -device virtio-net,netdev=user0 -netdev user,id=user0,hostfwd=tcp::5555-:22" - qemu_cmd+=" -cdrom \"$iso_path\" -drive file=\"$vm_images_path/$vm_name.qcow2\",index=0,media=disk,if=virtio" - - if [[ $guest_os == "windows" ]]; then - qemu_cmd+=" -drive file=\"$iso_images_path/virtio-win.iso\",index=3,media=cdrom" - fi - - qemu_cmd+=" -boot menu=on" - qemu_cmd+=" -net nic -net user,hostname=$vm_name -name \"$vm_name\"" - - echo "Starting VM: $vm_name" - eval "$qemu_cmd" -} - -# Main script starts here -vm_images_path="$HOME/machines/vm" -iso_images_path="$HOME/machines/images" - -# Check if directories exist -mkdir -p "$vm_images_path" -mkdir -p "$iso_images_path" - -# List available VMs -list_vms - -# List available ISO and IMG files in the images directory -list_iso_img_files - -# Ask the user if they want to use an existing VM or create a new one -read -p "Do you want to use an existing VM? (y/n): " use_existing_vm -if [[ $use_existing_vm =~ ^[Yy]$ ]]; then - read -p "Enter the name of the existing VM: " existing_vm_name - while [ ! -f "$vm_images_path/$existing_vm_name.qcow2" ]; do - echo "VM '$existing_vm_name' does not exist." - read -p "Enter a valid existing VM name: " existing_vm_name - done - vm_name=$existing_vm_name -else - # Prompt user for VM parameters - get_vm_parameters - - # Check if VM already exists - if [ -f "$vm_images_path/$vm_name.qcow2" ]; then - read -p "VM '$vm_name' already exists. Do you want to start it? (y/n): " start_vm_choice - if [[ $start_vm_choice =~ ^[Yy]$ ]]; then - start_vm "$vm_name" - exit 0 - fi - else - # Create new VM - echo "Creating new VM: $vm_name" - qemu-img create -f qcow2 "$vm_images_path/$vm_name.qcow2" "$disk_size" - start_vm "$vm_name" - exit 0 - fi -fi - -# If an existing VM is selected, ask if the user wants to modify its parameters -read -p "Do you want to modify the VM parameters? (y/n): " modify_vm_params -if [[ $modify_vm_params =~ ^[Yy]$ ]]; then - get_vm_parameters -fi - -# Start the VM -start_vm "$vm_name" - -echo "Script execution completed." diff --git a/random-wall b/random-wall deleted file mode 100755 index bd23983..0000000 --- a/random-wall +++ /dev/null @@ -1,128 +0,0 @@ -#!/bin/bash - -WPDIR="$HOME/pictures/wallpapers" -LAST_WP_FILE="$HOME/.config/random-wall_last_wp.txt" -CURRENT_WP_FILE="$HOME/.config/nitrogen/bg-saved.cfg" - -random=true -apply=true -wpfile="" - -function usage { - local stream exitcode - if [ "$1" -eq 1 ]; then - stream=2 - exitcode=255 - else - stream=1 - exitcode=0 - fi - echo "Usage: $(basename "$0") [-n|--noapply] [-h|--help] [wallpaper_location]" >&"$stream" - echo "If wallpaper location is not given, a random wallpaper from $WPDIR will be chosen" >&"$stream" - exit "$exitcode" -} - -# Check if last wallpaper file exists and get its content -if [ -f "$LAST_WP_FILE" ]; then - LAST_WP=$(<"$LAST_WP_FILE") -else - LAST_WP="" -fi - -# Function to get a random wallpaper from $WPDIR, excluding the last and current wallpapers -function get_random_wallpaper { - local wallpapers - shopt -s nullglob - wallpapers=("$WPDIR"/*.jpg) - if [ ${#wallpapers[@]} -eq 0 ]; then - echo "No wallpapers found in $WPDIR" >&2 - exit 1 - fi - - # If there is only one wallpaper, return it - if [ ${#wallpapers[@]} -eq 1 ]; then - echo "${wallpapers[0]}" - return - fi - - local exclude=() - if [ "$LAST_WP" != "" ]; then - exclude+=("$LAST_WP") - fi - if [ "$CURRENT_WP" != "" ]; then - exclude+=("$CURRENT_WP") - fi - - local random_wallpaper - while true; do - random_wallpaper="${wallpapers[RANDOM % ${#wallpapers[@]}]}" - if [[ ! " ${exclude[@]} " =~ " ${random_wallpaper} " ]]; then - echo "$random_wallpaper" - return - fi - done -} - -# handle arguments -while [ $# -gt 0 ]; do - case "$1" in - "--help" | "-h") - usage 0 - ;; - "--noapply" | "-n") - apply=false - ;; - *) - if ! "$random"; then - usage 1 - elif [ ! -f "$1" ]; then - echo "File '$1' not found" >&2 - exit 1 - fi - random=false - wpfile="$1" - ;; - esac - shift -done - -# Choose a random wallpaper if necessary -if "$random"; then - wpfile=$(get_random_wallpaper) - echo "Chose: $wpfile" >&2 -fi - -# Set the desktop wallpaper -cat >"$HOME/.config/nitrogen/bg-saved.cfg" <<EOF -[xin_-1] -file=$wpfile -mode=4 -bgcolor=#000000 -EOF - -# Set the lock screen wallpaper -"$HOME/.scripts/lockscreen-wallpaper" "$wpfile" & - -# Set the LightDM wallpaper -cp "$wpfile" "/usr/share/lightdm-webkit/themes/glorious/assets/bg.jpg" - -# Save the last chosen wallpaper -echo "$wpfile" >"$LAST_WP_FILE" - -# Sync wallpaper using AccountsService D-Bus -if [ "$wpfile" != "" ]; then - dbus-send \ - --print-reply \ - --system \ - --dest=org.freedesktop.Accounts \ - /org/freedesktop/Accounts/User"$(id -u)" \ - org.freedesktop.DBus.Properties.Set \ - string:org.freedesktop.DisplayManager.AccountsService \ - string:BackgroundFile \ - variant:string:"$wpfile" -fi - -# Apply the desktop wallpaper if requested -if "$apply"; then - nitrogen --restore -fi diff --git a/random_data.py b/random_data.py deleted file mode 100755 index 071ab7c..0000000 --- a/random_data.py +++ /dev/null @@ -1,153 +0,0 @@ -#!/usr/bin/env python3 - -import os -import random -import string -import json -import datetime -import csv - - -def generate_random_string(length, charset=string.ascii_letters): - """Generate a random string of given length and character set.""" - return ''.join(random.choice(charset) for _ in range(length)) - - -def generate_random_number(min_value, max_value): - """Generate a random number within the specified range.""" - return random.randint(min_value, max_value) - - -def generate_random_date(start_date, end_date): - """Generate a random date within the specified range.""" - time_between_dates = end_date - start_date - days_between_dates = time_between_dates.days - random_number_of_days = random.randrange(days_between_dates) - random_date = start_date + datetime.timedelta(days=random_number_of_days) - return random_date.strftime("%Y-%m-%d") - - -def generate_sql_insert(table_name, columns, num_records): - """Generate SQL INSERT statements for populating a table.""" - sql_statements = [] - for _ in range(num_records): - values = [f"'{generate_random_string(int(input(f'Enter length for {column}: ')))}'" for column in columns] - sql = f"INSERT INTO {table_name} ({', '.join(columns)}) VALUES ({', '.join(values)});" - sql_statements.append(sql) - return sql_statements - - -def generate_placeholder_data(num_records, data_format): - """Generate placeholder data based on user-defined format.""" - placeholder_data = [] - for _ in range(num_records): - record = {} - for field, field_data in data_format.items(): - data_type = field_data['type'] - if data_type == 'string': - length = field_data['length'] - charset = field_data['charset'] - record[field] = generate_random_string(length, charset) - elif data_type == 'number': - min_value = field_data['min'] - max_value = field_data['max'] - record[field] = generate_random_number(min_value, max_value) - elif data_type == 'date': - start_date = datetime.datetime.strptime(field_data['start_date'], "%Y-%m-%d") - end_date = datetime.datetime.strptime(field_data['end_date'], "%Y-%m-%d") - record[field] = generate_random_date(start_date, end_date) - elif data_type == 'boolean': - record[field] = random.choice([True, False]) - placeholder_data.append(record) - return placeholder_data - - -def get_data_format_from_user(): - """Prompt the user for data format preferences.""" - data_format = {} - while True: - field = input("Enter field name (or 'done' to finish): ").strip() - if field.lower() == 'done': - break - - data_type = input(f"Enter data type for '{field}' (string/number/date/boolean): ").strip() - if data_type not in ['string', 'number', 'date', 'boolean']: - print("Invalid data type. Please enter 'string', 'number', 'date', or 'boolean'.") - continue - - if data_type == 'string': - length = int(input(f"Enter length for '{field}' (integer): ")) - charset = input(f"Enter character set for '{field}' (optional, press Enter for default): ").strip() - if not charset: - charset = string.ascii_letters - data_format[field] = {'type': 'string', 'length': length, 'charset': charset} - elif data_type == 'number': - min_value = int(input(f"Enter minimum value for '{field}' (integer): ")) - max_value = int(input(f"Enter maximum value for '{field}' (integer): ")) - data_format[field] = {'type': 'number', 'min': min_value, 'max': max_value} - elif data_type == 'date': - start_date = input(f"Enter start date for '{field}' (YYYY-MM-DD): ") - end_date = input(f"Enter end date for '{field}' (YYYY-MM-DD): ") - data_format[field] = {'type': 'date', 'start_date': start_date, 'end_date': end_date} - elif data_type == 'boolean': - data_format[field] = {'type': 'boolean'} - - return data_format - - -def get_file_type_from_user(): - """Prompt the user for the desired file type (e.g., JSON, CSV, SQL, TXT, MD, HTML).""" - while True: - file_type = input("Enter the desired file type for saving the data (json/csv/sql/txt/md/html): ").strip().lower() - if file_type in ['json', 'csv', 'sql', 'txt', 'md', 'html']: - return file_type - else: - print("Invalid file type. Please enter 'json', 'csv', 'sql', 'txt', 'md', or 'html'.") - - -def save_data_to_file(data, file_type): - """Save the generated data to a file of the specified type.""" - if file_type == 'json': - with open('placeholder_data.json', 'w') as json_file: - json.dump(data, json_file, indent=4) - elif file_type == 'csv': - with open('placeholder_data.csv', 'w', newline='') as csv_file: - fieldnames = data[0].keys() - writer = csv.DictWriter(csv_file, fieldnames=fieldnames) - writer.writeheader() - for record in data: - writer.writerow(record) - elif file_type == 'sql': - table_name = input("Enter the SQL table name: ") - columns = input("Enter column names separated by commas: ").split(',') - sql_statements = generate_sql_insert(table_name, columns, len(data)) - with open('generated_data.sql', 'w') as sql_file: - sql_file.write('\n'.join(sql_statements)) - elif file_type == 'txt': - with open('placeholder_data.txt', 'w') as txt_file: - for record in data: - txt_file.write(str(record) + '\n') - elif file_type == 'md': - with open('placeholder_data.md', 'w') as md_file: - for record in data: - md_file.write('- ' + ', '.join([f"{key}: {value}" for key, value in record.items()]) + '\n') - elif file_type == 'html': - with open('placeholder_data.html', 'w') as html_file: - html_file.write('<html>\n<head>\n<title>Placeholder Data</title>\n</head>\n<body>\n') - for record in data: - html_file.write('<ul>\n') - for key, value in record.items(): - html_file.write(f'<li>{key}: {value}</li>\n') - html_file.write('</ul>\n') - - -if __name__ == "__main__": - num_records = int(input("Enter the number of records to generate: ")) - data_format = get_data_format_from_user() - file_type = get_file_type_from_user() - - placeholder_data = generate_placeholder_data(num_records, data_format) - - save_data_to_file(placeholder_data, file_type) - - print(f"Data will be saved to: {os.path.abspath('generated_data.sql')}") diff --git a/root.sh b/root.sh deleted file mode 100755 index 7706bce..0000000 --- a/root.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/bash - -# Define the source base directory -BASE_DIR="$HOME/extras" - -# Check if the script is running with superuser privileges -if [ "$EUID" -ne 0 ]; then - echo "Please run as root or with sudo" - exit 1 -fi - -# Function to backup existing files -backup_existing() { - local dest=$1 - if [ -e "$dest" ]; then - echo "Backing up $dest" - mv "$dest" "$dest.bak" - fi -} - -# Function to copy directories, backup, and change permissions -copy_and_set_permissions() { - local src=$1 - local dest=$2 - - if [ -d "$src" ]; then - echo "Processing directory $src" - - for file in "$src"/*; { - dest_file="$dest/$(basename "$file")" - - backup_existing "$dest_file" - - echo "Copying $file to $dest" - cp -rp "$file" "$dest" - - echo "Setting permissions for $dest_file" - chown root:root "$dest_file" - chmod 644 "$dest_file" - } - else - echo "Source directory $src does not exist." - fi -} - -# Iterate over all directories in the extras directory -for dir in "$BASE_DIR"/*; do - if [ -d "$dir" ]; then - dest_dir="/${dir##*/}" - copy_and_set_permissions "$dir" "$dest_dir" - fi -done - -echo "Files copied and permissions set successfully." diff --git a/screenshot b/screenshot deleted file mode 100755 index 25fc3bd..0000000 --- a/screenshot +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -DATE="$(date +'%d-%m-%y_%H:%M:%S')" -DIR="$HOME/pictures/screenshots" -PIC="$DIR/$DATE.jpg" - -[ ! -d "$DIR" ] && mkdir -pv "$DIR" - -abort() { - notify-send -a "Warn" -i dialog-error "Screenshot" "aborted" - exit 1 -} - -notification() { - NOTIFY=$(notify-send -A open=Open -A delete=Delete -a Screenshot -i "$PIC" "Screenshot" "$PIC") - if [[ $NOTIFY == "open" ]]; then - viewnior "$PIC" - elif [[ $NOTIFY == "delete" ]]; then - rm -rf "$PIC" - else - exit 0 - fi -} - -case "$1" in - full) - flameshot full -p "$PIC" || abort - #flameshot full -p ~/pictures/screenshots - xclip -selection primary <"$PIC" - notification - ;; - crop) - flameshot gui -p "$PIC" || abort - #flameshot gui -p ~/pictures/screenshots - xclip -selection primary <"$PIC" - notification - ;; -esac diff --git a/session b/session deleted file mode 100755 index c798403..0000000 --- a/session +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -# Check if the desired session is provided as an argument -if [ "$1" ]; then - case "$1" in - bspwm | sway | Hyprland) - echo "$1" >~/.session - echo "Session choice set to: $1" - ;; - *) - echo "Unsupported session: $1" - ;; - esac - - # Kill the current X session or logout the user - pkill -KILL -u "$USER" -else - echo "Usage: switch_session.sh <session_name>" - echo "Example: switch_session.sh bspwm" - exit 1 -fi @@ -1,73 +0,0 @@ -#!/usr/bin/env bash - -# Created By: srdusr -# Created On: Wed 18 Oct 2023 20:19:03 CAT -# Project: Create Spectrograms of audio files - -# Dependencies: sox - -# Define the timestamp function -timestamp() { - date +%Y%m%d%H%M%S -} - -spec() { - - if [[ $# -eq 0 ]]; then - echo "No audio files provided." - return - fi - - local outdir - - if [[ -d "$HOME/pictures" ]]; then - outdir="$HOME/pictures/spectrograms" - elif [[ -d "$HOME/Pictures" ]]; then - outdir="$HOME/Pictures/Spectrograms" - elif [[ -d "$HOME/Images" ]]; then - outdir="$HOME/Images/Spectrograms" - else - outdir="./spectrograms" # Save to the current directory if none of the expected directories exist - fi - - for file in "$@"; do - if [[ -f "$file" ]]; then - local filename - filename=$(basename "$file") - local filename_no_extension="${filename%.*}" - local target_dir="$outdir" - local outname="$target_dir/sox-spec-$(timestamp)-${filename_no_extension}.png" - - if [[ ! -d "$target_dir" ]]; then - mkdir -p "$target_dir" # Create the directory if it doesn't exist - fi - - sox "$file" -S -n spectrogram -o "$outname" - - # Add the generated spectrogram file name to the array - spectrogram_files+=("$outname") - else - echo "File not found: $file" - fi - done - - if [[ ${#} -gt 0 ]]; then - read -p "Do you want to open the spectrogram(s)? (y/n): " open_choice - case "$open_choice" in - [Yy]) - for file in "${spectrogram_files[@]}"; do - xdg-open "$file" # Open the spectrogram images generated from the provided audio files - done - ;; - [Nn]) - echo "Not opening the spectrogram(s)." - ;; - *) - echo "Invalid choice. Not opening the spectrogram(s)." - ;; - esac - fi -} - -# Call the spec function with provided arguments -spec "$@" diff --git a/systray b/systray deleted file mode 100755 index 0fbc768..0000000 --- a/systray +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash - -hidden=/tmp/syshide.lock -file="$HOME/.config/polybar/config.ini" - -if [[ ! -e $hidden ]]; then - polybar-msg action "#systray.hook.1" - xdo hide -n stalonetray - touch "$hidden" - sed -i 's/systray\ninitial=.*/systray\ninitial=2/g' "$file" -else - polybar-msg action "#systray.hook.0" - xdo show -n stalonetray - xdo raise -n stalonetray - rm "$hidden" - sed -i 's/systray\ninitial=.*/systray\ninitial=1/g' "$file" -fi diff --git a/test/.editorconfig b/test/.editorconfig deleted file mode 100644 index db601e5..0000000 --- a/test/.editorconfig +++ /dev/null @@ -1,14 +0,0 @@ - -root = true - - -[*] -charset = utf-8 -indent_style = tab -indent_size = 4 -max_line_length = 10000 - -# Python config -[**.py] -max_line_length = 10000 -indent_size = 4 diff --git a/test/unhidewindow b/test/unhidewindow deleted file mode 100755 index 7e276df..0000000 --- a/test/unhidewindow +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/env bash -# dependency: xtitle - -ids=($(bspc query -N -n .hidden.window)) || exit -options="$(xtitle "${ids[@]}")" - -id_index="$(<<< "$options" rofi -dmenu -format i)" -if [[ -n "${id_index}" ]]; then - bspc node "${ids[${id_index}]}" -g hidden=off -f -fi diff --git a/title-bar b/title-bar deleted file mode 100755 index 596d426..0000000 --- a/title-bar +++ /dev/null @@ -1,137 +0,0 @@ -#!/bin/bash - -# Lock file for preventing multiple instances -lockfile="/tmp/title-bar.lock" - -# Check if the lock file exists and obtain the PID -if [ -e "$lockfile" ]; then - # If the lock file exists, check if the process is still running - lock_pid=$(cat "$lockfile") - if ps -p "$lock_pid" >/dev/null; then - echo "title-bar is already running. Exiting." - exit 1 - else - # If the process is not running, remove the stale lock file - rm "$lockfile" - fi -fi - -# Create a new lock file with the current PID -echo "$$" >"$lockfile" - -# Debug log file -DEBUG_LOG="/tmp/title-bar_debug.log" - -# Debug function -debug_echo() { - echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >>"$DEBUG_LOG" -} - -debug_echo "Script execution started." - -# Background image path -bg_path="/tmp/bg_${bgw}.png" - -# Function to create background images -create_background_image() { - echo -e "\e[48;2;255;165;0m" # Orange color -} - -# Kill existing lemonbar instances and update script -pkill -f "lemonbar" -pkill -f "update-title" - -# Get the focused window information -focused_info="$(bspc query -T --names -M)" - -# Get the focused window ID -focused_id="$(bspc query -N -n focused)" - -# Check if the window ID is empty -if [ "$focused_id" = "" ]; then - echo "No focused window found." - exit -fi - -# Check if the focused window is in fullscreen mode -if bspc query -T -n "$focused_id" | grep -q '"state":"fullscreen"'; then - echo "Focused window is in fullscreen mode. Exiting." - rm -f "$lockfile" # Remove the lock file - exit -fi - -# Get the WM_CLASS of the focused window -focused_class="$(xprop WM_CLASS -id "$focused_id" | awk -F '"' '{print $2}')" - -debug_echo "Focused Class: $focused_class" - -# Exclude certain window classes -case "$focused_class" in - "Nwg-menu" | "nwg-menu" | "Steam" | "Qmmp" | "Wrapper-1.0" | "xfce4-panel") - debug_echo "Excluded window class: $focused_class. Exiting script." - rm -f "$lockfile" # Remove the lock file - exit - ;; -esac - -# Get focused window geometry -read -r x y w h < <(xwininfo -id "$focused_id" | - awk '/Absolute upper-left X:/ { x=$4 } - /Absolute upper-left Y:/ { y=$4 } - /Width:/ { w=$2 } - /Height:/ { h=$2 } -END { print x, y, w, h }') - -#geoBar=$(echo "$w 13 $x $y" | awk '{print $1+4"x13+"$3"+"$4-8}') -#geoBar=$(echo "$w 13 $x $y" | awk '{print $1-7"x10+"$3+5"+"$4-4}') -geoBar=$(echo "$w 13 $x $y" | awk '{print $1+7"x10+"$3"+"$4}') - -# Create background image based on window width -create_background_image - -# Launch lemonbar with consistent background color -echo -e "%{c}%{I:${bg_path}:}%{l}%{I#FF000000:$HOME/.scripts/assets/corner.png:}%{r}%{I:$HOME/.scripts/assets/corner.png:}" | -lemonbar -d -p -B "#000000" -U "#FFFFFF" -F "#FFFFFF" -f "Jetbrains Mono:bold:size=10" -g "$geoBar" & -#lemonbar -d -p -B "#FFFFFF" -U "#000000" -F "#000000" -f "Jetbrains Mono:bold:size=10" -g "$geoBar" & -#lemonbar -d -p -B "$HOME/.scripts/assets/trans.png" -U "#77dd77" -F "#4e585d" -f "Terminus:size=1" -g "$geoBar" & -sleep 0.05 - -# Launch update-title for the focused window -~/.scripts/update-title "$focused_id" | lemonbar -d -g "$geoBar" -f "Jetbrains Mono:bold:size=6" -f "Jetbrains Mono:bold:size=6" -p | bash & - -# Wait for lemonbar to start -sleep 0.1 - -# Adjust stacking order for the focused window -barinfos="$(xwininfo -tree -root | grep "$focused_id")" -bgid="$(echo "$barinfos" | tail -n 1 | awk '{print $1}')" -xdo below -t "$focused_id" "$bgid" -xdo above -t "$bgid" "$(echo "$barinfos" | head -n 1 | awk '{print $1}')" - -debug_echo "Script execution completed." - -# Function to handle updates triggered by BSPWM events -handle_bspwm_events() { - while read -r line; do - event_type=$(echo "$line" | jq -r '.type') - - case "$event_type" in - focus_changed) - focused_node=$(echo "$line" | jq -r '.node') - ~/.scripts/update-title "$focused_node" | lemonbar -p -g "$geoBar" -f "Jetbrains Mono:size=6" -f "Jetbrains Mono:size=6" | bash & - ;; - node_add | node_remove | node_flag | node_geometry) - # Handle other node-related events - ;; - *) - # Handle other events as needed - ;; - esac - done -} - -# Subscribe to BSPWM events and pass them to the handler function -bspc subscribe | handle_bspwm_events & - -# Remove the lock file when the script exits -trap 'rm -f "$lockfile"; pkill -f "title-bar"; exit 0' EXIT diff --git a/toggle-control b/toggle-control deleted file mode 100755 index 8de5200..0000000 --- a/toggle-control +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/bash - -EWW=$(which eww) -STATE_FILE="$HOME/.eww_module_state" - -# Function to close the control module -close_control_module() { - "$EWW" close control & -} - -# Function to open the control module -open_control_module() { - touch "$STATE_FILE" - "$EWW" open control & -} - -# Function to check if the cursor is inside the control window -is_cursor_inside_control() { - local mouse_x=$(xdotool getmouselocation --shell | awk -F '=' '/X/{print $2}') - local mouse_y=$(xdotool getmouselocation --shell | awk -F '=' '/Y/{print $2}') - - local control_x=1110 - local control_y=0 - local control_width=240 - local control_height=360 - - [ "$mouse_x" -ge "$control_x" -a "$mouse_x" -le "$((control_x + control_width))" -a \ - "$mouse_y" -ge "$control_y" -a "$mouse_y" -le "$((control_y + control_height))" ] -} - -# Function to close the control module if cursor is outside the control window -close_control_on_leave() { - while true; do - sleep 0.1 - if [ -e "$STATE_FILE" ] && ! is_cursor_inside_control; then - close_control_module - rm "$STATE_FILE" - break - fi - done -} - -# Check if the module is currently running -if [ -e "$STATE_FILE" ]; then - # If the file exists, the module is running, so close it when cursor leaves - close_control_on_leave -else - # If the file doesn't exist, the module is not running, so start it - open_control_module - - # Start monitoring cursor position to close the control window if it leaves - close_control_on_leave & -fi diff --git a/unix/sys/battery_alert.sh b/unix/sys/battery_alert.sh new file mode 100755 index 0000000..47fa556 --- /dev/null +++ b/unix/sys/battery_alert.sh @@ -0,0 +1,43 @@ +#!/bin/sh + +# Send a notification if the laptop battery is either low or is fully charged. + +# Battery percentage at which to notify +WARNING_LEVEL=78 +CRITICAL_LEVEL=5 +BATTERY_DISCHARGING=$(acpi -b | grep "Battery 0" | grep -c "Discharging") +BATTERY_LEVEL=$(acpi -b | grep "Battery 0" | grep -P -o '[0-9]+(?=%)') + +# Use files to store whether we've shown a notification or not (to prevent multiple notifications) +FULL_FILE=/tmp/batteryfull +EMPTY_FILE=/tmp/batteryempty +CRITICAL_FILE=/tmp/batterycritical + +# Reset notifications if the computer is charging/discharging +if [ "$BATTERY_DISCHARGING" -eq 1 ]; then + # Battery is discharging + if [ -f "$FULL_FILE" ]; then + command rm "$FULL_FILE" + fi + if [ "$BATTERY_LEVEL" -le "$WARNING_LEVEL" ] && [ ! -f "$EMPTY_FILE" ]; then + notify-send "Low Battery" "${BATTERY_LEVEL}% of battery remaining." -u critical -i "battery-low-symbolic" -r 9991 + touch "$EMPTY_FILE" + fi + if [ "$BATTERY_LEVEL" -le "$CRITICAL_LEVEL" ] && [ ! -f "$CRITICAL_FILE" ]; then + notify-send "Battery Critical" "The computer will shut down soon." -u critical -i "battery-caution-symbolic" -r 9991 + touch "$CRITICAL_FILE" + fi +elif [ "$BATTERY_DISCHARGING" -eq 0 ]; then + # Battery is charging + if [ "$BATTERY_LEVEL" -gt 99 ] && [ ! -f "$FULL_FILE" ]; then + notify-send "Battery Charged" "Battery is fully charged." -i "battery-full-symbolic" -r 9991 + touch "$FULL_FILE" + fi + # Reset empty/critical notifications when charging begins + if [ -f "$EMPTY_FILE" ]; then + command rm "$EMPTY_FILE" + fi + if [ -f "$CRITICAL_FILE" ]; then + command rm "$CRITICAL_FILE" + fi +fi diff --git a/gsettings.sh b/unix/sys/gsettings.sh index 0cd28c2..9054344 100755 --- a/gsettings.sh +++ b/unix/sys/gsettings.sh @@ -25,3 +25,16 @@ gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/or # Disable keyboard plugin gsettings set org.gnome.settings-daemon.plugins.keyboard active false + +# Prefer dark mode +gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark' + +# Default terminal +gsettings set org.cinnamon.desktop.default-applications.terminal exec wezterm + +# Disable screensaver +gsettings set org.gnome.desktop.session idle-delay 0 +gsettings set org.gnome.desktop.screensaver lock-enabled false + +# Hack +#gsettings set org.gnome.desktop.app-folders folder-children "['Red', 'SysAdmin', 'Blue']" diff --git a/unix/sys/keep_guest_awake.sh b/unix/sys/keep_guest_awake.sh new file mode 100755 index 0000000..13d839b --- /dev/null +++ b/unix/sys/keep_guest_awake.sh @@ -0,0 +1,84 @@ +#!/bin/bash + +# Host-side script to keep QEMU guest display awake without triggering anything +# and handle Windows key logic (disable it in the guest, map Caps Lock to Windows key). + +MONITOR_SOCKET_DIR="$HOME/machines/vm" +QEMU_CLASS="qemu-system-x86_64" +SEND_CMD_MONITOR="sendkey f15" +SEND_CMD_XDO="key --clearmodifiers F15" +DISABLE_WINDOWS_KEY_CMD="sendkey meta_l NoSymbol" # Using ctrl_l as a placeholder +CAPS_LOCK_CMD="sendkey meta_l" # Remapped Caps Lock to Super_L (Windows key) + +## Function to disable the Windows key and remap Caps Lock to Super_L +#disable_windows_key_and_remap_caps_lock() { +# shopt -s nullglob +# sockets=("$MONITOR_SOCKET_DIR"/*.socket) +# shopt -u nullglob +# +# for socket in "${sockets[@]}"; do +# # Get VM name from socket file +# VM_NAME=$(basename "$socket" .socket) +# VM_NAME=${VM_NAME%-serial} +# +# # Send QMP command to disable Windows key (Super_L key press/remap) +# qmp_command='{"execute": "device_key_press", "arguments": {"dev": "virtio-keyboard-pci", "key": "capslock"}}' +# echo "$qmp_command" | socat - "UNIX-CONNECT:$socket" >/dev/null +# +# # Send QMP command to remap Caps Lock to Super_L (Windows key) +# qmp_command='{"execute": "guest-execute", "arguments": {"path": "/usr/bin/xmodmap", "arg": ["-e", "keycode 66 = Super_L"]}}' +# echo "$qmp_command" | socat - "UNIX-CONNECT:$socket" >/dev/null +# +# # Optional: Disable Super_L key (Windows key) +# qmp_command='{"execute": "guest-execute", "arguments": {"path": "/usr/bin/xmodmap", "arg": ["-e", "keycode 133 = NoSymbol"]}}' +# echo "$qmp_command" | socat - "UNIX-CONNECT:$socket" >/dev/null +# done +#} + +# Function to keep the guest display awake by sending F15 key press +keep_guest_display_awake() { + shopt -s nullglob + sockets=("$MONITOR_SOCKET_DIR"/*.socket) + shopt -u nullglob + + if [ ${#sockets[@]} -eq 0 ]; then + sleep 30 + return + fi + + focused_qemu=false + + if command -v xdotool >/dev/null 2>&1; then + active_win_id=$(xdotool getwindowfocus 2>/dev/null) + if [ "$active_win_id" != "" ]; then + active_class=$(xdotool getwindowclassname "$active_win_id" 2>/dev/null) + if [[ "$active_class" == "$QEMU_CLASS" ]]; then + focused_qemu=true + fi + fi + fi + + if "$focused_qemu"; then + # QEMU is focused → send F15 via xdotool + #xdotool "$SEND_CMD_XDO" + echo "" + + else + # QEMU is not focused → send F15 via monitor + for socket in "${sockets[@]}"; do + echo "$SEND_CMD_MONITOR" | socat - "UNIX-CONNECT:$socket" + done + fi +} + +# Main loop +while true; do + # Handle Windows key remapping and Caps Lock disablement + #disable_windows_key_and_remap_caps_lock + + # Keep the guest display awake + keep_guest_display_awake + + # Sleep before next cycle + sleep 30 +done diff --git a/unix/sys/low-bat-notifier b/unix/sys/low-bat-notifier new file mode 100755 index 0000000..a67b25d --- /dev/null +++ b/unix/sys/low-bat-notifier @@ -0,0 +1,79 @@ +#!/bin/bash + +### VARIABLES + +POLL_INTERVAL=120 # seconds at which to check battery level +LAST_NOTIFIED=-1 # track last notified battery percentage to avoid repeated notifications + +# If BAT0 doesn't work for you, check available devices with: +# $ ls -1 /sys/class/power_supply/ +BAT_PATH=/sys/class/power_supply/BAT0 +BAT_STAT=$BAT_PATH/status + +if [[ -f $BAT_PATH/charge_full ]]; then + BAT_FULL=$BAT_PATH/charge_full + BAT_NOW=$BAT_PATH/charge_now +elif [[ -f $BAT_PATH/energy_full ]]; then + BAT_FULL=$BAT_PATH/energy_full + BAT_NOW=$BAT_PATH/energy_now +else + exit +fi + +kill_running() { + local mypid=$$ + local pids + mapfile -t pids < <(pgrep -f "${0##*/}") + + for pid in "${pids[@]}"; do + if [[ $pid -ne $mypid ]]; then + kill "$pid" + sleep 1 + fi + done +} + +get_battery_icon() { + local percent=$1 + if ((percent > 80)); then + echo "🔋" # Full battery + elif ((percent > 60)); then + echo "🔋" # High battery + elif ((percent > 40)); then + echo "🔋" # Medium battery + elif ((percent > 20)); then + echo "🪫" # Low battery + else + echo "⚠️" # Critical battery + fi +} + +# Run only if battery is detected +if ls -1qA /sys/class/power_supply/ | grep -q BAT; then + + kill_running + + while true; do + bf=$(cat "$BAT_FULL") + bn=$(cat "$BAT_NOW") + bs=$(cat "$BAT_STAT") + + bat_percent=$((100 * $bn / $bf)) + bat_icon=$(get_battery_icon "$bat_percent") + + # Notify only at exact levels: 20%, 15%, and 10% + if [[ ($bat_percent -eq 20 || $bat_percent -eq 15 || $bat_percent -eq 10) && "$bs" = "Discharging" && $bat_percent -ne $LAST_NOTIFIED ]]; then + notify-send --urgency=critical "$bat_icon $bat_percent% : $([[ $bat_percent -eq 10 ]] && echo 'Critical Battery! Shutting down in 1 minute...' || echo 'Low Battery!')" + LAST_NOTIFIED=$bat_percent + + if [[ $bat_percent -eq 10 ]]; then + sleep 60 + shutdown now + fi + elif [[ "$bs" = "Charging" ]]; then + LAST_NOTIFIED=-1 + fi + + sleep "$POLL_INTERVAL" + done +fi diff --git a/session_manager.sh b/unix/sys/session_manager.sh index b6a6b03..b6a6b03 100755 --- a/session_manager.sh +++ b/unix/sys/session_manager.sh diff --git a/unix/sys/shutdown_watchdog b/unix/sys/shutdown_watchdog new file mode 100755 index 0000000..79bc7bf --- /dev/null +++ b/unix/sys/shutdown_watchdog @@ -0,0 +1,11 @@ +#!/bin/sh + +LOGFILE="/tmp/shutdown_watchdog.log" + +while true; do + echo "--- $(date) ---" >> "$LOGFILE" + uptime >> "$LOGFILE" + sensors >> "$LOGFILE" 2>/dev/null + acpi -V >> "$LOGFILE" 2>/dev/null + sleep 10 +done diff --git a/unix/utils/backlight_default.sh b/unix/utils/backlight_default.sh new file mode 100755 index 0000000..b79e680 --- /dev/null +++ b/unix/utils/backlight_default.sh @@ -0,0 +1,32 @@ +#!/bin/sh +set -e + +backlight_sys_dir="/sys/class/backlight/intel_backlight" + +read -r max_brightness < "${backlight_sys_dir}/max_brightness" +read -r curr_brightness < "${backlight_sys_dir}/brightness" + +if ! groups | grep -q backlight; then + echo "User is not in the backlight group" + exit 1 +fi + +if [ "$#" -eq 0 ] ; then + # set to half that of 'max_brightness' + echo $((max_brightness / 2)) > "$backlight_sys_dir"/brightness + exit 0 +fi + +case "$1" in + up) increment="+ 10" ;; + down) increment="- 10" ;; + *) exit 1 ;; +esac + +new_brightness=$(($curr_brightness $increment)) + +if $((new_brightness < 1)) || $((new_brightness > $max_brightness)); then + exit 1 +else + echo "$new_brightness" > "$backlight_sys_dir"/brightness +fi diff --git a/colors.sh b/unix/utils/colors.sh index fc1c10c..fc1c10c 100755 --- a/colors.sh +++ b/unix/utils/colors.sh diff --git a/cryptocheck b/unix/utils/cryptocheck index 02ba42d..02ba42d 100755 --- a/cryptocheck +++ b/unix/utils/cryptocheck diff --git a/cryptonotify b/unix/utils/cryptonotify index 47883c3..47883c3 100755 --- a/cryptonotify +++ b/unix/utils/cryptonotify diff --git a/unix/utils/disable_meta_key_guest.sh b/unix/utils/disable_meta_key_guest.sh new file mode 100755 index 0000000..acbbdf5 --- /dev/null +++ b/unix/utils/disable_meta_key_guest.sh @@ -0,0 +1,94 @@ +ain "$@" +#!/usr/bin/env bash + +# Function to find the QEMU monitor socket dynamically and extract the VM name +get_socket_path() { + local socket_dir="$HOME/machines/vm" # Directory where your sockets are stored + local socket_file=$(find "$socket_dir" -type s -name "*-monitor.socket" | head -n 1) + + echo "[DEBUG] Found socket file: $socket_file" # Debugging line to check the socket file + + if [[ -z "$socket_file" ]]; then + echo "Error: No QEMU monitor socket found in $socket_dir." >&2 + exit 1 + fi + + # Extract the VM name from the socket file name + local vm_name=$(basename "$socket_file" -monitor.socket) + echo "[DEBUG] VM name detected: $vm_name" # Debugging line to check VM name + + echo "$socket_file" # Return the full socket path + echo "$vm_name" # Return the VM name +} + +# Function to check if sendkeys.awk is in the user's PATH or fall back to specific directories +find_sendkeys_awk() { + # Check if sendkeys.awk is in the user's PATH + if command -v sendkeys.awk &>/dev/null; then + echo "$(command -v sendkeys.awk)" + return 0 + fi + + # Otherwise, check in specific fallback directories + local possible_paths=( + "$HOME/.scripts/env/linux/utils/sendkeys.awk" + "$HOME/.scripts/sendkeys.awk" + ) + + for path in "${possible_paths[@]}"; do + if [[ -f "$path" ]]; then + echo "$path" + return 0 + fi + done + + echo "sendkeys.awk not found in the user's PATH or known directories." >&2 + exit 1 +} + +send_guest_command() { + local cmd="$1" + local socket="$2" + echo "$cmd" | awk -f "$SENDKEYS_AWK" | socat - UNIX-CONNECT:"$socket" +} + +main() { + # Get the QEMU socket and VM name dynamically + SOCKET=$(get_socket_path) + VM_NAME=$(basename "$SOCKET" -monitor.socket) + + # Debugging output to verify the socket and VM name + echo "[DEBUG] Using socket: $SOCKET" + echo "[DEBUG] VM name: $VM_NAME" + + # If no socket file is found, exit + if [[ ! -S "$SOCKET" ]]; then + echo "Error: QEMU monitor socket for $VM_NAME does not exist or is not available." + exit 1 + fi + + # Try to find the sendkeys.awk file + SENDKEYS_AWK=$(find_sendkeys_awk) + + echo "[*] Attempting Caps Lock to Super remapping on all known platforms..." + + # === X11 === + send_guest_command "setxkbmap -option caps:super" "$SOCKET" + send_guest_command "xmodmap -e 'remove Mod4 = Super_L Super_R'" "$SOCKET" + send_guest_command "xmodmap -e 'keycode 133 = NoSymbol'" "$SOCKET" + send_guest_command "xmodmap -e 'keycode 134 = NoSymbol'" "$SOCKET" + + # === Wayland (note: this just gives a reminder) === + send_guest_command "gsettings set org.gnome.desktop.input-sources xkb-options \"['caps:super']\"" "$SOCKET" + send_guest_command "echo 'Wayland? Try remapping via wlroots/wlr-keyboard'" "$SOCKET" + + # === Windows (registry scancode map) === + send_guest_command "powershell -Command \"Set-ItemProperty -Path 'HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Keyboard Layout' -Name 'Scancode Map' -Value ([byte[]](0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x3A,0x00,0x5B,0x00,0x00,0x00))\"" "$SOCKET" + + # === macOS (hidutil remap) === + send_guest_command "hidutil property --set '{\"UserKeyMapping\":[{\"HIDKeyboardModifierMappingSrc\":0x700000039,\"HIDKeyboardModifierMappingDst\":0x7000000E3}]}'" "$SOCKET" + + echo "[*] Done sending remapping commands to guest." +} + +main "$@" diff --git a/heads-up-display b/unix/utils/heads-up-display index 8857675..54f5de1 100755 --- a/heads-up-display +++ b/unix/utils/heads-up-display @@ -34,7 +34,7 @@ # Set the environment variables to x11 to allow working in Wayland export GDK_BACKEND=x11 export QT_QPA_PLATFORM=xcb -export WAYLAND_DISPLAY="" +#export WAYLAND_DISPLAY="" export WINIT_UNIX_BACKEND=x11 # Supported terminals and dropdown class @@ -62,9 +62,9 @@ if [ "$my_term" = "" ]; then # Start terminal with scratchpad class case "$my_term" in - "wezterm") wezterm start --class heads-up-display -e tmux new-session -A -s HUD -e bash & ;; - "kitty") kitty --class heads-up-display tmux new-session -A -s HUD -e bash & ;; - "alacritty") alacritty --class heads-up-display -e tmux new-session -A -s HUD -e bash & ;; + "wezterm") wezterm start --class heads-up-display -e tmux new-session -A -s HUD -e bash htop & ;; + "kitty") kitty --class heads-up-display tmux new-session -A -s HUD -e bash htop & ;; + "alacritty") alacritty --class heads-up-display -e tmux new-session -A -s HUD -e bash htop & ;; esac fi diff --git a/unix/utils/hex2rgb.sh b/unix/utils/hex2rgb.sh new file mode 100755 index 0000000..a8ccc38 --- /dev/null +++ b/unix/utils/hex2rgb.sh @@ -0,0 +1,3 @@ +#!/bin/bash +hex="${1}" +printf "R: %d G: %d B: %d\n" 0x"${hex:0:2}" 0x"${hex:2:2}" 0x"${hex:4:2}" diff --git a/kill-notify b/unix/utils/kill-notify index f7f749e..f7f749e 100755 --- a/kill-notify +++ b/unix/utils/kill-notify diff --git a/kill-process b/unix/utils/kill-process index 5247e5f..5247e5f 100755 --- a/kill-process +++ b/unix/utils/kill-process diff --git a/unix/utils/move_terminal b/unix/utils/move_terminal new file mode 100755 index 0000000..2b447d0 --- /dev/null +++ b/unix/utils/move_terminal @@ -0,0 +1,65 @@ +#!/bin/bash + +SCRATCHPAD_CLASSES=("scratchpad" "pack" "heads-up-display") +PRIMARY_MONITOR="eDP-1" +SECOND_MONITOR="HDMI-A-2" + +PRIMARY_WORKSPACES=(2 4 5 6) +SECOND_WORKSPACES=(1 3) + +# Check if second monitor exists +if ! hyprctl monitors -j | jq -e ".[] | select(.name == \"$SECOND_MONITOR\")" >/dev/null; then + # No second monitor, do nothing + exit 0 +fi + +# Check if list of workspaces has any windows +has_windows_in_workspaces() { + local workspaces=("$@") + for ws in "${workspaces[@]}"; do + local count + count=$(hyprctl clients -j | jq "[.[] | select(.workspace.id == $ws)] | length") + if ((count > 0)); then + return 0 + fi + done + return 1 +} + +# Check window presence +primary_has_windows=false +second_has_windows=false + +if has_windows_in_workspaces "${PRIMARY_WORKSPACES[@]}"; then + primary_has_windows=true +fi +if has_windows_in_workspaces "${SECOND_WORKSPACES[@]}"; then + second_has_windows=true +fi + +# Decide target monitor +if [[ "$primary_has_windows" == true && "$second_has_windows" == false ]]; then + TARGET_MONITOR="$SECOND_MONITOR" +elif [[ "$primary_has_windows" == false && "$second_has_windows" == true ]]; then + TARGET_MONITOR="$PRIMARY_MONITOR" +else + TARGET_MONITOR="$SECOND_MONITOR" # both busy or both empty → second monitor +fi + +# Get workspace id of the target monitor +TARGET_WORKSPACE=$(hyprctl monitors -j | jq ".[] | select(.name == \"$TARGET_MONITOR\") | .activeWorkspace.id") + +# Wait for scratchpad window to appear and move it +for _ in {1..20}; do + for class in "${SCRATCHPAD_CLASSES[@]}"; do + client=$(hyprctl clients -j | jq -r ".[] | select(.class == \"$class\") | .address") + if [[ -n "$client" ]]; then + hyprctl dispatch movetoworkspacesilent "$TARGET_WORKSPACE,address:$client" + hyprctl dispatch focuswindow address:"$client" + exit 0 + fi + done + sleep 0.1 +done + +exit 1 diff --git a/neovim.sh b/unix/utils/neovim.sh index 39554cb..c9adcca 100755 --- a/neovim.sh +++ b/unix/utils/neovim.sh @@ -11,7 +11,7 @@ RED='\033[0;31m' GREEN='\033[0;32m' NC='\033[0m' # No Color -# Function to handle errors +# Handle errors handle_error() { local message="$1" printf "${RED}Error: $message${NC}\n" @@ -65,15 +65,16 @@ check_neovim_installed() { # Nightly version nightly_version() { - local url="https://github.com/neovim/neovim/releases/download/nightly/nvim.appimage" + local url="https://github.com/neovim/neovim/releases/download/nightly/nvim-linux-x86_64.appimage" install_neovim "$url" local version_output=$(nvim --version) - version_id="Nightly $(echo "$version_output" | grep -oP 'NVIM \d+\.\d+')" + version_id="Nightly $(echo "$version_output" | grep -oP 'NVIM \d+\.\d+\.\d+')" } # Stable version stable_version() { - local url="https://github.com/neovim/neovim/releases/download/stable/nvim.appimage" + #local url="https://github.com/neovim/neovim/releases/download/stable/nvim.appimage" + local url="https://github.com/neovim/neovim/releases/latest/download/nvim-linux-x86_64.appimage" install_neovim "$url" local version_output=$(nvim --version) version_id="Stable $(echo "$version_output" | grep -oP 'NVIM \d+\.\d+')" @@ -88,13 +89,13 @@ specific_version() { version="v$version" fi - local url="https://github.com/neovim/neovim/releases/download/$version/nvim.appimage" + local url="https://github.com/neovim/neovim/releases/download/$version/nvim-linux-x86_64.appimage" install_neovim "$url" local version_output=$(nvim --version) - version_id="Version $version $(echo "$version_output" | grep -oP 'NVIM \d+\.\d+')" + version_id="Version $version $(echo "$version_output" | grep -oP 'NVIM \d+\.\d+\.\d+')" } -# Function to download a file using wget or curl +# Download a file using wget or curl download_file() { local url="$1" local output="$2" @@ -346,7 +347,7 @@ else esac fi -# Function to check for updates and display breaking changes +# Check for updates and display breaking changes check_version_updates() { local latest_version_url="https://api.github.com/repos/neovim/neovim/releases/latest" local latest_version="" @@ -368,7 +369,7 @@ check_version_updates() { fi } -# Function to display breaking changes for a specific version +# To display breaking changes for a specific version display_breaking_changes() { local version="$1" local changelog_url="https://github.com/neovim/neovim/releases/tag/$version" diff --git a/scratchpad b/unix/utils/pack index 430e71e..4b8760c 100755 --- a/scratchpad +++ b/unix/utils/pack @@ -1,19 +1,40 @@ -#!/bin/bash +#!/bin/sh # Created By: srdusr -# Created On: Tue 07 Mar 2023 15:06:47 PM CAT -# Project: Agnostic scratchpad/dropdown terminal that works on most window managers +# Created On: Wed 05 Feb 2023 01:24:37 AM CAT +# Project: bspwm scratchpad (pack) with tmux session + +### Alternative method + +#if id="$(xdo id -N pack)"; then +# bspc node "$id" -g hidden -f +#else +# #kitty --class "pack" -e tmux new-session -A -s pack -e bash >/dev/null 2>&1 & +# wezterm start --class "pack" -e tmux new-session -A -s pack -e bash >/dev/null 2>&1 & +#fi + +#- - - - - - - - - - + +### Alternative method + +#id=$(xdotool search --class pack) +#if [ "$id" = "" ]; then +# #kitty --class "pack" -e tmux new-session -A -s pack -e bash > /dev/null 2>&1 & +# alacritty --class "pack" -e tmux new-session -A -s pack -e bash >/dev/null 2>&1 & +#else +# if [ ! -f /tmp/hide_hud ]; then +# touch /tmp/hide_hud && xdo hide "$id" +# elif [ -f /tmp/hide_hud ]; then +# rm /tmp/hide_hud && xdo show "$id" +# fi +#fi + -# Dependencies: wmctrl, xprop, xdo, xdotool -# NOTE: Ensure script is included in system's path and can therefore be invoked with the command 'scratchpad'. -# Furthermore make sure the terminal is using x11 as a backend in wayland to allow this to work. -# Example: wezterm.lua: enable_wayland = false, -# kitty.conf: linux_display_server x11 # Set the environment variables to x11 to allow working in Wayland export GDK_BACKEND=x11 export QT_QPA_PLATFORM=xcb -export WAYLAND_DISPLAY="" +#export WAYLAND_DISPLAY="" export WINIT_UNIX_BACKEND=x11 # Supported terminals and dropdown class @@ -21,7 +42,7 @@ supported_terminals=("wezterm" "kitty" "alacritty") # Check if any supported terminal with scratchpad class is running for term in "${supported_terminals[@]}"; do - if pgrep -f "$term.*--class scratchpad" >/dev/null; then + if pgrep -f "$term.*--class pack" >/dev/null; then my_term="$term" break fi @@ -41,14 +62,15 @@ if [ "$my_term" = "" ]; then # Start terminal with scratchpad class case "$my_term" in - "wezterm") wezterm start --class scratchpad -e tmux new-session -A -s tmux -e bash & ;; - "kitty") kitty --class scratchpad tmux new-session -A -s tmux -e bash & ;; - "alacritty") alacritty --class scratchpad -e tmux new-session -A -s tmux -e bash & ;; + "wezterm") wezterm start --class pack -e tmux new-session -A -s pack -e bash & ;; + "kitty") kitty --class pack tmux new-session -A -s pack -e bash & ;; + "alacritty") alacritty --class pack -e tmux new-session -A -s pack -e bash & ;; + esac fi # Get the window ID of the scratchpad terminal -id="$(xdo id -N scratchpad)" +id="$(xdo id -N pack)" # Toggle scratchpad terminal visibility if [ "$id" != "" ]; then diff --git a/rofi-network-manager.sh b/unix/utils/rofi-network-manager.sh index 61106d2..61106d2 100755 --- a/rofi-network-manager.sh +++ b/unix/utils/rofi-network-manager.sh diff --git a/unix/utils/root.sh b/unix/utils/root.sh new file mode 100755 index 0000000..a7807bc --- /dev/null +++ b/unix/utils/root.sh @@ -0,0 +1,152 @@ +#!/bin/bash + +# Created By: srdusr +# Created On: Mon 19 Feb 2025 14:18:00 PM CAT +# Project: Backup and restore system files to/from home extras (system dotfiles) directory + +# Dependencies: None +# NOTE: The backups will be stored in the ~/extras directory, preserving the original file structure. Run as sudo or be prompted for password +# Example usage: +# To backup a specific file: root.sh --backup /some_directory/some_file.conf +# To restore a specific file: root.sh --restore ~/extras/some_directory/some_file.conf +# To restore all files: root.sh --restore +# + +# Use $SUDO_USER to get the original user when run with sudo, or fall back to the current user if not +BASE_DIR="/home/${SUDO_USER:-$(whoami)}/extras" + +if [ "$EUID" -eq 0 ] && [ "$SUDO_USER" = "" ]; then + echo "You are running this script directly as root, not through sudo!" + exit 1 +fi + +if [ "$EUID" -ne 0 ]; then + echo "Elevating to sudo..." + exec sudo "$0" "$@" # Re-run the script with sudo +fi + +# Create directories if they do not exist +create_directory() { + local dir=$1 + if [ ! -d "$dir" ]; then + echo "Creating directory: $dir" + mkdir -p "$dir" + else + echo "Directory already exists: $dir" + fi +} + +# Backup files +backup_to_extras() { + local src=$1 + + # Ensure the file or directory exists + if [ -e "$src" ]; then + # Strip the leading / from src to avoid double slashes + local stripped_src="${src#/}" + + # Determine the destination path + dest_dir="$BASE_DIR/$(dirname "$stripped_src")" # Get the directory part of the source + dest_file="$BASE_DIR/$stripped_src" # Get the full destination file path + + # Debug: Print paths + echo "Source file: $src" + echo "Destination directory: $dest_dir" + echo "Destination file: $dest_file" + + # Create the necessary directories in extras if they don't exist + create_directory "$dest_dir" + + # Backup the file to extras + echo "Backing up $src to $dest_file" + cp -p "$src" "$dest_file" + + # Set permission to user + chown "$SUDO_USER:$SUDO_USER" "$dest_file" + + echo "Backup of $src completed." + else + echo "Error: The file or directory '$src' does not exist." + fi +} + +# Restore files +restore_from_extras() { + local src=$1 + + # Ensure the file or directory exists in extras + if [ -e "$src" ]; then + # Strip the leading / from src to avoid double slashes + local stripped_src="${src#/}" + + # Determine the destination path + dest_dir="/$(dirname "$stripped_src")" # Get the directory part of the source + dest_file="/$stripped_src" # Get the full destination file path + + # Debug: Print paths + echo "Source file: $src" + echo "Destination directory: $dest_dir" + echo "Destination file: $dest_file" + + # Create the necessary directories in the system if they don't exist + create_directory "$dest_dir" + + # Backup the file if it exists before restoring + if [ -e "$dest_file" ]; then + echo "File $dest_file exists, creating a backup..." + mv "$dest_file" "$dest_file.bak" + echo "Backup created at $dest_file.bak" + fi + + # Restore the file from extras + echo "Restoring $src to $dest_file" + cp -p "$BASE_DIR/$stripped_src" "$dest_file" + + # Set permissions for the restored file + chmod 644 "$dest_file" + + echo "Restore of $src completed." + else + echo "Error: The file or directory '$src' does not exist in extras." + fi +} + +# Restore all files from extras +restore_all_from_extras() { + echo "Restoring all files and directories from extras..." + + # Loop over all files and directories in BASE_DIR and restore them + find "$BASE_DIR" -type f | while read -r file; do + restore_from_extras "$file" + done + + echo "Restore completed." +} + +# Backup system files based on user input +if [ "$1" == "--backup" ]; then + if [ "$2" = "" ]; then + echo "Error: Please specify the file or directory to backup." + exit 1 + fi + + # Perform the backup + echo "Backing up system files to extras..." + backup_to_extras "$2" + echo "Backup completed." + + # Restore system files based on user input +elif [ "$1" == "--restore" ]; then + if [ "$2" = "" ]; then + # If no specific file is provided, restore everything + restore_all_from_extras + else + # Restore a specific file or directory + echo "Restoring system files from extras..." + restore_from_extras "$2" + echo "Restore completed." + fi + +else + echo "Invalid option. Use '--backup' to backup or '--restore' to restore." +fi diff --git a/unix/utils/run_with_display.sh b/unix/utils/run_with_display.sh new file mode 100755 index 0000000..5f1f3a0 --- /dev/null +++ b/unix/utils/run_with_display.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +run_with_display() { + output=$("$@" 2>&1) + exit_status=$? + + if [[ $exit_status -ne 0 && ("$output" =~ "cannot open display" || "$output" =~ "DISPLAY environment variable is missing") ]]; then + DISPLAY=:0 "$@" + else + echo "$output" + return $exit_status + fi +} + +# Call this script with any command you want to run +command=$1 +shift +run_with_display "$command" "$@" + diff --git a/unix/utils/scratchpad b/unix/utils/scratchpad new file mode 100755 index 0000000..d47dafe --- /dev/null +++ b/unix/utils/scratchpad @@ -0,0 +1,147 @@ +#!/bin/bash + +# Created By: srdusr +# Created On: Tue 07 Mar 2023 15:06:47 PM CAT +# Project: Agnostic scratchpad/dropdown terminal that works on most window managers + +# Dependencies: wmctrl, xprop, xdo, xdotool +# NOTE: Ensure script is included in system's path and can therefore be invoked with the command 'scratchpad'. +# Furthermore make sure the terminal is using x11 as a backend in wayland to allow this to work. +# Example: wezterm.lua: enable_wayland = false, +# kitty.conf: linux_display_server x11 + +# Set the environment variables to x11 to allow working in Wayland +#export GDK_BACKEND=x11 +#export QT_QPA_PLATFORM=xcb +#export WAYLAND_DISPLAY="" +#export WINIT_UNIX_BACKEND=x11 +#export WAYLAND_DISPLAY=wayland-0 + +# Supported terminals and dropdown class +supported_terminals=("wezterm" "kitty" "alacritty") + +# Check if any supported terminal with scratchpad class is running +for term in "${supported_terminals[@]}"; do + if pgrep -f "$term.*--class scratchpad" >/dev/null; then + my_term="$term" + break + fi +done + +# If no supported terminal is running, start the first available one +if [ "$my_term" = "" ]; then + for term in "${supported_terminals[@]}"; do + if command -v "$term" >/dev/null 2>&1; then + my_term="$term" + break + fi + done + if [ "$my_term" = "" ]; then + echo "No supported terminal found." && exit 1 + fi + + # Start terminal with scratchpad class + case "$my_term" in + "wezterm") wezterm start --class scratchpad -e tmux new-session -A -s tmux -e bash & ;; + "kitty") kitty --class scratchpad tmux new-session -A -s tmux -e bash & ;; + "alacritty") alacritty --class scratchpad -e tmux new-session -A -s tmux -e bash & ;; + esac +fi + +# Get the window ID of the scratchpad terminal +id="$(xdo id -N scratchpad)" +# Get the ID of the currently focused window +focused_id="$(xdotool getwindowfocus)" + +# Get class of the currently focused window +focused_class="$(xprop -id "$focused_id" WM_CLASS 2>/dev/null | awk -F '"' '{print $4}')" + +# Toggle visible/hide and smart focus +if [ "$id" != "" ]; then + if xwininfo -id "$id" | grep "Map State: IsViewable" >/dev/null; then + if [ "$focused_id" = "$id" ] || [ "$focused_class" = "scratchpad" ]; then + # Scratchpad is focused, hide it + dimensions="$(xwininfo -id "$id" | awk '/Width:|Height:/ { printf("%s=%s;", tolower($1), $2) }')" + xdo hide "$id" 2>/dev/null + else + # Scratchpad is visible but not focused + xdotool windowactivate "$id" + xdotool windowfocus "$id" + hyprctl dispatch focuswindow scratchpad + fi + else + # Scratchpad is hidden, show and focus it + xdo show "$id" + xdotool windowsize "$id" "$(echo "$dimensions" | tr ';' ' ')" 2>/dev/null + xdotool windowactivate "$id" + xdotool windowfocus "$id" + hyprctl dispatch focuswindow scratchpad + fi +fi + +## Get the window ID of the PiP window by title +#pip_id="$(xdo id -n "Picture-in-Picture")" +# +## Toggle scratchpad terminal visibility +#if [ "$id" != "" ]; then +# if xwininfo -id "$id" | grep "Map State: IsViewable" >/dev/null; then +# # Scratchpad is visible, hide it +# dimensions="$(xwininfo -id "$id" | awk '/Width:|Height:/ { printf("%s=%s;", tolower($1), $2) }')" +# xdo hide "$id" 2>/dev/null +# else +# # Scratchpad is hidden, show it and restore dimensions +# xdo show "$id" +# xdotool windowsize "$id" "$(echo "$dimensions" | tr ';' ' ')" 2>/dev/null +# xdotool windowactivate "$id" +# xdotool windowfocus "$id" +# +# # Adjust layer based on PiP window presence +# if [ "$pip_id" != "" ]; then +# hyprctl dispatch layer "$id" 1 +# else +# hyprctl dispatch layer "$id" 0 +# fi +# +# hyprctl dispatch focuswindow scratchpad +# fi +#fi + +## Get the window ID of the scratchpad terminal +#id=$(hyprctl clients -j | jq -r '.[] | select(.class=="scratchpad").address') +# +## Toggle scratchpad terminal visibility +#if [ -n "$id" ]; then +# focused=$(hyprctl activewindow -j | jq -r '.address') +# if [ "$focused" = "$id" ]; then +# # Scratchpad is focused, minimize it +# hyprctl dispatch move "address:$id workspace silent 0" +# else +# # Show scratchpad, set above normal windows but below PiP +# hyprctl dispatch movetoworkspace current address:$id +# hyprctl dispatch focuswindow address:$id +# hyprctl dispatch layer "address:$id above" +# hyprctl dispatch focuswindow address:$id +# fi +#fi + +## Get the window ID of the PiP window by title +#pip_id=$(hyprctl clients -j | jq -r '.[] | select(.title=="Picture-in-Picture").address') +# +## Toggle scratchpad terminal visibility +#if [ -n "$id" ]; then +# focused=$(hyprctl activewindow -j | jq -r '.address') +# if [ "$focused" = "$id" ]; then +# # Scratchpad is focused, minimize it +# hyprctl dispatch move "address:$id workspace silent 0" +# else +# # Show scratchpad, set above normal windows but below PiP +# hyprctl dispatch movetoworkspace current address:$id +# hyprctl dispatch focuswindow address:$id +# if [ -n "$pip_id" ]; then +# hyprctl dispatch layer "address:$id below" +# else +# hyprctl dispatch layer "address:$id above" +# fi +# hyprctl dispatch focuswindow address:$id +# fi +#fi diff --git a/unix/utils/screenshot b/unix/utils/screenshot new file mode 100755 index 0000000..b209574 --- /dev/null +++ b/unix/utils/screenshot @@ -0,0 +1,102 @@ +#!/bin/sh + +# === Config: directory for QEMU sockets === +QEMU_SOCK_DIR="/home/$(whoami)/virt/machines" + +# === Send 'print' key to QEMU monitor === +send_print_to_qemu() { + monitor_socket=$(find "$QEMU_SOCK_DIR" -maxdepth 1 -type s -name '*-monitor.socket' | head -n1) + if [ "$monitor_socket" = "" ]; then + echo "No QEMU monitor socket found." + return 1 + fi + echo "Using QEMU monitor socket: $monitor_socket" + if ! printf "sendkey print\n" | socat - UNIX-CONNECT:"$monitor_socket"; then + echo "Failed to send key via socat." + return 1 + fi + echo "Sent 'sendkey print' to QEMU successfully." + return 0 +} + +# === Detect if current window is QEMU (X11 only) === +if [ "$DISPLAY" != "" ] && command -v xprop >/dev/null 2>&1; then + win_id=$(xprop -root _NET_ACTIVE_WINDOW | awk -F' ' '{print $5}') + class=$(xprop -id "$win_id" WM_CLASS 2>/dev/null | awk -F'"' '{print $4}') + if [ "$class" = "qemu-system-x86_64" ]; then + send_print_to_qemu && exit 0 + # if sending fails, optionally fallback or exit anyway + exit 1 + fi +fi +#if command -v hyprctl >/dev/null 2>&1 && command -v jq >/dev/null 2>&1; then +# focused_class="$(hyprctl activewindow -j | jq -r '.class' 2>/dev/null)" +# if [ "$focused_class" = "qemu-system-x86_64" ]; then +# send_print_to_qemu && exit 0 +# fi +#fi + +DIR="$HOME/pictures/screenshots" +OUTPUT_DIR="$HOME/documents/ocr_output" + +# Create the directories if they don't exist +[ ! -d "$DIR" ] && mkdir -pv "$DIR" +[ ! -d "$OUTPUT_DIR" ] && mkdir -pv "$OUTPUT_DIR" + +file="$DIR/screenshot_$(date '+%Y-%m-%d_%H-%M-%S').png" +text_file="$OUTPUT_DIR/ocr_output_$(date '+%Y-%m-%d_%H-%M-%S')" + +copy_to_clipboard() { + if [ "$WAYLAND_DISPLAY" != "" ]; then + wl-copy <"$1" + elif [ "$DISPLAY" != "" ]; then + xclip -selection clipboard -i <"$1" + else + echo "No display server detected. Cannot copy to clipboard." + return 1 + fi +} + +case "$1" in +screen) grim "$file" ;; +output) slurp -o -r | grim -g - "$file" ;; +area) slurp | grim -g - "$file" ;; +output-area) slurp -o | grim -g - "$file" ;; +ocr) slurp | grim -g - "$file" && tesseract "$file" "$text_file" ;; +ocr-clipboard) + slurp | grim -g - "$file" && tesseract "$file" "$text_file" + if [ -f "$text_file.txt" ]; then + copy_to_clipboard "$text_file.txt" + CLIP_STATUS=$? + if [ "$CLIP_STATUS" -eq 0 ]; then + command rm -f "$text_file.txt" + notify-send -t 10000 --app-name "Screenshot" "OCR to Clipboard" "Text copied to clipboard." + echo "OCR output copied to clipboard and file deleted." + else + notify-send -t 10000 --app-name "Screenshot" "Clipboard Copy Failed" "Failed to copy text to clipboard." + echo "Failed to copy text to clipboard." + fi + else + notify-send -t 10000 --app-name "Screenshot" "OCR Error" "OCR process failed." + exit 1 + fi + ;; +*) + echo "Invalid argument" + notify-send -t 10000 --app-name "Screenshot" "Screenshot" "Something went wrong." + exit 1 + ;; +esac + +if [ "$1" = "ocr" ]; then + if [ -f "$text_file" ]; then + notify-send -t 10000 --app-name "Screenshot" "OCR Complete" "Text saved to $text_file" + echo "OCR output saved to: $text_file" + else + notify-send -t 10000 --app-name "Screenshot" "OCR Error" "OCR process failed." + exit 1 + fi +else + notify-send -t 10000 --app-name "Screenshot" "Screenshot" "Saved as $file" + echo "$file" +fi diff --git a/unix/utils/sendkeys.awk b/unix/utils/sendkeys.awk new file mode 100755 index 0000000..16a3fa9 --- /dev/null +++ b/unix/utils/sendkeys.awk @@ -0,0 +1,86 @@ +#!/usr/bin/env awk -f +# +# AWK script to send multiple `sendkey` commands to a QEMU virtual machine. +# It writes at a rate of roughly 40 keys per second, due to lower delays +# resulting in garbage output. +# +# It makes use of a TCP client created by an external utility, such as OpenBSD +# Netcat, to interact with QEMU's monitor and send a stream of `sendkey` +# commands. This is a practical way to transfer a small file or to script +# interactions with a terminal user interface. + +BEGIN { + # Set default delay if not provided via command-line args + if (!delay) { + delay = 0.025 + } + + # Define key mappings for common characters and symbols + key["#"] = "backspace" + key[" "] = "tab" + key[" "] = "spc" + key["!"] = "shift-1" + key["\""] = "shift-apostrophe" + key["#"] = "shift-3" + key["$"] = "shift-4" + key["%"] = "shift-5" + key["&"] = "shift-7" + key["'"] = "apostrophe" + key["("] = "shift-9" + key[")"] = "shift-0" + key["*"] = "shift-8" + key["+"] = "shift-equal" + key[","] = "comma" + key["-"] = "minus" + key["."] = "dot" + key["/"] = "slash" + key[":"] = "shift-semicolon" + key[";"] = "semicolon" + key["<"] = "shift-comma" + key["="] = "equal" + key[">"] = "shift-dot" + key["?"] = "shift-slash" + key["@"] = "shift-2" + + # Map numbers + for (i = 48; i < 48 + 10; ++i) { + number = sprintf("%c", i) + key[number] = number + } + + # Map letters A-Z, including shift + for (i = 65; i < 65 + 26; ++i) { + key[sprintf("%c", i)] = sprintf("shift-%c", i + 32) + } + + # Other symbols + key["["] = "bracket_left" + key["\\"] = "backslash" + key["]"] = "bracket_right" + key["^"] = "shift-6" + key["_"] = "shift-minus" + key["`"] = "grave_accent" + key["{"] = "shift-bracket_left" + key["|"] = "shift-backslash" + key["}"] = "shift-bracket_right" + key["~"] = "shift-grave_accent" + key[""] = "delete" + + # Handle Super and Caps Lock key mappings (for remapping Caps to Super) + key["capslock"] = "super" + key["super"] = "super" + + # Handle other keys if needed +} + +{ + split($0, chars, "") + for (i = 1; i <= length($0); i++) { + # Print sendkey command for the character, mapping it through the key[] array + if (key[chars[i]] != "") { + printf("sendkey %s\n", key[chars[i]]) + } + system("sleep " delay) # Sleep for the defined delay + } + printf "sendkey ret\n" # Send "return" (enter) key at the end +} diff --git a/unix/utils/sext b/unix/utils/sext new file mode 100755 index 0000000..b120929 --- /dev/null +++ b/unix/utils/sext @@ -0,0 +1,47 @@ +#!/bin/bash + +#Sext (Shutdown Exit) +# List of package managers to check for +package_managers=("emerge" "apt" "dnf" "pacman" "zypper") + +# Set how often to check (in seconds) +check_interval=30 + +# Check if any package manager is running +is_package_manager_running() { + for pm in "${package_managers[@]}"; do + if pgrep -x "$pm" >/dev/null; then + return 0 # Return 0 (true) if any package manager is running + fi + done + return 1 # Return 1 (false) if no package manager is running +} + +# Safely shutdown the system +safe_shutdown() { + # Try using shutdown without sudo first + if shutdown -h now; then + echo "Shutdown initiated successfully using 'shutdown'." + # If shutdown fails, try using poweroff + elif poweroff; then + echo "Shutdown initiated successfully using 'poweroff'." + # If poweroff fails, try using sudo poweroff + elif sudo poweroff; then + echo "Shutdown initiated successfully with 'sudo poweroff'." + # If both shutdown and poweroff fail, try with sudo shutdown + elif sudo shutdown -h now; then + echo "Shutdown initiated successfully with 'sudo shutdown'." + else + echo "Shutdown command failed. Please check your system configuration." + fi +} + +# Loop until no package manager process is running +while is_package_manager_running; do + echo "Package manager is still running. Checking again in $check_interval seconds..." + sleep "$check_interval" +done + +# Once the process completes, initiate a safe shutdown +echo "Package manager has finished. Attempting to shutdown..." +safe_shutdown diff --git a/track-books.sh b/unix/utils/track-books.sh index f13add8..f13add8 100755 --- a/track-books.sh +++ b/unix/utils/track-books.sh diff --git a/unix/utils/waypipe_app b/unix/utils/waypipe_app new file mode 100755 index 0000000..a8520bc --- /dev/null +++ b/unix/utils/waypipe_app @@ -0,0 +1,16 @@ +#!/bin/sh + +# Usage: ./waypipe_app user@remote <app> +# Example: ./waypipe_app user@remote gedit + +if [ $# -lt 2 ]; then + echo "Usage: $0 user@remote <app> [args...]" + exit 1 +fi + +REMOTE=$1 +shift +APP="$@" + +# Run the app remotely, forward display over SSH with waypipe +waypipe ssh "$REMOTE" $APP diff --git a/unix/utils/wayvnc_session b/unix/utils/wayvnc_session new file mode 100755 index 0000000..1f2f30b --- /dev/null +++ b/unix/utils/wayvnc_session @@ -0,0 +1,10 @@ +#!/bin/sh + +# Usage: ./wayvnc_session [address] [port] +# Default: 0.0.0.0 5900 + +ADDR=${1:-0.0.0.0} +PORT=${2:-5900} + +echo "Starting Wayland VNC server on $ADDR:$PORT" +wayvnc $ADDR $PORT diff --git a/unix/utils/window_manager_name.sh b/unix/utils/window_manager_name.sh new file mode 100755 index 0000000..cfff9ee --- /dev/null +++ b/unix/utils/window_manager_name.sh @@ -0,0 +1,29 @@ +#! /bin/bash + + +windowManagerName () { + local window=$( + xprop -root -notype + ) + + local identifier=$( + echo "${window}" | + awk '$1=="_NET_SUPPORTING_WM_CHECK:"{print $5}' + ) + + local attributes=$( + xprop -id "${identifier}" -notype -f _NET_WM_NAME 8t + ) + + local name=$( + echo "${attributes}" | + grep "_NET_WM_NAME = " | + cut --delimiter=' ' --fields=3 | + cut --delimiter='"' --fields=2 + ) + + echo "${name}" +} + + +windowManagerName diff --git a/xtouch b/unix/utils/xtouch index a5eb23b..a5eb23b 100755 --- a/xtouch +++ b/unix/utils/xtouch diff --git a/unix/virt/checksum.sh b/unix/virt/checksum.sh new file mode 100755 index 0000000..adb6a24 --- /dev/null +++ b/unix/virt/checksum.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Check if the correct number of arguments are provided +if [[ $# -ne 2 ]]; then + echo "Usage: checksum <file> <website>" + exit 1 +fi + +FILE=$1 +CHECKSUM_PAGE=$2 + +# Fetch the HTML content of the page +HTML_CONTENT=$(curl -s "$CHECKSUM_PAGE") + +# Try searching for checksums with a more targeted approach, like matching a checksum pattern or look for files with checksum labels +CHECKSUM=$(echo "$HTML_CONTENT" | grep -oP '([a-f0-9]{64})' | head -n 1) # Try specifically looking for SHA256 checksum patterns + +# Check if any checksum was found +if [[ -z "$CHECKSUM" ]]; then + echo "Checksum not found on the page." + exit 1 +fi + +# Calculate the checksum of the file locally +LOCAL_CHECKSUM=$(sha256sum "$FILE" | awk '{print $1}') + +echo "Local checksum: $LOCAL_CHECKSUM" +echo "Remote checksum: $CHECKSUM" + +# Compare the local checksum with the one from the website +if [[ "$LOCAL_CHECKSUM" == "$CHECKSUM" ]]; then + echo "The checksums match! The file is verified." + +else + echo "The checksums do not match. The file may be corrupted or tampered with." +fi diff --git a/unix/virt/dos.sh b/unix/virt/dos.sh new file mode 100755 index 0000000..0b50c4b --- /dev/null +++ b/unix/virt/dos.sh @@ -0,0 +1,998 @@ +#!/usr/bin/env bash + +# Set variables +HOST_DIR="virt" +VM_NAME="dos" +VM_SIZE="80G" # Disk size in GB +VM_RAM="8G" # RAM size +VM_CPU="6" # Number of virtual CPUs +CORES=$((VM_CPU / 2)) +THREADS_PER_CORE=2 +SOCKETS=1 + +VM_DIR="$HOST_DIR/machines" +IMAGE_DIR="$HOST_DIR/images" +WIN_ISO_DIR="${IMAGE_DIR}/${VM_NAME}" # Directory for Windows ISO +VM_DIR="$WIN_ISO_DIR" +SOCKET_DIR="$VM_DIR" +SHARED_DIR="${HOST_DIR}/shared" +FIRMWARE_DIR="${HOST_DIR}/firmware" +TPM_DIR="$WIN_ISO_DIR" +TPM_SOCKET="${WIN_ISO_DIR}/${VM_NAME}.swtpm-sock" +GUEST_PORT=22 +QCOW2_FILE="${VM_DIR}/${VM_NAME}.qcow2" +RAW_FILE="${VM_DIR}/${VM_NAME}.raw" + +# Anti-detection: Generate realistic hardware identifiers +REAL_MAC="00:1A:2B:3C:4D:5E" # Example Dell MAC - replace with your choice +REAL_SERIAL="$(openssl rand -hex 8 | tr '[:lower:]' '[:upper:]')" +REAL_UUID="$(uuidgen)" +REAL_VENDOR="Dell Inc." +REAL_PRODUCT="OptiPlex 7090" +REAL_VERSION="01" +REAL_FAMILY="OptiPlex" + +# Try to find an available host port starting from 22220 +HOST_PORT_START=22220 +HOST_PORT_END=22300 + +for ((port = HOST_PORT_START; port <= HOST_PORT_END; port++)); do + if ! ss -tuln | grep -q ":$port\b"; then + HOST_PORT=$port + echo "Using available port: $HOST_PORT" + break + fi +done + +if [[ $port -gt $HOST_PORT_END ]]; then + echo "Error: No available ports found between $HOST_PORT_START and $HOST_PORT_END" >&2 + exit 1 +fi + +# Set SMP configuration +SMP_CONFIG="cores=$CORES,threads=$THREADS_PER_CORE,sockets=$SOCKETS" + +# Create necessary directories +mkdir -p "${HOME}/${HOST_DIR}" +mkdir -p "$IMAGE_DIR" "$SHARED_DIR" "$FIRMWARE_DIR" +mkdir -p "$WIN_ISO_DIR" "$VM_DIR" +mkdir -p "${WIN_ISO_DIR}/unattended" + +# Define ISO paths and URLs +ISO_VIRTIO="${WIN_ISO_DIR}/virtio-win.iso" +ISO_UNATTENDED="${WIN_ISO_DIR}/unattended.iso" + +# Find Windows ISO with flexible pattern matching +find_windows_iso() { + # Check if directory exists + if [[ ! -d "$WIN_ISO_DIR" ]]; then + mkdir -p "$WIN_ISO_DIR" + fi + + # Try to find any Windows ISO using case-insensitive patterns + local found_iso + found_iso=$(find "$WIN_ISO_DIR" -maxdepth 1 -type f \( \ + -iname "*win11*.iso" -o \ + -iname "*win*11*.iso" -o \ + -iname "Win*.iso" -o \ + -iname "Win11*.iso" -o \ + -iname "Win*11*.iso" -o \ + -iname "*windows*11*.iso" -o \ + -iname "*windows11*.iso" \ + \) -exec stat --format="%Y %n" {} \; | sort -n | tail -n 1 | cut -d' ' -f2-) + + if [[ -n "$found_iso" && -f "$found_iso" ]]; then + echo "$found_iso" + return 0 + fi + + return 1 +} + +# Define download URLs +VIRTIO_ISO_URL="https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso" + +# Print colored messages +print_info() { echo -e "\033[1;34m[INFO]\033[0m $1" >&2; } +print_success() { echo -e "\033[1;32m[SUCCESS]\033[0m $1" >&2; } +print_warning() { echo -e "\033[1;33m[WARNING]\033[0m $1" >&2; } +print_error() { echo -e "\033[1;31m[ERROR]\033[0m $1" >&2; } + +# Helper: verify file integrity +verify_file() { + local file="$1" + local expected_sha256="$2" + + if [[ ! -f "$file" ]]; then + return 1 + fi + + if [[ -n "$expected_sha256" ]]; then + local actual_sha256 + actual_sha256=$(sha256sum "$file" | cut -d' ' -f1) + if [[ "$actual_sha256" != "$expected_sha256" ]]; then + print_error "File integrity check failed for $file" + return 1 + fi + fi + + return 0 +} + +# Helper: download file with verification +download_file() { + local url="$1" + local dest="$2" + local expected_sha256="$3" + local allow_failure="$4" + + # Check if file exists and is valid + if [[ -f "$dest" ]]; then + if verify_file "$dest" "$expected_sha256"; then + print_info "File $dest already exists and verified." + return 0 + else + print_warning "File $dest exists but failed verification. Redownloading..." + rm -f "$dest" + fi + fi + + print_info "Downloading $url..." + if ! curl -fL --progress-bar -o "$dest" "$url"; then + print_error "Failed to download $url." + if [[ "$allow_failure" != "true" ]]; then + return 1 + fi + else + # Verify downloaded file + if ! verify_file "$dest" "$expected_sha256"; then + print_error "Downloaded file failed verification" + rm -f "$dest" + return 1 + fi + print_success "Successfully downloaded and verified $dest" + fi + + return 0 +} + +# Handle curl errors +handle_curl_error() { + local exit_code=$1 + case $exit_code in + 6) print_error "Couldn't resolve host" ;; + 7) print_error "Failed to connect to host" ;; + 22) print_error "HTTP page not retrieved (404, etc.)" ;; + 28) print_error "Operation timeout" ;; + *) print_error "Curl failed with exit code $exit_code" ;; + esac +} + +# Download Windows 11 ISO using Microsoft's API +download_windows_iso() { + local windows_version="11" # Default to Windows 11 + local language="English (United States)" # Default language + + # Parse arguments if provided + if [[ -n "$1" ]]; then + windows_version="$1" + fi + + print_info "Attempting to download Windows $windows_version ISO from Microsoft..." + + # Set required variables + local user_agent="Mozilla/5.0 (X11; Linux x86_64; rv:100.0) Gecko/20100101 Firefox/100.0" + local session_id="$(uuidgen)" + local profile="606624d44113" + local url="https://www.microsoft.com/en-us/software-download/windows$windows_version" + + # Add ISO to URL for Windows 10 + case "$windows_version" in + 10) url="${url}ISO" ;; + esac + + # Step 1: Get download page HTML + print_info "Fetching download page: $url" + local iso_download_page_html + iso_download_page_html="$(curl --disable --silent --user-agent "$user_agent" --header "Accept:" --max-filesize 1M --fail --proto =https --tlsv1.2 --http1.1 -- "$url")" || { + handle_curl_error $? + print_error "Failed to fetch the download page. Please download Windows $windows_version ISO manually from $url" + return 1 + } + + # Step 2: Extract Product Edition ID + print_info "Getting Product Edition ID..." + local product_edition_id + product_edition_id="$(echo "$iso_download_page_html" | grep -Eo '<option value="[0-9]+">Windows' | cut -d '"' -f 2 | head -n 1 | tr -cd '0-9' | head -c 16)" + + if [[ -z "$product_edition_id" ]]; then + print_error "Failed to extract product edition ID." + print_error "Please download Windows $windows_version ISO manually from $url" + return 1 + fi + + print_success "Product Edition ID: $product_edition_id" + + # Step 3: Register session ID + print_info "Registering session ID: $session_id" + curl --disable --silent --output /dev/null --user-agent "$user_agent" \ + --header "Accept:" --max-filesize 100K --fail --proto =https --tlsv1.2 \ + --http1.1 -- "https://vlscppe.microsoft.com/tags?org_id=y6jn8c31&session_id=$session_id" || { + print_error "Failed to register session ID." + return 1 + } + + # Step 4: Get language SKU ID + print_info "Getting language SKU ID..." + local language_skuid_table_json + language_skuid_table_json="$(curl --disable -s --fail --max-filesize 100K --proto =https --tlsv1.2 --http1.1 \ + "https://www.microsoft.com/software-download-connector/api/getskuinformationbyproductedition?profile=${profile}&ProductEditionId=${product_edition_id}&SKU=undefined&friendlyFileName=undefined&Locale=en-US&sessionID=${session_id}")" || { + handle_curl_error $? + print_error "Failed to get language SKU information." + return 1 + } + + # Extract SKU ID for selected language + local sku_id + + # Try with jq if available (more reliable) + if command -v jq >/dev/null 2>&1; then + sku_id="$(echo "$language_skuid_table_json" | jq -r '.Skus[] | select(.LocalizedLanguage=="'"$language"'" or .Language=="'"$language"'").Id')" + else + # Fallback to grep/cut if jq not available + sku_id="$(echo "$language_skuid_table_json" | grep -o '"Id":"[^"]*","Language":"'"$language"'"' | cut -d'"' -f4)" + + if [[ -z "$sku_id" ]]; then + # Try alternative extraction method + sku_id="$(echo "$language_skuid_table_json" | grep -o '"LocalizedLanguage":"'"$language"'","Id":"[^"]*"' | cut -d'"' -f6)" + fi + fi + + if [[ -z "$sku_id" ]]; then + print_error "Failed to extract SKU ID for $language." + return 1 + fi + + print_success "SKU ID: $sku_id" + + # Step 5: Get ISO download link + print_info "Getting ISO download link..." + local iso_download_link_json + iso_download_link_json="$(curl --disable -s --fail --referer "$url" \ + "https://www.microsoft.com/software-download-connector/api/GetProductDownloadLinksBySku?profile=${profile}&productEditionId=undefined&SKU=${sku_id}&friendlyFileName=undefined&Locale=en-US&sessionID=${session_id}")" + + local failed=0 + + if [[ -z "$iso_download_link_json" ]]; then + print_error "Microsoft servers gave an empty response to the download request." + failed=1 + fi + + if echo "$iso_download_link_json" | grep -q "Sentinel marked this request as rejected."; then + print_error "Microsoft blocked the automated download request based on your IP address." + failed=1 + fi + + if [[ "$failed" -eq 1 ]]; then + print_warning "Please manually download the Windows $windows_version ISO using a web browser from: $url" + print_warning "Save the downloaded ISO to: $WIN_ISO_DIR" + return 1 + fi + + # Extract 64-bit ISO download URL + local iso_download_link + + # Try with jq if available + if command -v jq >/dev/null 2>&1; then + iso_download_link="$(echo "$iso_download_link_json" | jq -r '.ProductDownloadOptions[].Uri' | grep x64 | head -n 1)" + else + # Fallback to grep/cut if jq not available + iso_download_link="$(echo "$iso_download_link_json" | grep -o '"Uri":"[^"]*x64[^"]*"' | cut -d'"' -f4 | head -n 1)" + fi + + if [[ -z "$iso_download_link" ]]; then + print_error "Failed to extract the download link from Microsoft's response." + print_warning "Manually download the Windows $windows_version ISO using a web browser from: $url" + return 1 + fi + + print_success "Got download link: ${iso_download_link%%\?*}" + + # Extract filename from URL + local file_name="$(echo "$iso_download_link" | cut -d'?' -f1 | rev | cut -d'/' -f1 | rev)" + + # If filename couldn't be extracted, use default + if [[ -z "$file_name" || "$file_name" == "$iso_download_link" ]]; then + file_name="windows-$windows_version.iso" + fi + + # Step 6: Download the ISO + print_info "Downloading Windows $windows_version ISO to $WIN_ISO_DIR/$file_name. This may take a while..." + + # Direct curl download + curl --disable --progress-bar --fail --location --proto '=https' --tlsv1.2 --http1.1 \ + --retry 3 --retry-delay 3 --connect-timeout 30 \ + --output "$WIN_ISO_DIR/$file_name" "$iso_download_link" || { + handle_curl_error $? + return 1 + } + + if [[ $? -ne 0 ]]; then + print_error "Failed to download the Windows $windows_version ISO." + return 1 + fi + + print_success "Successfully downloaded Windows $windows_version ISO to $WIN_ISO_DIR/$file_name" + + # Return the downloaded filename so calling code can use it + echo "$file_name" + return 0 +} + +# Create unattended installation ISO with proper Windows 11 bypass +create_unattended_iso() { + print_info "Creating unattended installation ISO..." + + # Create enhanced autounattend.xml with Windows 11 TPM/Secure Boot bypass + if [ ! -f "$WIN_ISO_DIR/unattended/autounattend.xml" ]; then + print_info "Creating enhanced autounattend.xml with Windows 11 bypass..." + mkdir -p "$WIN_ISO_DIR/unattended" + cat >"$WIN_ISO_DIR/unattended/autounattend.xml" <<'EOF' +<?xml version="1.0" encoding="utf-8"?> +<unattend xmlns="urn:schemas-microsoft-com:unattend"> + <settings pass="windowsPE"> + <component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <SetupUILanguage> + <UILanguage>en-US</UILanguage> + </SetupUILanguage> + <InputLocale>en-US</InputLocale> + <SystemLocale>en-US</SystemLocale> + <UILanguage>en-US</UILanguage> + <UILanguageFallback>en-US</UILanguageFallback> + <UserLocale>en-US</UserLocale> + </component> + <component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <DiskConfiguration> + <Disk wcm:action="add"> + <CreatePartitions> + <CreatePartition wcm:action="add"> + <Order>1</Order> + <Type>Primary</Type> + <Size>100</Size> + </CreatePartition> + <CreatePartition wcm:action="add"> + <Order>2</Order> + <Type>EFI</Type> + <Size>100</Size> + </CreatePartition> + <CreatePartition wcm:action="add"> + <Order>3</Order> + <Type>MSR</Type> + <Size>16</Size> + </CreatePartition> + <CreatePartition wcm:action="add"> + <Order>4</Order> + <Type>Primary</Type> + <Extend>true</Extend> + </CreatePartition> + </CreatePartitions> + <ModifyPartitions> + <ModifyPartition wcm:action="add"> + <Order>1</Order> + <PartitionID>1</PartitionID> + <Label>WINRE</Label> + <Format>NTFS</Format> + <TypeID>DE94BBA4-06D1-4D40-A16A-BFD50179D6AC</TypeID> + </ModifyPartition> + <ModifyPartition wcm:action="add"> + <Order>2</Order> + <PartitionID>2</PartitionID> + <Label>System</Label> + <Format>FAT32</Format> + </ModifyPartition> + <ModifyPartition wcm:action="add"> + <Order>3</Order> + <PartitionID>3</PartitionID> + </ModifyPartition> + <ModifyPartition wcm:action="add"> + <Order>4</Order> + <PartitionID>4</PartitionID> + <Label>Windows</Label> + <Format>NTFS</Format> + </ModifyPartition> + </ModifyPartitions> + <DiskID>0</DiskID> + <WillWipeDisk>true</WillWipeDisk> + </Disk> + </DiskConfiguration> + <ImageInstall> + <OSImage> + <InstallTo> + <DiskID>0</DiskID> + <PartitionID>4</PartitionID> + </InstallTo> + <InstallToAvailablePartition>false</InstallToAvailablePartition> + <WillShowUI>OnError</WillShowUI> + <InstallFrom> + <MetaData wcm:action="add"> + <Key>/IMAGE/INDEX</Key> + <Value>6</Value> + </MetaData> + </InstallFrom> + </OSImage> + </ImageInstall> + <UserData> + <AcceptEula>true</AcceptEula> + <FullName>Windows User</FullName> + <Organization>Windows</Organization> + <ProductKey> + <WillShowUI>Never</WillShowUI> + </ProductKey> + </UserData> + <!-- Windows 11 Requirements Bypass --> + <RunSynchronous> + <RunSynchronousCommand wcm:action="add"> + <Order>1</Order> + <Path>reg add HKLM\SYSTEM\Setup\LabConfig /v BypassTPMCheck /t REG_DWORD /d 1 /f</Path> + </RunSynchronousCommand> + <RunSynchronousCommand wcm:action="add"> + <Order>2</Order> + <Path>reg add HKLM\SYSTEM\Setup\LabConfig /v BypassSecureBootCheck /t REG_DWORD /d 1 /f</Path> + </RunSynchronousCommand> + <RunSynchronousCommand wcm:action="add"> + <Order>3</Order> + <Path>reg add HKLM\SYSTEM\Setup\LabConfig /v BypassRAMCheck /t REG_DWORD /d 1 /f</Path> + </RunSynchronousCommand> + <RunSynchronousCommand wcm:action="add"> + <Order>4</Order> + <Path>reg add HKLM\SYSTEM\Setup\LabConfig /v BypassStorageCheck /t REG_DWORD /d 1 /f</Path> + </RunSynchronousCommand> + <RunSynchronousCommand wcm:action="add"> + <Order>5</Order> + <Path>reg add HKLM\SYSTEM\Setup\LabConfig /v BypassCPUCheck /t REG_DWORD /d 1 /f</Path> + </RunSynchronousCommand> + </RunSynchronous> + <EnableFirewall>false</EnableFirewall> + <EnableNetwork>true</EnableNetwork> + </component> + </settings> + <settings pass="specialize"> + <component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <InputLocale>en-US</InputLocale> + <SystemLocale>en-US</SystemLocale> + <UILanguage>en-US</UILanguage> + <UILanguageFallback>en-US</UILanguageFallback> + <UserLocale>en-US</UserLocale> + </component> + <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <ComputerName>DESKTOP-PC</ComputerName> + <TimeZone>UTC</TimeZone> + </component> + <component name="Microsoft-Windows-Security-SPP-UX" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <SkipAutoActivation>true</SkipAutoActivation> + </component> + </settings> + <settings pass="oobeSystem"> + <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <AutoLogon> + <Password> + <Value>password</Value> + <PlainText>true</PlainText> + </Password> + <LogonCount>1</LogonCount> + <Username>Administrator</Username> + <Enabled>true</Enabled> + </AutoLogon> + <OOBE> + <HideEULAPage>true</HideEULAPage> + <HideLocalAccountScreen>true</HideLocalAccountScreen> + <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen> + <HideOnlineAccountScreens>true</HideOnlineAccountScreens> + <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE> + <NetworkLocation>Home</NetworkLocation> + <ProtectYourPC>1</ProtectYourPC> + <SkipMachineOOBE>true</SkipMachineOOBE> + <SkipUserOOBE>true</SkipUserOOBE> + </OOBE> + <UserAccounts> + <AdministratorPassword> + <Value>password</Value> + <PlainText>true</PlainText> + </AdministratorPassword> + <LocalAccounts> + <LocalAccount wcm:action="add"> + <Password> + <Value>password</Value> + <PlainText>true</PlainText> + </Password> + <Name>User</Name> + <Group>Administrators</Group> + <DisplayName>User</DisplayName> + </LocalAccount> + </LocalAccounts> + </UserAccounts> + <!-- Additional Windows 11 bypass commands --> + <FirstLogonCommands> + <SynchronousCommand wcm:action="add"> + <Order>1</Order> + <CommandLine>reg add HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU /v NoAutoUpdate /t REG_DWORD /d 1 /f</CommandLine> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <Order>2</Order> + <CommandLine>reg add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v EnableLUA /t REG_DWORD /d 0 /f</CommandLine> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <Order>3</Order> + <CommandLine>powershell -Command "Set-ExecutionPolicy Unrestricted -Force"</CommandLine> + </SynchronousCommand> + </FirstLogonCommands> + </component> + </settings> +</unattend> +EOF + fi + + # Create the unattended ISO + if command -v genisoimage >/dev/null 2>&1; then + print_info "Creating unattended ISO using genisoimage..." + genisoimage -J -r -o "$WIN_ISO_DIR/unattended.iso" "$WIN_ISO_DIR/unattended" 2>/dev/null + return $? + elif command -v mkisofs >/dev/null 2>&1; then + print_info "Creating unattended ISO using mkisofs..." + mkisofs -J -r -o "$WIN_ISO_DIR/unattended.iso" "$WIN_ISO_DIR/unattended" 2>/dev/null + return $? + elif command -v xorriso >/dev/null 2>&1; then + print_info "Creating unattended ISO using xorriso..." + xorriso -as genisoimage -J -r -o "$WIN_ISO_DIR/unattended.iso" "$WIN_ISO_DIR/unattended" 2>/dev/null + return $? + else + print_warning "No ISO creation tool found (genisoimage, mkisofs, or xorriso)" + print_warning "Installing genisoimage..." + if command -v apt-get >/dev/null 2>&1; then + sudo apt-get update && sudo apt-get install -y genisoimage + elif command -v yum >/dev/null 2>&1; then + sudo yum install -y genisoimage + elif command -v pacman >/dev/null 2>&1; then + sudo pacman -S --noconfirm cdrtools + fi + + # Try again after installation + if command -v genisoimage >/dev/null 2>&1; then + print_info "Creating unattended ISO using newly installed genisoimage..." + genisoimage -J -r -o "$WIN_ISO_DIR/unattended.iso" "$WIN_ISO_DIR/unattended" 2>/dev/null + return $? + else + print_error "Failed to install ISO creation tools" + return 1 + fi + fi +} + +# Download or locate essential files +prepare_files() { + # Find Windows ISO + WINDOWS_ISO_PATH=$(find_windows_iso | tail -n 1 | xargs) + local iso_found=false + + # Check if we found a valid ISO + if [[ -n "$WINDOWS_ISO_PATH" && -f "$WINDOWS_ISO_PATH" ]]; then + print_success "Using Windows ISO: $WINDOWS_ISO_PATH" + iso_found=true + else + print_warning "Windows ISO not found, attempting to download..." + if download_windows_iso; then + # Check again after download attempt + WINDOWS_ISO_PATH=$(find_windows_iso | tail -n 1 | xargs) + if [[ -n "$WINDOWS_ISO_PATH" && -f "$WINDOWS_ISO_PATH" ]]; then + print_success "Using downloaded Windows ISO: $WINDOWS_ISO_PATH" + iso_found=true + fi + fi + fi + + # If we still don't have an ISO, exit + if [[ "$iso_found" != "true" ]]; then + print_error "Could not find or download Windows ISO" + print_info "Please place your Windows ISO file in: $WIN_ISO_DIR" + exit 1 + fi + + # Download VirtIO drivers if missing (optional for stealth mode) + if [[ ! -f "$ISO_VIRTIO" ]]; then + print_info "VirtIO drivers ISO not found, downloading (for fallback)..." + download_file "$VIRTIO_ISO_URL" "$ISO_VIRTIO" "" "true" + else + print_success "VirtIO drivers ISO already exists: $ISO_VIRTIO" + fi + + # Create unattended ISO if it doesn't exist + if [[ ! -f "$ISO_UNATTENDED" ]]; then + create_unattended_iso + if [[ $? -eq 0 ]]; then + print_success "Created unattended ISO: $ISO_UNATTENDED" + else + print_error "Failed to create unattended ISO" + exit 1 + fi + else + print_success "Unattended ISO already exists: $ISO_UNATTENDED" + fi +} + +# Locate OVMF firmware files +locate_ovmf() { + OVMF_DIRS=( + "/usr/share/OVMF" + "/usr/share/qemu" + "/usr/lib/qemu" + "/usr/share/edk2" + "/usr/lib/edk2" + "/usr/share/edk2/ovmf" + "/usr/share/edk2-ovmf" + "/usr/share/qemu/edk2-x86_64-code.fd" + ) + + OVMF_CODE="" + OVMF_VARS="" + + for dir in "${OVMF_DIRS[@]}"; do + if [[ -f "$dir" ]]; then + # Handle direct file paths + if [[ "$dir" == *"code.fd" ]]; then + OVMF_CODE="$dir" + OVMF_VARS="$(dirname "$dir")/edk2-x86_64-vars.fd" + fi + elif [[ -d "$dir" ]]; then + # Handle directories + [[ -z "$OVMF_CODE" ]] && OVMF_CODE=$(find "$dir" -type f -name "OVMF_CODE.fd" -o -name "edk2-x86_64-code.fd" 2>/dev/null | head -n 1) + [[ -z "$OVMF_VARS" ]] && OVMF_VARS=$(find "$dir" -type f -name "OVMF_VARS.fd" -o -name "edk2-x86_64-vars.fd" 2>/dev/null | head -n 1) + fi + [[ -n "$OVMF_CODE" && -n "$OVMF_VARS" ]] && break + done + + # Try package-specific locations + if [[ -z "$OVMF_CODE" || -z "$OVMF_VARS" ]]; then + # Ubuntu/Debian locations + [[ -z "$OVMF_CODE" ]] && OVMF_CODE="/usr/share/OVMF/OVMF_CODE_4M.fd" + [[ -z "$OVMF_VARS" && -f "/usr/share/OVMF/OVMF_VARS_4M.fd" ]] && OVMF_VARS="/usr/share/OVMF/OVMF_VARS_4M.fd" + + # Arch Linux locations + [[ -z "$OVMF_CODE" ]] && OVMF_CODE="/usr/share/edk2-ovmf/x64/OVMF_CODE.fd" + [[ -z "$OVMF_VARS" && -f "/usr/share/edk2-ovmf/x64/OVMF_VARS.fd" ]] && OVMF_VARS="/usr/share/edk2-ovmf/x64/OVMF_VARS.fd" + fi + + # Ensure a writable copy of OVMF_VARS.fd + local original_ovmf_vars="$OVMF_VARS" + OVMF_VARS="$FIRMWARE_DIR/OVMF_VARS.fd" + + if [[ ! -f "$OVMF_VARS" && -f "$original_ovmf_vars" ]]; then + print_info "Copying OVMF_VARS.fd to $OVMF_VARS" + cp "$original_ovmf_vars" "$OVMF_VARS" 2>/dev/null || { + print_error "Failed to copy OVMF_VARS.fd!" + print_info "Trying to install OVMF firmware..." + + # Try to install OVMF + if command -v apt-get >/dev/null 2>&1; then + sudo apt-get update && sudo apt-get install -y ovmf + elif command -v yum >/dev/null 2>&1; then + sudo yum install -y edk2-ovmf + elif command -v pacman >/dev/null 2>&1; then + sudo pacman -S --noconfirm edk2-ovmf + fi + + # Try to locate again after installation + locate_ovmf + return + } + fi + + # Check if required files exist + if [[ -z "$OVMF_CODE" || ! -f "$OVMF_CODE" ]]; then + print_error "OVMF_CODE.fd not found!" + print_info "Please install OVMF firmware package:" + print_info " Ubuntu/Debian: sudo apt install ovmf" + print_info " RHEL/CentOS: sudo yum install edk2-ovmf" + print_info " Arch: sudo pacman -S edk2-ovmf" + exit 1 + fi + if [[ ! -f "$OVMF_VARS" ]]; then + print_error "OVMF_VARS.fd not found or could not be copied!" + exit 1 + fi + + print_success "Found OVMF firmware: $OVMF_CODE" + print_success "Using OVMF vars: $OVMF_VARS" +} + +# Create VM disk image +create_disk() { + # Check if the qcow2 image file exists; if not, create it + if [[ ! -f "$QCOW2_FILE" ]]; then + print_info "Creating $QCOW2_FILE with a size of $VM_SIZE" + qemu-img create -f qcow2 "$QCOW2_FILE" "$VM_SIZE" || { + print_error "Failed to create qcow2 image!" + exit 1 + } + print_success "Created VM disk image: $QCOW2_FILE" + else + print_success "VM disk image already exists: $QCOW2_FILE" + fi +} + +# Helper function to convert QCOW2 to RAW for stealth +convert_to_raw() { + if [[ -f "$QCOW2_FILE" && ! -f "$RAW_FILE" ]]; then + print_info "Converting QCOW2 to RAW format for stealth..." + qemu-img convert -f qcow2 -O raw "$QCOW2_FILE" "$RAW_FILE" || { + print_error "Failed to convert to RAW format" + exit 1 + } + print_success "Successfully converted to RAW format: $RAW_FILE" + elif [[ ! -f "$RAW_FILE" ]]; then + print_info "Creating RAW disk image..." + qemu-img create -f raw "$RAW_FILE" "$VM_SIZE" || { + print_error "Failed to create RAW disk image" + exit 1 + } + print_success "Created RAW disk image: $RAW_FILE" + else + print_success "RAW disk image already exists: $RAW_FILE" + fi +} + +# Check dependencies +check_dependencies() { + local missing_deps=() + + # Check for essential tools + command -v qemu-system-x86_64 >/dev/null 2>&1 || missing_deps+=("qemu-system-x86_64") + command -v swtpm >/dev/null 2>&1 || missing_deps+=("swtpm") + command -v curl >/dev/null 2>&1 || missing_deps+=("curl") + command -v uuidgen >/dev/null 2>&1 || missing_deps+=("uuidgen") + command -v openssl >/dev/null 2>&1 || missing_deps+=("openssl") + + if [[ ${#missing_deps[@]} -gt 0 ]]; then + print_error "Missing required dependencies: ${missing_deps[*]}" + print_info "Please install them using your package manager:" + print_info " Ubuntu/Debian: sudo apt install qemu-system-x86 swtpm curl uuid-runtime openssl" + print_info " RHEL/CentOS: sudo yum install qemu-kvm swtpm curl util-linux openssl" + print_info " Arch: sudo pacman -S qemu swtpm curl util-linux openssl" + exit 1 + fi +} + +start_vm() { + # Verify that we have a Windows ISO + if [[ -z "$WINDOWS_ISO_PATH" || ! -f "$WINDOWS_ISO_PATH" ]]; then + print_error "Windows ISO file not found. Cannot start VM." + print_info "Please download Windows 11 ISO and save it to: $WIN_ISO_DIR" + exit 1 + fi + + # Anti-detection: Generate realistic hardware identifiers + REAL_MAC="00:1A:79:$(openssl rand -hex 3 | sed 's/../&:/g; s/:$//')" # Dell OUI + REAL_SERIAL="$(openssl rand -hex 8 | tr '[:lower:]' '[:upper:]')" + REAL_UUID="$(uuidgen)" + REAL_VENDOR="Dell Inc." + REAL_PRODUCT="OptiPlex 7090" + REAL_VERSION="01" + REAL_FAMILY="OptiPlex" + + # Stop any existing swtpm process for this VM + if [[ -f "$TPM_SOCKET" ]]; then + print_info "Cleaning up existing TPM socket..." + rm -f "$TPM_SOCKET" + fi + + # Start swtpm (TPM emulator) + print_info "Starting TPM emulator..." + /sbin/swtpm socket \ + --ctrl type=unixio,path="$TPM_SOCKET" \ + --terminate \ + --tpmstate dir="$TPM_DIR" \ + --tpm2 & + + # Give swtpm a moment to create the socket + sleep 1 + + # Verify TPM socket exists + if [[ ! -S "$TPM_SOCKET" ]]; then + print_error "TPM socket not created: $TPM_SOCKET" + exit 1 + fi + + print_info "Starting stealth Windows 11 VM..." + print_info "Using Windows ISO: $WINDOWS_ISO_PATH" + print_info "VM will boot from unattended installation ISO" + print_info "Default login: Username=User, Password=password" + print_info "Anti-detection measures enabled" + + # Build qemu arguments in an array for safety and readability + QEMU_ARGS=( + # Basic VM configuration + -name "$REAL_PRODUCT",process="$VM_NAME" + -machine q35,hpet=off,smm=on,vmport=off,accel=kvm + + # Global optimizations + -global kvm-pit.lost_tick_policy=discard + -global ICH9-LPC.disable_s3=1 + + # CPU configuration (stealth) + -cpu host,-hypervisor,+invtsc,+ssse3,l3-cache=on,migratable=no + -smp "$SMP_CONFIG" + -m "$VM_RAM" + + # SMBIOS spoofing for anti-detection + -smbios type=0,vendor="$REAL_VENDOR",version="$REAL_VERSION",date="03/15/2023" + -smbios type=1,manufacturer="$REAL_VENDOR",product="$REAL_PRODUCT",version="$REAL_VERSION",serial="$REAL_SERIAL",uuid="$REAL_UUID",family="$REAL_FAMILY" + -smbios type=2,manufacturer="$REAL_VENDOR",product="0NK70N",version="A00",serial="$REAL_SERIAL" + -smbios type=3,manufacturer="$REAL_VENDOR",version="$REAL_VERSION",serial="$REAL_SERIAL" + -smbios type=4,manufacturer="Intel(R) Corporation",version="11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz" + + # Process management + -pidfile "$VM_DIR/$VM_NAME.pid" + -rtc base=localtime,clock=host,driftfix=slew + + # Display and graphics + -vga qxl + -display sdl + + # Boot configuration + -boot menu=on,splash-time=0,order=d,reboot-timeout=5000 + + # Random number generator + -object rng-random,id=rng0,filename=/dev/urandom + -device i82801b11-bridge,id=pci.1 + + # usb + -device usb-ehci,id=usb + -device usb-kbd,bus=usb.0 + -device usb-tablet,bus=usb.0 + -k en-us + + # audio (pipewire) + -audiodev pipewire,id=audio0 + -device AC97,audiodev=audio0 + + # Network (realistic NIC with stealth MAC) + -device rtl8139,netdev=nic,mac="$REAL_MAC" + -netdev user,hostname="$VM_NAME",hostfwd=tcp::"$HOST_PORT"-:"$GUEST_PORT",smb="$SHARED_DIR",id=nic + + # UEFI firmware + -global driver=cfi.pflash01,property=secure,value=on + -drive if=pflash,format=raw,unit=0,file="$OVMF_CODE",readonly=on + -drive if=pflash,format=raw,unit=1,file="$OVMF_VARS" + + # Main storage (AHCI for compatibility) + -device ahci,id=ahci + -drive if=none,id=disk0,format=raw,file="$RAW_FILE",cache=writeback + -device ide-hd,bus=ahci.0,drive=disk0 + + # CD-ROM drives (Windows ISO + Unattended) + -drive media=cdrom,index=0,file="$WINDOWS_ISO_PATH",if=ide + -drive media=cdrom,index=1,file="$ISO_UNATTENDED",if=ide + + # TPM 2.0 + -chardev socket,id=chrtpm,path="$TPM_SOCKET" + -tpmdev emulator,id=tpm0,chardev=chrtpm + -device tpm-tis,tpmdev=tpm0 + + # Monitoring and management sockets + -monitor unix:"$SOCKET_DIR/$VM_NAME-monitor.socket",server,nowait + -serial unix:"$SOCKET_DIR/$VM_NAME-serial.socket",server,nowait + ) + + # Add VirtIO ISO if it exists (as fallback) + if [[ -f "$ISO_VIRTIO" ]]; then + QEMU_ARGS+=(-drive media=cdrom,index=2,file="$ISO_VIRTIO",if=ide,readonly=on) + fi + + print_info "Starting QEMU with stealth configuration..." + print_info "Monitor socket: $SOCKET_DIR/$VM_NAME-monitor.socket" + print_info "Serial socket: $SOCKET_DIR/$VM_NAME-serial.socket" + print_info "SSH port forwarding: localhost:$HOST_PORT -> VM:$GUEST_PORT" + + # Execute qemu + exec qemu-system-x86_64 "${QEMU_ARGS[@]}" + +} + +# Helper function to convert QCOW2 to RAW for stealth +convert_to_raw() { + if [[ -f "$QCOW2_FILE" && ! -f "$RAW_FILE" ]]; then + print_info "Converting QCOW2 to RAW format for stealth..." + qemu-img convert -f qcow2 -O raw "$QCOW2_FILE" "$RAW_FILE" + if [[ $? -eq 0 ]]; then + print_success "Successfully converted to RAW format" + else + print_error "Failed to convert to RAW format" + exit 1 + fi + elif [[ ! -f "$RAW_FILE" ]]; then + print_info "Creating RAW disk image..." + qemu-img create -f raw "$RAW_FILE" "$VM_SIZE" + fi +} + +# Anti-detection validation commands +show_validation_commands() { + cat << 'EOF' +=== VM DETECTION VALIDATION COMMANDS === + +Run these commands inside Windows to verify stealth: + +# PowerShell - Check hardware info: +Get-WmiObject -Class Win32_ComputerSystem | Select-Object Manufacturer, Model, TotalPhysicalMemory +Get-WmiObject -Class Win32_BIOS | Select-Object Manufacturer, Version, SerialNumber +Get-WmiObject -Class Win32_BaseBoard | Select-Object Manufacturer, Product, SerialNumber +Get-WmiObject -Class Win32_Processor | Select-Object Name, Manufacturer, MaxClockSpeed + +# Check for hypervisor presence: +Get-WmiObject -Class Win32_ComputerSystem | Select-Object HypervisorPresent +bcdedit /enum | findstr hypervisorlaunchtype + +# Registry checks for VM artifacts: +reg query "HKLM\HARDWARE\DESCRIPTION\System" /v SystemBiosVersion +reg query "HKLM\HARDWARE\DESCRIPTION\System\BIOS" /v SystemManufacturer +reg query "HKLM\SYSTEM\CurrentControlSet\Services" | findstr -i "vbox\|vmware\|qemu\|virtio" + +# Check network adapter: +Get-WmiObject -Class Win32_NetworkAdapter | Where-Object {$_.NetConnectionStatus -eq 2} | Select-Object Name, MACAddress, Manufacturer + +# Check PCI devices for VM signatures: +Get-WmiObject -Class Win32_PnPEntity | Where-Object {$_.Name -match "VirtIO|QEMU|VMware|VirtualBox|Hyper-V"} | Select-Object Name, DeviceID + +# Check running services: +Get-Service | Where-Object {$_.Name -match "vbox|vmware|qemu|virtio|spice"} + +# Check for VM-specific processes: +Get-Process | Where-Object {$_.ProcessName -match "vbox|vmware|qemu|virtio"} + +# Additional checks: +systeminfo | findstr /C:"System Manufacturer" /C:"System Model" /C:"BIOS Version" +wmic computersystem get manufacturer,model,name,systemtype + +EOF +} + +# Cleanup function +cleanup() { + print_info "Cleaning up..." + + # Kill swtpm if running + if [[ -f "$TPM_SOCKET" ]]; then + pkill -f "swtpm.*$TPM_SOCKET" 2>/dev/null || true + rm -f "$TPM_SOCKET" 2>/dev/null || true + fi + + # Remove PID file + if [[ -f "$VM_DIR/$VM_NAME.pid" ]]; then + rm -f "$VM_DIR/$VM_NAME.pid" 2>/dev/null || true + fi +} + +# Trap cleanup on exit +trap cleanup EXIT + +# Main execution function +main() { + print_info "=== Windows 11 Stealth VM Setup ===" + print_info "VM Name: $VM_NAME" + print_info "VM Size: $VM_SIZE" + print_info "VM RAM: $VM_RAM" + print_info "VM CPUs: $VM_CPU" + print_info "Host Port: $HOST_PORT" + + check_dependencies + prepare_files + locate_ovmf + create_disk + convert_to_raw + show_validation_commands + + print_success "Setup complete! Starting VM..." + start_vm +} + +# Run main function +main "$@" diff --git a/unix/virt/server.sh b/unix/virt/server.sh new file mode 100755 index 0000000..6d937fd --- /dev/null +++ b/unix/virt/server.sh @@ -0,0 +1,128 @@ +#!/usr/bin/env bash + +# Set variables +HOST_DIR="machines" +VM_NAME="proxmox" +VM_DIR="$HOME/machines/vm" +IMAGE_DIR="$HOME/machines/images" +SOCKET_DIR="$VM_DIR" +#ISO_FILE="$IMAGE_DIR/proxmox-ve_8.3-1.iso" +ISO_FILE=$(find "$IMAGE_DIR" -type f -iname "proxmox*.iso" -exec stat --format="%Y %n" {} \; | sort -n | tail -n 1 | cut -d' ' -f2-) +QCOW2_FILE="$VM_DIR/$VM_NAME.qcow2" +HOST_PORT=22220 +GUEST_PORT=22 +SHARED_DIR="$HOME/machines/shared" +FIRMWARE_DIR="$HOME/machines/firmware" +VM_SIZE="300G" # Disk size in GB +VM_RAM="12G" # RAM size +VM_CPU="6" # Number of virtual CPUs +CORES=$((VM_CPU / 2)) +THREADS_PER_CORE=2 +SOCKETS=1 + +# Set SMP configuration +SMP_CONFIG="cores=$CORES,threads=$THREADS_PER_CORE,sockets=$SOCKETS" + +# Ensure necessary directories exist +mkdir -p "$HOME"/"$HOST_DIR" +mkdir -p "$VM_DIR" "$IMAGE_DIR" "$SHARED_DIR" "$FIRMWARE_DIR" + +# Locate OVMF firmware files +OVMF_DIRS=( + "/usr/share/OVMF" + "/usr/share/qemu" + "/usr/lib/qemu" + "/usr/share/edk2" + "/usr/lib/edk2" +) + +OVMF_CODE="" +OVMF_VARS="" + +for dir in "${OVMF_DIRS[@]}"; do + [[ -z "$OVMF_CODE" ]] && OVMF_CODE=$(find "$dir" -type f -name "OVMF_CODE.fd" -o -name "edk2-x86_64-code.fd" 2>/dev/null | head -n 1) + [[ -z "$OVMF_VARS" ]] && OVMF_VARS=$(find "$dir" -type f -name "OVMF_VARS.fd" 2>/dev/null | head -n 1) + [[ -n "$OVMF_CODE" && -n "$OVMF_VARS" ]] && break +done + +# Ensure a writable copy of OVMF_VARS.fd +OVMF_VARS="$FIRMWARE_DIR/OVMF_VARS.fd" + +if [[ ! -f "$OVMF_VARS" ]]; then + echo "Copying OVMF_VARS.fd to $OVMF_VARS" + cp /usr/share/edk2/OvmfX64/OVMF_VARS.fd "$OVMF_VARS" 2>/dev/null || { + echo "Error: Failed to copy OVMF_VARS.fd!" >&2 + exit 1 + } +fi + +# Check if required files exist +if [[ -z "$OVMF_CODE" ]]; then + echo "Error: OVMF_CODE.fd not found!" >&2 + exit 1 +fi +if [[ ! -f "$OVMF_VARS" ]]; then + echo "Error: OVMF_VARS.fd not found or could not be copied!" >&2 + exit 1 +fi +if [[ ! -f "$ISO_FILE" ]]; then + echo "Warning: $ISO_FILE ISO not found at $IMAGE_DIR" +fi + +# Check if the qcow2 image file exists; if not, create it +if [[ ! -f "$QCOW2_FILE" ]]; then + echo "Creating $QCOW2_FILE with a size of $VM_SIZE" + qemu-img create -f qcow2 "$QCOW2_FILE" "$VM_SIZE" || { + echo "Error: Failed to create qcow2 image!" >&2 + exit 1 + } +else + echo "" +fi + +# Run QEMU +/sbin/qemu-system-x86_64 \ + -name "$VM_NAME",process="$VM_NAME" \ + -machine q35,smm=off,vmport=off,accel=kvm \ + -global kvm-pit.lost_tick_policy=discard \ + -cpu host \ + -smp "$SMP_CONFIG" \ + -m "$VM_RAM" \ + -device virtio-balloon \ + -pidfile "$VM_DIR/$VM_NAME.pid" \ + -rtc base=utc,clock=host \ + -vga none \ + -device virtio-vga-gl,xres=1280,yres=800 \ + -display sdl,gl=on \ + -device virtio-rng-pci,rng=rng0 \ + -object rng-random,id=rng0,filename=/dev/urandom \ + -device qemu-xhci,id=spicepass \ + -chardev spicevmc,id=usbredirchardev1,name=usbredir \ + -device usb-redir,chardev=usbredirchardev1,id=usbredirdev1 \ + -chardev spicevmc,id=usbredirchardev2,name=usbredir \ + -device usb-redir,chardev=usbredirchardev2,id=usbredirdev2 \ + -chardev spicevmc,id=usbredirchardev3,name=usbredir \ + -device usb-redir,chardev=usbredirchardev3,id=usbredirdev3 \ + -device pci-ohci,id=smartpass \ + -device usb-ccid \ + -device usb-ehci,id=input \ + -device usb-kbd,bus=input.0 \ + -k en-us \ + -device usb-tablet,bus=input.0 \ + -audiodev pipewire,id=audio0 \ + -device intel-hda \ + -device hda-micro,audiodev=audio0 \ + -device virtio-net,netdev=nic \ + -netdev user,hostname="$VM_NAME",hostfwd=tcp::"$HOST_PORT"-:"$GUEST_PORT",id=nic \ + -fsdev local,id=fsdev0,path="$SHARED_DIR",security_model=mapped-xattr \ + -device virtio-9p-pci,fsdev=fsdev0,mount_tag="SharedDir" \ + -global driver=cfi.pflash01,property=secure,value=on \ + -drive if=pflash,format=raw,unit=0,file="$OVMF_CODE",readonly=on \ + -drive if=pflash,format=raw,unit=1,file="$OVMF_VARS" \ + -drive media=cdrom,index=0,file="$ISO_FILE" \ + -device virtio-blk-pci,drive=SystemDisk \ + -drive id=SystemDisk,if=none,format=qcow2,file="$QCOW2_FILE" \ + -monitor unix:"$SOCKET_DIR/$VM_NAME-monitor.socket",server,nowait \ + -serial unix:"$SOCKET_DIR/$VM_NAME-serial.socket",server,nowait +#-serial unix:"$SOCKET_DIR/$VM_NAME-serial.socket",server,nowait 2>/dev/null +#-netdev user,hostname="$VM_NAME",hostfwd=tcp::"$HOST_PORT"-:"$GUEST_PORT",smb="$SHARED_DIR",id=nic \ diff --git a/unix/virt/ubuntu b/unix/virt/ubuntu new file mode 100755 index 0000000..c93941d --- /dev/null +++ b/unix/virt/ubuntu @@ -0,0 +1,222 @@ +#!/usr/bin/env bash + +# Set variables +HOST_DIR="$HOME/virt" +VM_DIR="$HOME/virt/machines" +IMAGE_DIR="$HOME/virt/images" +SOCKET_DIR="$VM_DIR" + +BASE_NAME=$(basename "$0" .sh | sed 's/[0-9]*$//') +VM_INDEX=$(basename "$0" .sh | grep -o '[0-9]*$') +VM_INDEX=${VM_INDEX:-1} # default to 1 if no number + +VM_NAME="${BASE_NAME}${VM_INDEX}" +QCOW2_FILE="$VM_DIR/$VM_NAME.qcow2" + +# If the file exists, use it +if [[ -f "$QCOW2_FILE" ]]; then + echo "Using existing VM image: $QCOW2_FILE" +else + # Loop to find first available index if not + while [[ -f "$QCOW2_FILE" ]]; do + ((VM_INDEX++)) + VM_NAME="${BASE_NAME}${VM_INDEX}" + QCOW2_FILE="$VM_DIR/$VM_NAME.qcow2" + done + echo "Creating new VM image: $QCOW2_FILE" + qemu-img create -f qcow2 "$QCOW2_FILE" "$VM_SIZE" || { + echo "Error: Failed to create qcow2 image!" >&2 + exit 1 + } +fi + +#ISO_FILE=$(ls -1t "$IMAGE_DIR"/ubuntu-*-desktop-*-amd64.iso 2>/dev/null | head -n1) +#if [[ -z "$ISO_FILE" ]]; then +# echo "Error: No Ubuntu ISO found in $IMAGE_DIR" >&2 +# exit 1 +#fi +#echo "Using ISO: $ISO_FILE" + +# Directory to remember ISO choices +CHOICES_DIR="$VM_DIR/.iso_choices" +mkdir -p "$CHOICES_DIR" + +CHOICE_FILE="$CHOICES_DIR/$BASE_NAME.choice" + +if [[ -f "$CHOICE_FILE" ]]; then + # Already have a saved ISO for this VM + ISO_FILE=$(<"$CHOICE_FILE") + echo "Using previously selected ISO: $ISO_FILE" +else + # List all ISO files newest first + mapfile -t ISO_LIST < <(ls -1t "$IMAGE_DIR"/*.iso 2>/dev/null) + if [[ ${#ISO_LIST[@]} -eq 0 ]]; then + echo "Error: No ISO files found in $IMAGE_DIR" >&2 + exit 1 + fi + + echo "Available ISOs:" + PS3="Select an ISO to boot (default: 1): " + select ISO_FILE in "${ISO_LIST[@]}"; do + ISO_FILE=${ISO_FILE:-${ISO_LIST[0]}} + echo "Using ISO: $ISO_FILE" + # Save choice so we don’t ask again + echo "$ISO_FILE" > "$CHOICE_FILE" + break + done +fi + +QCOW2_FILE="$VM_DIR/$VM_NAME.qcow2" +GUEST_PORT=22 +SHARED_DIR="$HOST_DIR/shared" +VM_SIZE="60G" # Disk size in GB +VM_RAM="8G" # RAM size +VM_CPU="6" # Number of virtual CPUs +CORES=$((VM_CPU / 2)) +THREADS_PER_CORE=2 +SOCKETS=1 +SHARED_DIR="$HOST_DIR/shared" +FIRMWARE_DIR="$HOST_DIR/firmware" + +SMP_CONFIG="cores=$CORES,threads=$THREADS_PER_CORE,sockets=$SOCKETS" + +# Set SPICE_NOGRAB environment variable +export SPICE_NOGRAB=1 + +# Try to find an available host port starting from 22220 +HOST_PORT_START=22220 +HOST_PORT_END=22300 + +for ((port = HOST_PORT_START; port <= HOST_PORT_END; port++)); do + if ! ss -tuln | grep -q ":$port\b"; then + HOST_PORT=$port + echo "Using available port: $HOST_PORT" + break + fi +done + +if [[ $port -gt $HOST_PORT_END ]]; then + echo "Error: No available ports found between $HOST_PORT_START and $HOST_PORT_END" >&2 + exit 1 +fi + +# Ensure necessary directories exist +mkdir -p "$HOST_DIR" +mkdir -p "$VM_DIR" "$IMAGE_DIR" "$SHARED_DIR" "$FIRMWARE_DIR" + +# Locate OVMF firmware files +OVMF_DIRS=( + "/usr/share/OVMF" + "/usr/share/qemu" + "/usr/lib/qemu" + "/usr/share/edk2" + "/usr/lib/edk2" +) + +OVMF_CODE="" +OVMF_VARS="" + +for dir in "${OVMF_DIRS[@]}"; do + [[ -z "$OVMF_CODE" ]] && OVMF_CODE=$(find "$dir" -type f -name "OVMF_CODE.fd" -o -name "edk2-x86_64-code.fd" 2>/dev/null | head -n 1) + [[ -z "$OVMF_VARS" ]] && OVMF_VARS=$(find "$dir" -type f -name "OVMF_VARS.fd" 2>/dev/null | head -n 1) + [[ -n "$OVMF_CODE" && -n "$OVMF_VARS" ]] && break +done + +# Ensure a writable copy of OVMF_VARS.fd +OVMF_VARS="$IMAGE_DIR/OVMF_VARS.fd" + +if [[ ! -f "$OVMF_VARS" ]]; then + echo "Copying OVMF_VARS.fd to $OVMF_VARS" + cp /usr/share/edk2/OvmfX64/OVMF_VARS.fd "$OVMF_VARS" 2>/dev/null || { + echo "Error: Failed to copy OVMF_VARS.fd!" >&2 + exit 1 + } +fi + +# Check if required files exist +if [[ -z "$OVMF_CODE" ]]; then + echo "Error: OVMF_CODE.fd not found!" >&2 + exit 1 +fi +if [[ ! -f "$OVMF_VARS" ]]; then + echo "Error: OVMF_VARS.fd not found or could not be copied!" >&2 + exit 1 +fi +if [[ ! -f "$ISO_FILE" ]]; then + echo "Warning: $ISO_FILE ISO not found at $IMAGE_DIR" +fi + +# Check if the qcow2 image file exists; if not, create it +if [[ ! -f "$QCOW2_FILE" ]]; then + echo "Creating $QCOW2_FILE with a size of $VM_SIZE" + qemu-img create -f qcow2 "$QCOW2_FILE" "$VM_SIZE" || { + echo "Error: Failed to create qcow2 image!" >&2 + exit 1 + } +else + echo "" +fi + +# Run QEMU +/sbin/qemu-system-x86_64 \ + -name "$VM_NAME",process="$VM_NAME" \ + -machine q35,smm=off,vmport=off,accel=kvm \ + -enable-kvm \ + -global kvm-pit.lost_tick_policy=discard \ + -cpu host \ + -smp "$SMP_CONFIG" \ + -m "$VM_RAM" \ + -device virtio-balloon \ + -pidfile "$VM_DIR/$VM_NAME.pid" \ + -rtc base=utc,clock=host \ + -vga none \ + -device virtio-vga-gl,xres=1280,yres=800 \ + -display sdl,gl=on \ + -device virtio-rng-pci,rng=rng0 \ + -device virtio-serial \ + -object rng-random,id=rng0,filename=/dev/urandom \ + -device qemu-xhci,id=spicepass \ + -chardev spicevmc,id=usbredirchardev1,name=usbredir \ + -device usb-redir,chardev=usbredirchardev1,id=usbredirdev1 \ + -chardev spicevmc,id=usbredirchardev2,name=usbredir \ + -device usb-redir,chardev=usbredirchardev2,id=usbredirdev2 \ + -chardev spicevmc,id=usbredirchardev3,name=usbredir \ + -device usb-redir,chardev=usbredirchardev3,id=usbredirdev3 \ + -device pci-ohci,id=smartpass \ + -device usb-ccid \ + -device usb-ehci,id=input \ + -device usb-kbd,bus=input.0 \ + -k en-us \ + -device usb-tablet,bus=input.0 \ + -audiodev pipewire,id=audio0 \ + -device intel-hda \ + -device hda-micro,audiodev=audio0 \ + -device virtio-net,netdev=nic \ + -netdev user,hostname="$VM_NAME",hostfwd=tcp::"$HOST_PORT"-:"$GUEST_PORT",id=nic \ + -device virtio-9p-pci,fsdev=fsdev0,mount_tag="Public-$(whoami)" \ + -global driver=cfi.pflash01,property=secure,value=on \ + -drive if=pflash,format=raw,unit=0,file="$OVMF_CODE",readonly=on \ + -drive if=pflash,format=raw,unit=1,file="$OVMF_VARS" \ + -drive media=cdrom,index=0,file="$ISO_FILE" \ + -device virtio-blk-pci,drive=SystemDisk \ + -drive id=SystemDisk,if=none,format=qcow2,file="$QCOW2_FILE" \ + -fsdev local,id=fsdev0,path="$SHARED_DIR",security_model=mapped-xattr \ + -monitor unix:"$SOCKET_DIR/$VM_NAME-monitor.socket",server,nowait \ + -serial unix:"$SOCKET_DIR/$VM_NAME-serial.socket",server,nowait + +#-display sdl,gl=on \ +#-display gtk,gl=on \ +#-display gtk,grab-on-hover=on,grab-mod=none \ +#-qmp unix:/tmp/qmp-sock,server,nowait \ +#-qmp unix:"$SOCKET_DIR/$VM_NAME-qmp.socket",server,nowait \ +#-chardev socket,path="$VM_DIR/$VM_NAME-qga.sock",server=on,wait=off,id=qga0 \ + +# Network Isolation: +#-netdev user,hostname="$VM_NAME",restrict=yes,id=nic \ + +# No file-sharing: +#-netdev user,hostname="$VM_NAME",hostfwd=tcp::"$HOST_PORT"-:"$GUEST_PORT",id=nic \ + +# File-sharing and networking: +#-netdev user,hostname="$VM_NAME",hostfwd=tcp::"$HOST_PORT"-:"$GUEST_PORT",smb="$SHARED_DIR",id=nic \ +#-device virtio-9p-pci,fsdev=fsdev0,mount_tag="Public-$(whoami)" \ diff --git a/unix/virt/windows.sh b/unix/virt/windows.sh new file mode 100755 index 0000000..1863f8a --- /dev/null +++ b/unix/virt/windows.sh @@ -0,0 +1,1156 @@ +#!/usr/bin/env bash + +# Windows VM Creation Script +# Description: Creates and manages Windows virtual machines using QEMU/KVM +# Features: +# - Supports Windows 10, 11, and Server +# - Automatic dependency checking +# - Multiple VM instance support +# - Cache management +# - Unattended installation +# - SMB sharing support +# - Network control + +# Usage: ./windows.sh [VERSION] [OPTIONS] +# Versions: +# 10 Windows 10 +# 11 Windows 11 (default) +# server Windows Server 2022 +# server2019 Windows Server 2019 +# server2016 Windows Server 2016 + +# Options: +# --help Show this help message +# --clean Clean dependency cache +# --check-deps Only check dependencies and exit +# --download-iso Download Windows ISO and exit +# --name NAME Set custom VM name +# --ram SIZE Set RAM size (default: 8G) +# --cpu NUM Set number of CPUs (default: 6) +# --disk SIZE Set disk size (default: 80G) +# --enable-smb Enable SMB sharing (default: disabled) +# --disable-net Disable network connectivity (default: enabled) + +# Parse command line arguments +WINDOWS_VERSION="11" +CLEAN_CACHE=false +CHECK_DEPS_ONLY=false +DOWNLOAD_ISO_ONLY=false +VM_NAME="windows-11" +VM_RAM="8G" +VM_CPU="6" +VM_SIZE="80G" +ENABLE_SMB=false +DISABLE_NET=false + +# Handle version argument if it's the first argument +if [[ $# -gt 0 && ! $1 =~ ^-- ]]; then + case "$1" in + "10") + WINDOWS_VERSION="10" + VM_NAME="windows-10" + ;; + "server" | "server2022") + WINDOWS_VERSION="server2022" + VM_NAME="windows-server-2022" + ;; + "server2019") + WINDOWS_VERSION="server2019" + VM_NAME="windows-server-2019" + ;; + "server2016") + WINDOWS_VERSION="server2016" + VM_NAME="windows-server-2016" + ;; + *) + echo "Unknown Windows version: $1" + echo "Use --help for usage information" + exit 1 + ;; + esac + shift +fi + +while [[ $# -gt 0 ]]; do + case $1 in + --help) + echo "Windows VM Creation Script" + echo "Usage: $0 [VERSION] [OPTIONS]" + echo + echo "Versions:" + echo " 10 Windows 10" + echo " 11 Windows 11 (default)" + echo " server Windows Server 2022" + echo " server2019 Windows Server 2019" + echo " server2016 Windows Server 2016" + echo + echo "Options:" + echo " --help Show this help message" + echo " --clean Clean dependency cache" + echo " --check-deps Only check dependencies and exit" + echo " --download-iso Download Windows ISO and exit" + echo " --name NAME Set custom VM name" + echo " --ram SIZE Set RAM size (default: 8G)" + echo " --cpu NUM Set number of CPUs (default: 6)" + echo " --disk SIZE Set disk size (default: 80G)" + echo " --enable-smb Enable SMB sharing (default: disabled)" + echo " --disable-net Disable network connectivity (default: enabled)" + exit 0 + ;; + --clean) + CLEAN_CACHE=true + shift + ;; + --check-deps) + CHECK_DEPS_ONLY=true + shift + ;; + --download-iso) + DOWNLOAD_ISO_ONLY=true + shift + ;; + --name) + VM_NAME="$2" + shift 2 + ;; + --ram) + VM_RAM="$2" + shift 2 + ;; + --cpu) + VM_CPU="$2" + shift 2 + ;; + --disk) + VM_SIZE="$2" + shift 2 + ;; + --enable-smb) + ENABLE_SMB=true + shift + ;; + --disable-net) + DISABLE_NET=true + shift + ;; + *) + echo "Unknown option: $1" + echo "Use --help for usage information" + exit 1 + ;; + esac +done + +# Set variables +HOST_DIR="$HOME/virt-new-new" +VM_DIR="$HOST_DIR/machines" +IMAGE_DIR="$HOST_DIR/images" +WIN_ISO_DIR="${IMAGE_DIR}/${VM_NAME}" # Directory for Windows ISO +SOCKET_DIR="$VM_DIR" +SHARED_DIR="${HOST_DIR}/shared" +FIRMWARE_DIR="${HOST_DIR}/firmware" +TPM_DIR="$WIN_ISO_DIR" +TPM_SOCKET="${WIN_ISO_DIR}/${VM_NAME}.swtpm-sock" +GUEST_PORT=22 +QCOW2_FILE="${VM_DIR}/${VM_NAME}.qcow2" + +# Try to find an available host port starting from 22220 +HOST_PORT_START=22220 +HOST_PORT_END=22300 + +for ((port = HOST_PORT_START; port <= HOST_PORT_END; port++)); do + if ! ss -tuln | grep -q ":$port\b"; then + HOST_PORT=$port + echo "Using available port: $HOST_PORT" + break + fi +done + +if [[ $port -gt $HOST_PORT_END ]]; then + echo "Error: No available ports found between $HOST_PORT_START and $HOST_PORT_END" >&2 + exit 1 +fi + +# Set SMP configuration +CORES=$((VM_CPU / 2)) +THREADS_PER_CORE=2 +SOCKETS=1 +SMP_CONFIG="cores=$CORES,threads=$THREADS_PER_CORE,sockets=$SOCKETS" + +# Create necessary directories +mkdir -p "${HOME}/${HOST_DIR}" +mkdir -p "$IMAGE_DIR" "$SHARED_DIR" "$FIRMWARE_DIR" +mkdir -p "$WIN_ISO_DIR" "$VM_DIR" +mkdir -p "${WIN_ISO_DIR}/unattended" + +# Define ISO paths and URLs +ISO_VIRTIO="${WIN_ISO_DIR}/virtio-win.iso" +ISO_UNATTENDED="${WIN_ISO_DIR}/unattended.iso" + +# Find Windows ISO with flexible pattern matching +find_windows_iso() { + # Check if directory exists + if [[ ! -d "$WIN_ISO_DIR" ]]; then + mkdir -p "$WIN_ISO_DIR" + fi + + # Try to find any Windows ISO using case-insensitive patterns + local found_iso + found_iso=$(find "$WIN_ISO_DIR" -maxdepth 1 -type f \( \ + -iname "*win11*.iso" -o \ + -iname "*win*11*.iso" -o \ + -iname "Win*.iso" -o \ + -iname "Win11*.iso" -o \ + -iname "Win*11*.iso" -o \ + -iname "*windows*11*.iso" -o \ + -iname "*windows11*.iso" \ + \) -exec stat --format="%Y %n" {} \; | sort -n | tail -n 1 | cut -d' ' -f2-) + + if [[ -n "$found_iso" && -f "$found_iso" ]]; then + echo "$found_iso" + return 0 + fi + + return 1 +} + +# Define download URLs +VIRTIO_ISO_URL="https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso" +SPICE_WEBDAVD_URL="https://www.spice-space.org/download/windows/spice-webdavd/spice-webdavd-x64-latest.msi" +SPICE_VDAGENT_URL="https://www.spice-space.org/download/windows/spice-vdagent/spice-vdagent-x64-latest.msi" +SPICE_VDAGENT_FALLBACK_URL="https://www.spice-space.org/download/windows/spice-vdagent/spice-vdagent-x64-0.10.0.msi" +USBDK_URL="https://www.spice-space.org/download/windows/usbdk/UsbDk_1.0.22_x64.msi" + +# Fido download URL (Windows ISO downloader) +#FIDO_URL="https://github.com/pbatard/Fido/raw/master/Fido.ps1" +#FIDO_PATH="$WIN_DIR/Fido.ps1" + +# Print colored messages +print_info() { echo -e "\033[1;34m[INFO]\033[0m $1" >&2; } +print_success() { echo -e "\033[1;32m[SUCCESS]\033[0m $1" >&2; } +print_warning() { echo -e "\033[1;33m[WARNING]\033[0m $1" >&2; } +print_error() { echo -e "\033[1;31m[ERROR]\033[0m $1" >&2; } + +# Helper: verify file integrity +verify_file() { + local file="$1" + local expected_sha256="$2" + + if [[ ! -f "$file" ]]; then + return 1 + fi + + if [[ -n "$expected_sha256" ]]; then + local actual_sha256 + actual_sha256=$(sha256sum "$file" | cut -d' ' -f1) + if [[ "$actual_sha256" != "$expected_sha256" ]]; then + print_error "File integrity check failed for $file" + return 1 + fi + fi + + return 0 +} + +# Helper: download file with verification +download_file() { + local url="$1" + local dest="$2" + local expected_sha256="$3" + local allow_failure="$4" + + # Check if file exists and is valid + if [[ -f "$dest" ]]; then + if verify_file "$dest" "$expected_sha256"; then + print_info "File $dest already exists and verified." + return 0 + else + print_warning "File $dest exists but failed verification. Redownloading..." + rm -f "$dest" + fi + fi + + print_info "Downloading $url..." + if ! curl -fL --progress-bar -o "$dest" "$url"; then + print_error "Failed to download $url." + if [[ "$allow_failure" != "true" ]]; then + return 1 + fi + else + # Verify downloaded file + if ! verify_file "$dest" "$expected_sha256"; then + print_error "Downloaded file failed verification" + rm -f "$dest" + return 1 + fi + print_success "Successfully downloaded and verified $dest" + fi + + return 0 +} + +# Download Windows 11 ISO using Microsoft's API +download_windows_iso() { + local windows_version="11" # Default to Windows 11 + local language="English (United States)" # Default language + + # Parse arguments if provided + if [[ -n "$1" ]]; then + windows_version="$1" + fi + + print_info "Attempting to download Windows $windows_version ISO from Microsoft..." + + # Set required variables + local user_agent="Mozilla/5.0 (X11; Linux x86_64; rv:100.0) Gecko/20100101 Firefox/100.0" + local session_id="$(uuidgen)" + local profile="606624d44113" + local url="https://www.microsoft.com/en-us/software-download/windows$windows_version" + + # Add ISO to URL for Windows 10 + case "$windows_version" in + 10) url="${url}ISO" ;; + esac + + # Step 1: Get download page HTML + print_info "Fetching download page: $url" + local iso_download_page_html + iso_download_page_html="$(curl --disable --silent --user-agent "$user_agent" --header "Accept:" --max-filesize 1M --fail --proto =https --tlsv1.2 --http1.1 -- "$url")" || { + handle_curl_error $? + print_error "Failed to fetch the download page. Please download Windows $windows_version ISO manually from $url" + return 1 + } + + # Step 2: Extract Product Edition ID + print_info "Getting Product Edition ID..." + local product_edition_id + product_edition_id="$(echo "$iso_download_page_html" | grep -Eo '<option value="[0-9]+">Windows' | cut -d '"' -f 2 | head -n 1 | tr -cd '0-9' | head -c 16)" + + if [[ -z "$product_edition_id" ]]; then + print_error "Failed to extract product edition ID." + print_error "Please download Windows $windows_version ISO manually from $url" + return 1 + fi + + print_success "Product Edition ID: $product_edition_id" + + # Step 3: Register session ID + print_info "Registering session ID: $session_id" + curl --disable --silent --output /dev/null --user-agent "$user_agent" \ + --header "Accept:" --max-filesize 100K --fail --proto =https --tlsv1.2 \ + --http1.1 -- "https://vlscppe.microsoft.com/tags?org_id=y6jn8c31&session_id=$session_id" || { + print_error "Failed to register session ID." + return 1 + } + + # Step 4: Get language SKU ID + print_info "Getting language SKU ID..." + local language_skuid_table_json + language_skuid_table_json="$(curl --disable -s --fail --max-filesize 100K --proto =https --tlsv1.2 --http1.1 \ + "https://www.microsoft.com/software-download-connector/api/getskuinformationbyproductedition?profile=${profile}&ProductEditionId=${product_edition_id}&SKU=undefined&friendlyFileName=undefined&Locale=en-US&sessionID=${session_id}")" || { + handle_curl_error $? + print_error "Failed to get language SKU information." + return 1 + } + + # Extract SKU ID for selected language + local sku_id + + # Try with jq if available (more reliable) + if command -v jq >/dev/null 2>&1; then + sku_id="$(echo "$language_skuid_table_json" | jq -r '.Skus[] | select(.LocalizedLanguage=="'"$language"'" or .Language=="'"$language"'").Id')" + else + # Fallback to grep/cut if jq not available + sku_id="$(echo "$language_skuid_table_json" | grep -o '"Id":"[^"]*","Language":"'"$language"'"' | cut -d'"' -f4)" + + if [[ -z "$sku_id" ]]; then + # Try alternative extraction method + sku_id="$(echo "$language_skuid_table_json" | grep -o '"LocalizedLanguage":"'"$language"'","Id":"[^"]*"' | cut -d'"' -f6)" + fi + fi + + if [[ -z "$sku_id" ]]; then + print_error "Failed to extract SKU ID for $language." + return 1 + fi + + print_success "SKU ID: $sku_id" + + # Step 5: Get ISO download link + print_info "Getting ISO download link..." + local iso_download_link_json + iso_download_link_json="$(curl --disable -s --fail --referer "$url" \ + "https://www.microsoft.com/software-download-connector/api/GetProductDownloadLinksBySku?profile=${profile}&productEditionId=undefined&SKU=${sku_id}&friendlyFileName=undefined&Locale=en-US&sessionID=${session_id}")" + + local failed=0 + + if [[ -z "$iso_download_link_json" ]]; then + print_error "Microsoft servers gave an empty response to the download request." + failed=1 + fi + + if echo "$iso_download_link_json" | grep -q "Sentinel marked this request as rejected."; then + print_error "Microsoft blocked the automated download request based on your IP address." + failed=1 + fi + + if [[ "$failed" -eq 1 ]]; then + print_warning "Please manually download the Windows $windows_version ISO using a web browser from: $url" + print_warning "Save the downloaded ISO to: $WIN_ISO_DIR" + return 1 + fi + + # Extract 64-bit ISO download URL + local iso_download_link + + # Try with jq if available + if command -v jq >/dev/null 2>&1; then + iso_download_link="$(echo "$iso_download_link_json" | jq -r '.ProductDownloadOptions[].Uri' | grep x64 | head -n 1)" + else + # Fallback to grep/cut if jq not available + iso_download_link="$(echo "$iso_download_link_json" | grep -o '"Uri":"[^"]*x64[^"]*"' | cut -d'"' -f4 | head -n 1)" + fi + + if [[ -z "$iso_download_link" ]]; then + print_error "Failed to extract the download link from Microsoft's response." + print_warning "Manually download the Windows $windows_version ISO using a web browser from: $url" + return 1 + fi + + print_success "Got download link: ${iso_download_link%%\?*}" + + # Extract filename from URL + local file_name="$(echo "$iso_download_link" | cut -d'?' -f1 | rev | cut -d'/' -f1 | rev)" + + # If filename couldn't be extracted, use default + if [[ -z "$file_name" || "$file_name" == "$iso_download_link" ]]; then + file_name="windows-$windows_version.iso" + fi + + # Step 6: Download the ISO + print_info "Downloading Windows $windows_version ISO to $WIN_ISO_DIR/$file_name. This may take a while..." + + # Check which download function to use + if type web_get >/dev/null 2>&1; then + web_get "$iso_download_link" "$WIN_ISO_DIR" "$file_name" + else + # Fallback to direct curl download + curl --disable --progress-bar --fail --location --proto '=https' --tlsv1.2 --http1.1 \ + --retry 3 --retry-delay 3 --connect-timeout 30 \ + --output "$WIN_ISO_DIR/$file_name" "$iso_download_link" || { + handle_curl_error $? + return 1 + } + fi + + if [[ $? -ne 0 ]]; then + print_error "Failed to download the Windows $windows_version ISO." + return 1 + fi + + print_success "Successfully downloaded Windows $windows_version ISO to $WIN_ISO_DIR/$file_name" + + # Return the downloaded filename so calling code can use it + echo "$file_name" + return 0 +} + +# Create unattended installation ISO +create_unattended_iso() { + print_info "Creating unattended installation ISO..." + + # Create basic autounattend.xml if it doesn't exist + if [ ! -f "$WIN_ISO_DIR/unattended/autounattend.xml" ]; then + print_info "Creating autounattend.xml..." + cat >"$WIN_ISO_DIR/unattended/autounattend.xml" <<'EOF' +<?xml version="1.0" encoding="utf-8"?> +<unattend xmlns="urn:schemas-microsoft-com:unattend" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"> + <settings pass="windowsPE"> + <component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> + <SetupUILanguage> + <UILanguage>en-US</UILanguage> + </SetupUILanguage> + <InputLocale>0409:00000409</InputLocale> + <SystemLocale>en-US</SystemLocale> + <UILanguage>en-US</UILanguage> + <UserLocale>en-US</UserLocale> + </component> + <component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> + <ImageInstall> + <OSImage> + <Compact>false</Compact> + <InstallTo> + <DiskID>0</DiskID> + <PartitionID>3</PartitionID> + </InstallTo> + </OSImage> + </ImageInstall> + <UserData> + <AcceptEula>true</AcceptEula> + </UserData> + <UseConfigurationSet>false</UseConfigurationSet> + <RunSynchronous> + <RunSynchronousCommand wcm:action="add"> + <Order>1</Order> + <Path>cmd.exe /c ">>"X:\diskpart.txt" (echo SELECT DISK=0&echo CLEAN&echo CONVERT GPT&echo CREATE PARTITION EFI SIZE=300&echo FORMAT QUICK FS=FAT32 LABEL="System"&echo CREATE PARTITION MSR SIZE=16)"</Path> + </RunSynchronousCommand> + <RunSynchronousCommand wcm:action="add"> + <Order>2</Order> + <Path>cmd.exe /c ">>"X:\diskpart.txt" (echo CREATE PARTITION PRIMARY&echo SHRINK MINIMUM=1000&echo FORMAT QUICK FS=NTFS LABEL="Windows"&echo CREATE PARTITION PRIMARY&echo FORMAT QUICK FS=NTFS LABEL="Recovery")"</Path> + </RunSynchronousCommand> + <RunSynchronousCommand wcm:action="add"> + <Order>3</Order> + <Path>cmd.exe /c ">>"X:\diskpart.txt" (echo SET ID="de94bba4-06d1-4d40-a16a-bfd50179d6ac"&echo GPT ATTRIBUTES=0x8000000000000001)"</Path> + </RunSynchronousCommand> + <RunSynchronousCommand wcm:action="add"> + <Order>4</Order> + <Path>cmd.exe /c "diskpart.exe /s "X:\diskpart.txt" >>"X:\diskpart.log" || ( type "X:\diskpart.log" & echo diskpart encountered an error. & pause & exit /b 1 )"</Path> + </RunSynchronousCommand> + </RunSynchronous> + </component> + </settings> + <settings pass="specialize"> + <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> + <ComputerName>Win11-VM</ComputerName> + <TimeZone>UTC</TimeZone> + </component> + </settings> + <settings pass="oobeSystem"> + <component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> + <InputLocale>0409:00000409</InputLocale> + <SystemLocale>en-US</SystemLocale> + <UILanguage>en-US</UILanguage> + <UserLocale>en-US</UserLocale> + </component> + <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> + <UserAccounts> + <LocalAccounts> + <LocalAccount wcm:action="add"> + <Name>Admin</Name> + <DisplayName></DisplayName> + <Group>Administrators</Group> + <Password> + <Value>password</Value> + <PlainText>true</PlainText> + </Password> + </LocalAccount> + <LocalAccount wcm:action="add"> + <Name>User</Name> + <DisplayName></DisplayName> + <Group>Users</Group> + <Password> + <Value>password</Value> + <PlainText>true</PlainText> + </Password> + </LocalAccount> + </LocalAccounts> + </UserAccounts> + <AutoLogon> + <Username>Admin</Username> + <Enabled>true</Enabled> + <LogonCount>1</LogonCount> + <Password> + <Value>password</Value> + <PlainText>true</PlainText> + </Password> + </AutoLogon> + <OOBE> + <ProtectYourPC>3</ProtectYourPC> + <HideEULAPage>true</HideEULAPage> + <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE> + <HideOnlineAccountScreens>true</HideOnlineAccountScreens> + <HideLocalAccountScreen>true</HideLocalAccountScreen> + <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen> + <SkipMachineOOBE>true</SkipMachineOOBE> + <SkipUserOOBE>true</SkipUserOOBE> + </OOBE> + </component> + </settings> +</unattend> +EOF + fi + + # Create the unattended ISO + local iso_tool="" + + if command -v genisoimage >/dev/null 2>&1; then + iso_tool="genisoimage" + elif command -v mkisofs >/dev/null 2>&1; then + iso_tool="mkisofs" + elif command -v xorriso >/dev/null 2>&1; then + iso_tool="xorriso" + fi + + if [ "$iso_tool" != "" ]; then + print_info "Creating unattended ISO using $iso_tool..." + + if [ "$iso_tool" = "xorriso" ]; then + xorriso -as genisoimage -J -r -o "$WIN_ISO_DIR/unattended.iso" "$WIN_ISO_DIR/unattended" + else + "$iso_tool" -J -r -o "$WIN_ISO_DIR/unattended.iso" "$WIN_ISO_DIR/unattended" + fi + + return $? + else + print_warning "No ISO creation tool found (genisoimage, mkisofs, or xorriso)" + print_warning "Creating empty ISO file as fallback" + touch "$WIN_ISO_DIR/unattended.iso" + return 1 + fi +} + +# Download or locate essential files +prepare_files() { + # Find Windows ISO + WINDOWS_ISO_PATH=$(find_windows_iso | tail -n 1 | xargs) + local iso_found=false + + # Check if we found a valid ISO + if [[ -n "$WINDOWS_ISO_PATH" && -f "$WINDOWS_ISO_PATH" ]]; then + print_success "Using Windows ISO: $WINDOWS_ISO_PATH" + iso_found=true + else + print_warning "Windows ISO not found, attempting to download..." + if download_windows_iso; then + # Check again after download attempt + WINDOWS_ISO_PATH=$(find_windows_iso | tail -n 1 | xargs) + if [[ -n "$WINDOWS_ISO_PATH" && -f "$WINDOWS_ISO_PATH" ]]; then + print_success "Using downloaded Windows ISO: $WINDOWS_ISO_PATH" + iso_found=true + fi + fi + fi + + # If we still don't have an ISO, exit + if [[ "$iso_found" != "true" ]]; then + print_error "Could not find or download Windows ISO" + print_info "Please place your Windows ISO file in: $WIN_ISO_DIR" + exit 1 + fi + + # Download VirtIO drivers if missing + if [[ ! -f "$ISO_VIRTIO" ]]; then + print_info "VirtIO drivers ISO not found, downloading..." + download_file "$VIRTIO_ISO_URL" "$ISO_VIRTIO" + else + print_success "VirtIO drivers ISO already exists: $ISO_VIRTIO" + fi + + # Define the directory containing the MSI files + UNATTENDED_DIR="$WIN_ISO_DIR/unattended" + mkdir -p "$UNATTENDED_DIR" + + # Find the latest Spice WebDAVD MSI + SPICE_WEBDAVD_MSI=$(find "$UNATTENDED_DIR" -type f -iname "spice-webdavd-x64*.msi" -exec stat --format="%Y %n" {} \; | sort -n | tail -n 1 | cut -d' ' -f2-) + + # Find the latest Spice VD Agent MSI + SPICE_VDAGENT_MSI=$(find "$UNATTENDED_DIR" -type f -iname "spice-vdagent-x64*.msi" -exec stat --format="%Y %n" {} \; | sort -n | tail -n 1 | cut -d' ' -f2-) + + # Find the latest UsbDk MSI + USBDK_MSI=$(find "$UNATTENDED_DIR" -type f -iname "UsbDk_*_x64.msi" -exec stat --format="%Y %n" {} \; | sort -n | tail -n 1 | cut -d' ' -f2-) + + # Spice Guest Tools + UsbDk (only if missing) + if [[ ! -f "$SPICE_WEBDAVD_MSI" ]]; then + print_info "Downloading Spice WebDAVD..." + curl -L -o "$UNATTENDED_DIR/spice-webdavd-x64-latest.msi" "$SPICE_WEBDAVD_URL" || { + print_error "Failed to download Spice WebDAVD" + return 1 + } + else + print_success "Spice WebDAVD MSI already exists: $SPICE_WEBDAVD_MSI" + fi + + if [[ ! -f "$SPICE_VDAGENT_MSI" ]]; then + print_info "Downloading Spice VD Agent..." + # Try multiple mirrors for spice-vdagent + local spice_mirrors=( + "https://www.spice-space.org/download/windows/spice-vdagent/spice-vdagent-x64-latest.msi" + "https://www.spice-space.org/download/windows/spice-vdagent/spice-vdagent-x64-0.10.0.msi" + "https://www.spice-space.org/download/windows/spice-vdagent/spice-vdagent-x64-0.9.0.msi" + "https://www.spice-space.org/download/windows/spice-vdagent/spice-vdagent-x64-0.8.0.msi" + "https://www.spice-space.org/download/windows/spice-vdagent/spice-vdagent-x64-0.7.0.msi" + ) + + local download_success=false + for mirror in "${spice_mirrors[@]}"; do + print_info "Trying mirror: $mirror" + if curl -L -o "$UNATTENDED_DIR/spice-vdagent-x64-latest.msi" "$mirror"; then + # Verify the downloaded file + if [[ -s "$UNATTENDED_DIR/spice-vdagent-x64-latest.msi" ]]; then + download_success=true + break + else + print_warning "Downloaded file is empty, trying next mirror..." + rm -f "$UNATTENDED_DIR/spice-vdagent-x64-latest.msi" + fi + fi + done + + if [[ "$download_success" == "false" ]]; then + print_error "Failed to download Spice VD Agent from all mirrors" + return 1 + fi + else + print_success "Spice VD Agent MSI already exists: $SPICE_VDAGENT_MSI" + fi + + if [[ ! -f "$USBDK_MSI" ]]; then + print_info "Downloading UsbDk..." + curl -L -o "$UNATTENDED_DIR/UsbDk_1.0.22_x64.msi" "$USBDK_URL" || { + print_error "Failed to download UsbDk" + return 1 + } + else + print_success "UsbDk MSI already exists: $USBDK_MSI" + fi + + # Create unattended ISO if it doesn't exist + if [[ ! -f "$ISO_UNATTENDED" ]]; then + create_unattended_iso + else + print_success "Unattended ISO already exists: $ISO_UNATTENDED" + fi +} + +# Locate OVMF firmware files +locate_ovmf() { + OVMF_DIRS=( + "/usr/share/OVMF" + "/usr/share/qemu" + "/usr/lib/qemu" + "/usr/share/edk2" + "/usr/lib/edk2" + "/usr/share/edk2/ovmf" + "/usr/share/edk2-ovmf" + ) + + OVMF_CODE="" + OVMF_VARS="" + + for dir in "${OVMF_DIRS[@]}"; do + [[ -z "$OVMF_CODE" ]] && OVMF_CODE=$(find "$dir" -type f -name "OVMF_CODE.fd" -o -name "edk2-x86_64-code.fd" 2>/dev/null | head -n 1) + [[ -z "$OVMF_VARS" ]] && OVMF_VARS=$(find "$dir" -type f -name "OVMF_VARS.fd" 2>/dev/null | head -n 1) + [[ -n "$OVMF_CODE" && -n "$OVMF_VARS" ]] && break + done + + # Ensure a writable copy of OVMF_VARS.fd + local original_ovmf_vars="$OVMF_VARS" + OVMF_VARS="$FIRMWARE_DIR/OVMF_VARS.fd" + + if [[ ! -f "$OVMF_VARS" && -f "$original_ovmf_vars" ]]; then + print_info "Copying OVMF_VARS.fd to $OVMF_VARS" + cp "$original_ovmf_vars" "$OVMF_VARS" 2>/dev/null || { + print_error "Failed to copy OVMF_VARS.fd!" + exit 1 + } + fi + + # Check if required files exist + if [[ -z "$OVMF_CODE" || ! -f "$OVMF_CODE" ]]; then + print_error "OVMF_CODE.fd not found!" + exit 1 + fi + if [[ ! -f "$OVMF_VARS" ]]; then + print_error "OVMF_VARS.fd not found or could not be copied!" + exit 1 + fi + #} +} + +# Create VM disk image +create_disk() { + # Check if the qcow2 image file exists; if not, create it + if [[ ! -f "$QCOW2_FILE" ]]; then + print_info "Creating $QCOW2_FILE with a size of $VM_SIZE" + qemu-img create -f qcow2 "$QCOW2_FILE" "$VM_SIZE" || { + print_error "Failed to create qcow2 image!" + exit 1 + } + else + print_success "VM disk image already exists: $QCOW2_FILE" + fi +} + +# Generate unique PID file for this instance +generate_pid_file() { + local base_pid_file="$VM_DIR/$VM_NAME.pid" + local pid_file="$base_pid_file" + local counter=1 + + # If the base PID file exists, try to find an available number + while [[ -f "$pid_file" ]]; do + pid_file="${base_pid_file%.pid}-${counter}.pid" + ((counter++)) + done + + echo "$pid_file" +} + +# Start the VM +start_vm() { + # Verify that we have a Windows ISO + if [[ -z "$WINDOWS_ISO_PATH" || ! -f "$WINDOWS_ISO_PATH" ]]; then + print_error "Windows ISO file not found. Cannot start VM." + print_info "Please download Windows 11 ISO and save it to: $WIN_ISO_DIR" + exit 1 + fi + + # Start swtpm + print_info "Starting TPM emulator..." + /sbin/swtpm socket \ + --ctrl type=unixio,path="$TPM_SOCKET" \ + --terminate \ + --tpmstate dir="$TPM_DIR" \ + --tpm2 & + + # Wait for swtpm socket + sleep 1 + + print_info "Starting Windows 11 VM..." + print_info "Using Windows ISO: $WINDOWS_ISO_PATH" + + # Build network options + local network_opts=() + if [[ "$DISABLE_NET" == "true" ]]; then + network_opts=( + "-netdev" "none,id=nic" + "-device" "virtio-net,netdev=nic" + ) + else + if [[ "$ENABLE_SMB" == "true" ]]; then + network_opts=( + "-netdev" "user,id=nic,hostname=$VM_NAME,hostfwd=tcp::$HOST_PORT-:$GUEST_PORT,smb=$SHARED_DIR" + "-device" "virtio-net,netdev=nic" + ) + else + network_opts=( + "-netdev" "user,id=nic,hostname=$VM_NAME,hostfwd=tcp::$HOST_PORT-:$GUEST_PORT" + "-device" "virtio-net,netdev=nic" + ) + fi + fi + + # Run QEMU + /sbin/qemu-system-x86_64 \ + -name "$VM_NAME",process="$VM_NAME" \ + -machine q35,hpet=off,smm=on,vmport=off,accel=kvm \ + -global kvm-pit.lost_tick_policy=discard \ + -global ICH9-LPC.disable_s3=1 \ + -cpu host,+hypervisor,+invtsc,l3-cache=on,migratable=no,hv_passthrough \ + -smp "$SMP_CONFIG" \ + -m "$VM_RAM" \ + -device virtio-balloon \ + -pidfile "$PID_FILE" \ + -rtc base=localtime,clock=host,driftfix=slew \ + -vga none \ + -device virtio-vga-gl,xres=1280,yres=800 \ + -display sdl,gl=on \ + -boot menu=on,splash-time=0,order=d,reboot-timeout=5000 \ + -device virtio-rng-pci,rng=rng0 \ + -object rng-random,id=rng0,filename=/dev/urandom \ + -device qemu-xhci,id=spicepass \ + -chardev spicevmc,id=usbredirchardev1,name=usbredir \ + -device usb-redir,chardev=usbredirchardev1,id=usbredirdev1 \ + -chardev spicevmc,id=usbredirchardev2,name=usbredir \ + -device usb-redir,chardev=usbredirchardev2,id=usbredirdev2 \ + -chardev spicevmc,id=usbredirchardev3,name=usbredir \ + -device usb-redir,chardev=usbredirchardev3,id=usbredirdev3 \ + -device pci-ohci,id=smartpass \ + -device usb-ccid \ + -chardev spicevmc,id=ccid,name=smartcard \ + -device ccid-card-passthru,chardev=ccid \ + -device usb-ehci,id=input \ + -device usb-kbd,bus=input.0 \ + -k en-us \ + -device usb-tablet,bus=input.0 \ + -audiodev pipewire,id=audio0 \ + -device intel-hda \ + -device hda-micro,audiodev=audio0 \ + "${network_opts[@]}" \ + -global driver=cfi.pflash01,property=secure,value=on \ + -drive if=pflash,format=raw,unit=0,file="$OVMF_CODE",readonly=on \ + -drive if=pflash,format=raw,unit=1,file="$OVMF_VARS" \ + -drive media=cdrom,index=1,file="$ISO_UNATTENDED" \ + -drive media=cdrom,index=0,file="$WINDOWS_ISO_PATH" \ + -drive media=cdrom,index=2,file="$ISO_VIRTIO" \ + -device virtio-blk-pci,drive=SystemDisk \ + -drive id=SystemDisk,if=none,format=qcow2,file="$QCOW2_FILE" \ + -chardev socket,id=chrtpm,path="$TPM_SOCKET" \ + -tpmdev emulator,id=tpm0,chardev=chrtpm \ + -device tpm-tis,tpmdev=tpm0 \ + -monitor unix:"$SOCKET_DIR/$VM_NAME-monitor.socket",server,nowait \ + -serial unix:"$SOCKET_DIR/$VM_NAME-serial.socket",server,nowait +} + +# Check dependencies +check_dependencies() { + # Cache file for dependency checks and VM configuration + local cache_name="windows-vm-${VM_NAME}-${VM_RAM}-${VM_CPU}-${VM_SIZE}" + local cache_file="${HOME}/.cache/${cache_name}.cache" + local cache_dir=$(dirname "$cache_file") + local cache_valid=false + local missing_deps=() + local pkg_manager="" + local pkg_install_cmd="" + local privilege_cmd="" + + # Clean cache if requested + if [[ "$CLEAN_CACHE" == "true" ]]; then + print_info "Cleaning dependency cache..." + rm -f "$cache_file" + fi + + # Create cache directory if it doesn't exist + mkdir -p "$cache_dir" + + # Check if cache is valid (less than 24 hours old) + if [[ -f "$cache_file" ]]; then + local cache_age=$(($(date +%s) - $(stat -c %Y "$cache_file"))) + if [[ $cache_age -lt 86400 ]]; then # 24 hours in seconds + # Read cached configuration + local cached_config + cached_config=$(head -n 1 "$cache_file" 2>/dev/null) + # Compare with current configuration + if [[ "$cached_config" == "${VM_NAME}-${VM_RAM}-${VM_CPU}-${VM_SIZE}" ]]; then + cache_valid=true + else + print_info "VM configuration changed, invalidating cache..." + rm -f "$cache_file" + fi + fi + fi + + # Check KVM group membership + if ! groups | grep -q -E 'kvm|qemu'; then + print_warning "User is not a member of kvm or qemu group" + print_info "You may need to add your user to the kvm group:" + print_info "sudo usermod -aG kvm $USER" + fi + + # Detect package manager and privilege command + if command -v emerge >/dev/null 2>&1; then + pkg_manager="emerge" + pkg_install_cmd="emerge --ask --noreplace" + privilege_cmd="sudo" + # Gentoo package mapping + declare -A pkg_map=( + ["qemu-system-x86_64"]="app-emulation/qemu" + ["qemu-img"]="app-emulation/qemu" + ["swtpm"]="app-crypt/swtpm" + ["genisoimage"]="app-cdr/cdrtools" + ["curl"]="net-misc/curl" + ["uuidgen"]="sys-apps/util-linux" + ["jq"]="app-misc/jq" + ["glxinfo"]="x11-apps/mesa-progs" + ["lspci"]="sys-apps/pciutils" + ["ps"]="sys-process/procps" + ["python3"]="dev-lang/python" + ["mkisofs"]="app-cdr/cdrtools" + ["lsusb"]="sys-apps/usbutils" + ["socat"]="net-misc/socat" + ["spicy"]="app-emulation/spice" + ["xrandr"]="x11-apps/xrandr" + ["zsync"]="net-misc/zsync" + ["unzip"]="app-arch/unzip" + ) + elif command -v apt-get >/dev/null 2>&1; then + pkg_manager="apt-get" + pkg_install_cmd="apt-get install -y" + privilege_cmd="sudo" + # Debian/Ubuntu package mapping + declare -A pkg_map=( + ["qemu-system-x86_64"]="qemu-system-x86" + ["qemu-img"]="qemu-utils" + ["swtpm"]="swtpm-tools" + ["genisoimage"]="genisoimage" + ["curl"]="curl" + ["uuidgen"]="uuid-runtime" + ["jq"]="jq" + ["glxinfo"]="mesa-utils" + ["lspci"]="pciutils" + ["ps"]="procps" + ["python3"]="python3" + ["mkisofs"]="genisoimage" + ["lsusb"]="usbutils" + ["socat"]="socat" + ["spicy"]="spice-client-gtk" + ["xrandr"]="x11-xserver-utils" + ["zsync"]="zsync" + ["unzip"]="unzip" + ) + elif command -v dnf >/dev/null 2>&1; then + pkg_manager="dnf" + pkg_install_cmd="dnf install -y" + privilege_cmd="sudo" + # Fedora package mapping + declare -A pkg_map=( + ["qemu-system-x86_64"]="qemu-system-x86" + ["qemu-img"]="qemu-img" + ["swtpm"]="swtpm" + ["genisoimage"]="genisoimage" + ["curl"]="curl" + ["uuidgen"]="util-linux" + ["jq"]="jq" + ["glxinfo"]="mesa-demos" + ["lspci"]="pciutils" + ["ps"]="procps-ng" + ["python3"]="python3" + ["mkisofs"]="genisoimage" + ["lsusb"]="usbutils" + ["socat"]="socat" + ["spicy"]="spice-gtk-tools" + ["xrandr"]="xorg-x11-server-utils" + ["zsync"]="zsync" + ["unzip"]="unzip" + ) + elif command -v pacman >/dev/null 2>&1; then + pkg_manager="pacman" + pkg_install_cmd="pacman -S --noconfirm" + privilege_cmd="sudo" + # Arch package mapping + declare -A pkg_map=( + ["qemu-system-x86_64"]="qemu" + ["qemu-img"]="qemu" + ["swtpm"]="swtpm" + ["genisoimage"]="cdrtools" + ["curl"]="curl" + ["uuidgen"]="util-linux" + ["jq"]="jq" + ["glxinfo"]="mesa-utils" + ["lspci"]="pciutils" + ["ps"]="procps-ng" + ["python3"]="python" + ["mkisofs"]="cdrtools" + ["lsusb"]="usbutils" + ["socat"]="socat" + ["spicy"]="spice-gtk" + ["xrandr"]="xorg-xrandr" + ["zsync"]="zsync" + ["unzip"]="unzip" + ) + fi + + # List of required commands and their package names + local deps=( + "qemu-system-x86_64" + "qemu-img" + "swtpm" + "genisoimage" + "curl" + "uuidgen" + "jq" + "glxinfo" + "lspci" + "ps" + "python3" + "mkisofs" + "lsusb" + "socat" + "spicy" + "xrandr" + "zsync" + "unzip" + ) + + # If cache is valid, read from it + if [[ "$cache_valid" == "true" ]]; then + print_info "Using cached dependency information..." + # Skip the first line (configuration) and read dependencies + tail -n +2 "$cache_file" | while IFS= read -r dep; do + if ! command -v "$dep" >/dev/null 2>&1; then + missing_deps+=("$dep") + fi + done + else + # Check each dependency and cache the results + print_info "Checking dependencies..." + + # Clear cache file and write current configuration + echo "${VM_NAME}-${VM_RAM}-${VM_CPU}-${VM_SIZE}" >"$cache_file" + + for dep in "${deps[@]}"; do + # Special case for genisoimage/mkisofs + if [[ "$dep" == "genisoimage" ]]; then + if ! command -v genisoimage >/dev/null 2>&1 && ! command -v mkisofs >/dev/null 2>&1; then + missing_deps+=("$dep") + echo "$dep" >>"$cache_file" + fi + # Special case for xdg-user-dirs + elif [[ "$dep" == "xdg-user-dirs" ]]; then + if ! pkg-config --exists xdg-user-dirs; then + missing_deps+=("$dep") + echo "$dep" >>"$cache_file" + fi + # Normal case for other dependencies + elif ! command -v "$dep" >/dev/null 2>&1; then + missing_deps+=("$dep") + echo "$dep" >>"$cache_file" + fi + done + fi + + if [[ ${#missing_deps[@]} -gt 0 ]]; then + print_warning "Missing dependencies:" + for dep in "${missing_deps[@]}"; do + if [[ -n "${pkg_map[$dep]}" ]]; then + print_warning "- $dep (package: ${pkg_map[$dep]})" + else + print_warning "- $dep" + fi + done + + if [[ -n "$pkg_manager" ]]; then + print_info "Detected package manager: $pkg_manager" + read -p "Would you like to install the missing dependencies? (y/N) " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]]; then + # Check for privilege command + if ! command -v "$privilege_cmd" >/dev/null 2>&1; then + print_error "Privilege command ($privilege_cmd) not found" + print_info "Please install the missing packages manually" + else + # Install missing packages + print_info "Installing missing dependencies..." + local pkgs_to_install=() + for dep in "${missing_deps[@]}"; do + if [[ -n "${pkg_map[$dep]}" ]]; then + pkgs_to_install+=("${pkg_map[$dep]}") + fi + done + "$privilege_cmd" "$pkg_install_cmd" "${pkgs_to_install[@]}" + + # Clear cache after installation + rm -f "$cache_file" + fi + else + print_warning "Continuing without installing dependencies. Some features may not work correctly." + fi + else + print_warning "No supported package manager found" + print_info "Please install the missing packages manually" + fi + else + print_success "All required dependencies are installed" + fi +} + +# Main execution +check_dependencies + +if [[ "$CHECK_DEPS_ONLY" == "true" ]]; then + exit 0 +fi + +if [[ "$DOWNLOAD_ISO_ONLY" == "true" ]]; then + download_windows_iso "$WINDOWS_VERSION" + exit 0 +fi + +# Generate unique PID file for this instance +PID_FILE=$(generate_pid_file) +print_info "Using PID file: $PID_FILE" + +prepare_files +locate_ovmf +create_disk +start_vm diff --git a/update-title b/update-title deleted file mode 100755 index e01e34f..0000000 --- a/update-title +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -while true; do - bg_color="#000000" # Initial background color (white) - #bg_color="#FFFFFF" # Initial background color (white) - text_color="#FFFFFF" # Initial text color (black) - #text_color="#000000" # Initial text color (black) - font="%{T1}" # Initial font setting - - active_node=$(xdotool getactivewindow) - - if [[ "$1" -eq "$active_node" ]]; then - bg_color="#000000" # Change the background color when the window is active (white) - #bg_color="#FFFFFF" # Change the background color when the window is active (white) - text_color="#FFFFFF" # Change the text color when the window is active (black) - #text_color="#000000" # Change the text color when the window is active (black) - font="%{T2}" # Change the font setting when the window is active - fi - - title=$(xtitle "$1") - - echo -e "%{B$bg_color}%{F$text_color}$font%{c}%{A1:bspc node $1 -f:}%{A2:bspc node $1 -c:}%{A5:bspc node $1 -d last:}%{A4:bspc node $1 -s biggest && ~/.scripts/title-bar:} $title%{A}%{A}%{A}%{A}%{F#FFFFFF}%{r}%{A:bspc node $1 -c:}x %{A}" - #echo -e "%{B$bg_color}%{F$text_color}$font%{c}%{A1:bspc node $1 -f:}%{A2:bspc node $1 -c:}%{A5:bspc node $1 -d last:}%{A4:bspc node $1 -s biggest && ~/.scripts/title-bar:}$title%{A}%{A}%{A}%{A}%{F#000000}%{r}%{A:bspc node $1 -c:}x %{A}" - - sleep 0.5 -done diff --git a/win-nvim.bat b/win-nvim.bat deleted file mode 100644 index c99374d..0000000 --- a/win-nvim.bat +++ /dev/null @@ -1,37 +0,0 @@ -@echo off - -REM Install NeoVim with winget, if not already present on the system -where nvim >nul 2>nul -if %errorlevel% neq 0 ( - winget install Neovim.Neovim -q -) - -REM Clone my dotfiles repo -set dotFilesRoot=%USERPROFILE%\dotfiles -if not exist "%dotFilesRoot%\." ( - git clone git@github.com:srdusr/dotfiles.git "%dotFilesRoot%" -) - -REM Link NeoVim configuration -set localConfiguration=%LOCALAPPDATA%\nvim -set dotfilesConfiguration=%dotFilesRoot%\.config\nvim - -if not exist "%localConfiguration%\." ( - mklink /D "%localConfiguration%" "%dotfilesConfiguration%" -) - -REM Clone Packer.nvim, if not already present on the system -set localPacker=%LOCALAPPDATA%\nvim-data\site\pack\packer\start\packer.nvim - -if not exist "%localPacker%\." ( - git clone https://github.com/wbthomason/packer.nvim "%localPacker%" -) - -REM Run the script by using this command in the same existing directory: win-nvim.bat - -@powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((new-object net.webclient).DownloadString('https://aka.ms/install-winget'))" -iex ((new-object net.webclient).DownloadString('https://aka.ms/install-winget')) -curl -o winget-cli.appxbundle https://aka.ms/winget-cli-appxbundle - -powershell Add-AppxPackage -Path "winget-cli.appxbundle" - diff --git a/win-nvim.ps1 b/win-nvim.ps1 deleted file mode 100644 index ca67755..0000000 --- a/win-nvim.ps1 +++ /dev/null @@ -1,39 +0,0 @@ -# Install NeoVim with winget, if not already present on the system -if (!(Get-Command nvim -ErrorAction SilentlyContinue)) { - winget install Neovim.Neovim -} - -# Clone my dotfiles repo -$dotFilesRoot = Join-Path $HOME "dotfiles" - -if (!(Test-Path $dotFilesRoot -PathType Container)) { - git clone https://github.com/srdusr/dotfiles.git $dotFilesRoot -} - -# Link NeoVim configuration -$localConfiguration = Join-Path $env:LOCALAPPDATA "nvim" -$dotfilesConfiguration = Join-Path $dotFilesRoot ".config" "nvim" - -if (!(Test-Path $localConfiguration -PathType Container)) { - Start-Process -FilePath "cmd.exe" -ArgumentList "/c mklink /D $localConfiguration $dotfilesConfiguration" -Verb runas -} - -# Clone Packer.nvim, if not already present on the system -$localPacker = Join-Path $env:LOCALAPPDATA "nvim-data" "site" "pack" "packer" "start" "packer.nvim" - -if (!(Test-Path $localPacker -PathType Container)) { - git clone https://github.com/wbthomason/packer.nvim $localPacker -} - -# To allow script execution, run the following command in PowerShell as an administrator: -# Set-ExecutionPolicy RemoteSigned -# Then run the script by using this command in the same existing directory: -# ./win-nvim.ps1 -#curl -o winget-cli.appxbundle https://aka.ms/winget-cli-appxbundle -#powershell Add-AppxPackage -Path "winget-cli.appxbundle" -#Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')) -#use `-y` or consider: choco feature enable -n allowGlobalConfirmation -#choco install git -#- Refresh the environment -#Import-Module $env:ChocolateyInstall\helpers\chocolateyProfile.psm1 -#refreshenv |
