summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCasper <me@skylarcloud.xyz>2024-12-23 17:13:45 -0700
committerCasper <me@skylarcloud.xyz>2024-12-23 17:13:45 -0700
commit01a33ab0913d439150065ac23644c4f140ff8917 (patch)
tree9ad71bd7e6f5a5aad2c2cdbff4bb75de12565998
parent7b5a0e9e88f015cb68a46bb657661f4663e287af (diff)
added qmenu!
-rw-r--r--README.md4
-rw-r--r--dots/i3-config23
-rw-r--r--qmenu/install-qmenu-scripts.sls20
-rw-r--r--qmenu/install-qmenu.top3
-rw-r--r--qmenu/qmenu-am32
-rw-r--r--qmenu/qmenu-dm97
-rw-r--r--qmenu/qmenu-vm167
-rw-r--r--qmenu/qmenu_vm/f_notes23
-rw-r--r--qmenu/qmenu_vm/f_screenshot18
-rw-r--r--qmenu/qmenu_vm/fq_keyboard12
-rw-r--r--qmenu/qmenu_vm/fq_pm75
-rw-r--r--qmenu/qmenu_vm/fqubes_logs16
-rw-r--r--qmenu/qmenu_vm/fqubes_prefs109
-rw-r--r--qmenu/qmenu_vm/fqvm_appmenus64
-rw-r--r--qmenu/qmenu_vm/fqvm_clone39
-rw-r--r--qmenu/qmenu_vm/fqvm_create77
-rw-r--r--qmenu/qmenu_vm/fqvm_device70
-rw-r--r--qmenu/qmenu_vm/fqvm_firewall81
-rw-r--r--qmenu/qmenu_vm/fqvm_pci70
-rw-r--r--qmenu/qmenu_vm/fqvm_prefs155
-rw-r--r--qmenu/qmenu_vm/fqvm_remove18
-rw-r--r--qmenu/qmenu_vm/fqvm_run14
-rw-r--r--qmenu/qmenu_vm/fqvm_service39
-rw-r--r--qmenu/qmenu_vm/fqvm_tags19
-rw-r--r--qmenu/qmenu_vm/fqvm_volume26
25 files changed, 1271 insertions, 0 deletions
diff --git a/README.md b/README.md
index 996e888..005a870 100644
--- a/README.md
+++ b/README.md
@@ -79,6 +79,10 @@ Creates *tmpl-email* and two app qubes, *email-personal* and *email-work*, with
Creates *tmpl-irc* and *irc* with Hexchat installed, for chatting on IRC over Tor.
+## Qmenu
+
+Installs some useful dmenu-scripts to help administer your system with keybindings.
+
## SSH
Creates *tmpl-ssh* and *ssh-vps*, simple qubes I use to ssh into my VPS with.
diff --git a/dots/i3-config b/dots/i3-config
index 747e0cd..78ca2ae 100644
--- a/dots/i3-config
+++ b/dots/i3-config
@@ -129,6 +129,29 @@ mode "$mode_gaps_outer" {
bindsym Escape mode "default"
}
+set $mode_qmenu qmenu: (l)ocal-apps|(d)evices|(a)ttach
+bindsym $mod+p mode "$mode_qmenu"
+mode "$mode_qmenu" {
+ bindsym l exec "qmenu-am --focused"
+ bindsym d exec "qmenu-dm --all"
+ bindsym a exec "qmenu-dm --focused"
+
+ bindsym Return mode "default"
+ bindsym Escape mode "default"
+}
+
+set $mode_qmenu_vm qmenu-vm: (a)ll|(f)ocused|(h)alted|(p)aused|(r)unning
+bindsym $mod+Shift+v mode "$mode_qmenu_vm"
+mode "$mode_qmenu_vm" {
+ bindsym a exec "qmenu-vm --all"
+ bindsym f exec "qmenu-vm --focused"
+ bindsym h exec "qmenu-vm --halted"
+ bindsym r exec "qmenu-vm --running"
+
+ bindsym Return mode "default"
+ bindsym Escape mode "default"
+}
+
# Use Mouse+$mod to drag floating windows to their wanted position
floating_modifier $mod
diff --git a/qmenu/install-qmenu-scripts.sls b/qmenu/install-qmenu-scripts.sls
new file mode 100644
index 0000000..cffe96a
--- /dev/null
+++ b/qmenu/install-qmenu-scripts.sls
@@ -0,0 +1,20 @@
+qmenu--move-scripts:
+ file.managed:
+ - user: root
+ - group: root
+ - mode: 777
+ - names:
+ - /usr/bin/qmenu-am:
+ - source: salt://qmenu/qmenu-am
+ - /usr/bin/qmenu-dm:
+ - source: salt://qmenu/qmenu-dm
+ - /usr/bin/qmenu-vm:
+ - source: salt://qmenu/qmenu-vm
+
+qmenu-reset-lib:
+ cmd.run:
+ - name: 'sudo rm -fr /lib/qmenu_vm'
+
+qmenu-move-scripts:
+ cmd.run:
+ - name: 'sudo mv /srv/user_salt/qmenu/qmenu_vm/ /lib/'
diff --git a/qmenu/install-qmenu.top b/qmenu/install-qmenu.top
new file mode 100644
index 0000000..68a7ed8
--- /dev/null
+++ b/qmenu/install-qmenu.top
@@ -0,0 +1,3 @@
+user:
+ dom0:
+ - qmenu.install-qmenu-scripts
diff --git a/qmenu/qmenu-am b/qmenu/qmenu-am
new file mode 100644
index 0000000..0b00b29
--- /dev/null
+++ b/qmenu/qmenu-am
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+case $1 in
+
+ --all)
+
+ app_list=$(grep '^Name=\|^Exec=' "$HOME"/.local/share/applications/*.desktop /usr/share/applications/*.desktop)
+
+ chosen=$(printf =; echo "$app_list" | grep ':Name=' | cut -d= -f2- | dmenu-unlinked) || exit 1;;
+
+ --focused)
+
+ qube=$(xprop -id "$(xdotool getwindowfocus)" _QUBES_VMNAME | cut -f2 -d\")
+
+ [ "$qube" = '_QUBES_VMNAME: not found.' ] && exit 2
+
+ qube_label=$(grep -s "^$(qvm-prefs "$qube" label)=" "$HOME"/.config/qmenu.conf | cut -d= -f2)
+
+ [ -z "$qube_label" ] && qube_label='#000000'
+
+ app_list=$(grep '^Name=\|^Exec=' "$HOME/.local/share/qubes-appmenus/$qube"/apps/*.desktop)
+
+ chosen=$(echo "$app_list" | grep ':Name=' | cut -f3- -d: | dmenu-unlinked -p "$qube:" -sb "$qube_label") || exit 1;;
+
+ *)
+
+ printf "Usage: $0 [OPTION]\nLaunch domU and dom0 applications via dmenu.\n\n --all\n --focused\n\n"
+
+ [ "$1" = --help ] && exit 0; exit 2
+esac
+
+$(echo "$app_list" | grep "$(echo "$app_list" | grep "$chosen$" | cut -d: -f1)" | grep ':Exec=' | cut -d= -f2-) || exit 2
diff --git a/qmenu/qmenu-dm b/qmenu/qmenu-dm
new file mode 100644
index 0000000..4f95247
--- /dev/null
+++ b/qmenu/qmenu-dm
@@ -0,0 +1,97 @@
+#!/bin/sh
+
+get_qube_label() {
+
+qube_label=$(grep -s "^$(qvm-prefs "$qube" label)=" "$HOME"/.config/qmenu.conf | cut -d= -f2)
+
+[ -z "$qube_label" ] && qube_label='#000000'
+}
+
+
+case $1 in
+
+ --all)
+
+ qube_list=$(qvm-ls --no-spinner --running -O name,label | grep -v '^dom0 \|^sys-usb' | sed '1d; s/ red/ 1\@ /g; s/ orange/ 2\@ /g; s/ yellow/ 3\@ /g; s/ green/ 4\@ /g; s/ gray/ 5\@ /g; s/ blue/ 6\@ /g; s/ purple/ 7\@ /g; s/ black/ 8\@ /g' | sort -k2,2 | sed 's/ 1\@ / red/g; s/ 2\@ / orange/g; s/ 3\@ / yellow/g; s/ 4\@ / green/g; s/ 5\@ / gray/g; s/ 6\@ / blue/g; s/ 7\@ / purple/g; s/ 8\@ / black/g');;
+
+ --focused)
+
+ qube=$(xprop -id "$(xdotool getwindowfocus)" _QUBES_VMNAME | cut -f2 -d\")
+
+ [ "$qube" = '_QUBES_VMNAME: not found.' ] && exit 2;;
+
+ *)
+
+ printf "Usage: $0 [OPTION]\nList and manage your connected devices via dmenu.\n\n --all\n --focused\n\n"
+
+ [ "$1" = --help ] && exit 0; exit 2
+esac
+
+while :; do
+
+ device=$(echo "$(qvm-block ls | sed -e 's/^/|block| /' & qvm-device mic ls | sed -e 's/^/|mic| /' & qvm-usb ls | sed -e 's/^/|usb| /')" | sort -r -k1,2 | dmenu-unlinked -l 32) || exit 1
+
+ device_type=$(echo "$device" | cut -d\| -f2)
+
+ if [ -n "$device" ] && [ "$(echo "$device" | wc -l)" -eq 1 ]; then
+
+ device_id=$(echo "$device" | cut -f2 -d\ )
+
+ if [ "$device_type" = block ]; then
+
+ device_name=$(echo "$device" | awk '{print $3,$4}')
+ holding_qube=$(echo "$device" | awk '{print $5}')
+ else
+ device_name=$(echo "$device" | awk '{print $3}')
+ holding_qube=$(echo "$device" | awk '{print $4}')
+ fi
+
+ if [ -z "$holding_qube" ]; then
+
+ if [ "$1" = --all ]; then
+
+ qube=$(echo "$qube_list" | dmenu-unlinked -p "attach to:" -l 32 | cut -f1 -d\ )
+ fi
+
+ if [ -n "$qube" ]; then
+
+ get_qube_label
+
+ if [ "$(printf 'No\nYes' | dmenu-unlinked -sb "$qube_label" -i -p "Attach '$device_name' to $qube?")" = Yes ]; then
+
+ qvm-device "$device_type" attach -q "$qube" "$device_id" && exit 0
+
+ echo Quit... | dmenu-unlinked -p "Error: Failed to attach device!" > /dev/null 2>&1
+
+ exit 2
+ fi
+ fi
+
+ elif [ "$1" = --all ] || [ "$holding_qube" = "$qube" ]; then
+
+ get_qube_label
+
+ if [ "$(printf 'No\nYes' | dmenu-unlinked -sb "$qube_label" -i -p "Detach '$device_name' from $holding_qube?")" = Yes ]; then
+
+ qvm-device "$device_type" detach -q "$holding_qube" "$device_id" && exit 0
+
+ echo Quit... | dmenu-unlinked -p "Error: Failed to detach device!" > /dev/null 2>&1
+
+ exit 2
+ fi
+ else
+ get_qube_label
+
+ if [ "$(printf 'No\nYes' | dmenu-unlinked -sb "$qube_label" -i -p "Detach '$device_name' from $holding_qube and attach it to $qube?")" = Yes ]; then
+
+ qvm-device "$device_type" detach -q "$holding_qube" "$device_id" &&
+
+ qvm-device "$device_type" attach -q "$qube" "$device_id" && exit 0
+
+ echo Quit... | dmenu-unlinked -p "Error: Failed to handle device!" > /dev/null 2>1&
+
+ exit 2
+ fi
+ fi
+ fi
+done
diff --git a/qmenu/qmenu-vm b/qmenu/qmenu-vm
new file mode 100644
index 0000000..ce587a0
--- /dev/null
+++ b/qmenu/qmenu-vm
@@ -0,0 +1,167 @@
+#!/bin/sh
+
+fmenu_dom0() (
+
+while :; do
+
+ [ -z "$loop" ] && chosefrom=$(printf 'Global preferences\nLogs\nNew qube\nNotes\nProperty Manager' | dmenu-unlinked -l 5 -i -p "dom0:")
+
+ case $chosefrom in
+
+ Global*) . /lib/qmenu_vm/fqubes_prefs;;
+
+ Logs) . /lib/qmenu_vm/fqubes_logs;;
+
+ New*) . /lib/qmenu_vm/fqvm_create;;
+
+ Notes) . /lib/qmenu_vm/f_notes;;
+
+ Property*) . /lib/qmenu_vm/fq_pm;;
+
+ *) exit
+ esac
+
+ [ -z "$loop" ] || break
+done
+)
+
+fmenu_domu() (
+
+dmenu() (
+
+command dmenu-unlinked -sb "$qube_label" "$@"
+)
+
+qube_label=$(grep -s "^$(qvm-prefs "$qube" label)=" "$HOME"/.config/qmenu.conf | cut -d= -f2)
+
+[ -z "$qube_label" ] && qube_label='#000000'
+
+while :; do
+
+ [ -z "$loop" ] && chosefrom=$(printf 'Applications\nAudio input devices\nBlock devices\nBoot\nBootfrom\nClone\nCommand\nDisk\nFirewall\nKeyboard\nKill\nLogs\nNotes\nPause\nPCI devices\nPreferences\nReboot\nRemove\nScreenshot\nServices\nShutdown\nTags\nUnpause\nUSB devices' | dmenu-unlinked -l 24 -i -p "$qube:")
+
+ case $chosefrom in
+
+ Applications) . /lib/qmenu_vm/fqvm_appmenus;;
+
+ Audio*|Block*|USB*) . /lib/qmenu_vm/fqvm_device;;
+
+ Boot) qvm-start --skip-if-running "$qube"&;;
+
+ Bootfrom) qubes-vm-boot-from-device "$qube";;
+
+ Clone) . /lib/qmenu_vm/fqvm_clone;;
+
+ Command) . /lib/qmenu_vm/fqvm_run;;
+
+ Disk) . /lib/qmenu_vm/fqvm_volume;;
+
+ Firewall) . /lib/qmenu_vm/fqvm_firewall;;
+
+ Keyboard) . /lib/qmenu_vm/fq_keyboard;;
+
+ Kill) nyprompt "Kill $qube?" && (qvm-kill "$qube"&);;
+
+ Logs) . /lib/qmenu_vm/fqubes_logs;;
+
+ Notes) . /lib/qmenu_vm/f_notes;;
+
+ Pause) nyprompt "Pause $qube?" && (qvm-pause "$qube"&);;
+
+ PCI*) . /lib/qmenu_vm/fqvm_pci;;
+
+ Preferences) . /lib/qmenu_vm/fqvm_prefs;;
+
+ Reboot) nyprompt "Reboot $qube?" && qvm-check -q --running "$qube" && (qvm-shutdown --wait "$qube" && qvm-start "$qube"&);;
+
+ Remove) . /lib/qmenu_vm/fqvm_remove;;
+
+ Screenshot) . /lib/qmenu_vm/f_screenshot;;
+
+ Services) . /lib/qmenu_vm/fqvm_service;;
+
+ Shutdown) nyprompt "Shutdown $qube?" && (qvm-shutdown "$qube"&);;
+
+ Tags) . /lib/qmenu_vm/fqvm_tags;;
+
+ Unpause) qvm-unpause "$qube"&;;
+
+ *) exit
+ esac
+
+ [ -z "$loop" ] || break
+done
+)
+
+get_list() {
+
+case $1 in
+
+ mgmt_disptemp) flags='|a.....D.$';;
+
+ netvm) flags='|...N....$';;
+
+ template) flags='|t.......$'
+esac
+
+list=$(qvm-ls --raw-data -O name,flags | grep "$flags" | cut -d '|' -f1)
+}
+
+nyprompt() (
+
+[ "$(printf 'No\nYes' | dmenu-unlinked -i -p "$1")" = Yes ]
+)
+
+
+[ -n "$2" ] && chosefrom="$2" loop=0
+
+case $1 in
+
+ --all|--halted|--paused|--running|--tags=*)
+
+ while :; do
+
+ # Load 'qvm-ls' into a variable so that the list doesnt
+ # need to be reloaded after every user operation.
+ qube_list=$(qvm-ls --no-spinner "$1" -O name,state,label,maxmem,class,template,netvm,default_dispvm,ip,priv-max,priv-curr,priv-used,root-max,root-curr,root-used,kernel | sed 's/ LABEL/ 0\@ /g; s/ red/ 1\@ /g; s/ orange/ 2\@ /g; s/ yellow/ 3\@ /g; s/ green/ 4\@ /g; s/ gray/ 5\@ /g; s/ blue/ 6\@ /g; s/ purple/ 7\@ /g; s/ black/ 8\@ /g' | sort -k3,3 | sed 's/ 0\@ / LABEL/g; s/ 1\@ / red/g; s/ 2\@ / orange/g; s/ 3\@ / yellow/g; s/ 4\@ / green/g; s/ 5\@ / gray/g; s/ 6\@ / blue/g; s/ 7\@ / purple/g; s/ 8\@ / black/g')
+
+ # Get out of the while loop and reload the list by loading 'qvm-ls' again
+ # into $qube_list, whenever the user selects the 'qvm-ls' top row (NAME).
+ # Essentially making the top row a refresh button.
+ while [ "$qubes" != NAME ]; do
+
+ qubes=$(echo "$qube_list" | dmenu-unlinked -l 50 | cut -f1 -d\ )
+
+ [ -z "$qubes" ] && exit 1
+
+ for qube in $qubes; do
+
+ if [ "$qube" = dom0 ]; then fmenu_dom0
+ elif [ "$qube" != NAME ]; then fmenu_domu
+ fi
+ done
+ done
+
+ unset qubes
+ done;;
+
+ --focused)
+
+ qube=$(xprop -id "$(xdotool getwindowfocus)" _QUBES_VMNAME | cut -d\" -f2)
+
+ if [ "$qube" = '_QUBES_VMNAME: not found.' ]; then qube=dom0; fmenu_dom0; else fmenu_domu; fi;;
+
+ --qube=*)
+
+ qube=$(echo "$1" | cut -d= -f2)
+
+ if [ "$qube" = dom0 ]; then fmenu_dom0; else fmenu_domu; fi;;
+
+ *)
+
+ printf "Usage: $0 [OPTION] (MENU ITEM)\nList, manage and configure your qubes via dmenu.\n\n --all\n --focused\n --halted\n --paused\n --running\n --qube=[QUBE]\n --tags=[TAG]\n\n"
+
+ [ "$1" = --help ] && exit 0; exit 2
+esac
+
+exit 1
diff --git a/qmenu/qmenu_vm/f_notes b/qmenu/qmenu_vm/f_notes
new file mode 100644
index 0000000..ef93abd
--- /dev/null
+++ b/qmenu/qmenu_vm/f_notes
@@ -0,0 +1,23 @@
+if [ "$(qvm-prefs "$qube" klass)" = DispVM ]; then
+
+ notes=/tmp/."$qube".dispnotes
+else
+ mkdir -p "$HOME"/.local/share/qubes-notes
+ notes="$HOME/.local/share/qubes-notes/$qube"
+fi
+
+touch "$notes"
+
+note=$(dmenu-unlinked -l 25 -p "$qube:" < "$notes")
+
+while [ -n "$note" ]; do
+
+ if grep -q "^$note\$" "$notes"; then
+
+ sed -i "/^$note$/d" "$notes"
+ else
+ echo "$(date +%Y-%m-%d\ %H:%M) $note" >> "$notes"
+ fi
+
+ note=$(dmenu-unlinked -l 25 -p "$qube:" < "$notes")
+done
diff --git a/qmenu/qmenu_vm/f_screenshot b/qmenu/qmenu_vm/f_screenshot
new file mode 100644
index 0000000..cac22bc
--- /dev/null
+++ b/qmenu/qmenu_vm/f_screenshot
@@ -0,0 +1,18 @@
+err="qmenu-vm - Error: Failed to take a screenshot! Please make sure that 'scrot' is installed inside $qube."
+
+target=$(printf 'Focused window\nAll visible windows' | dmenu-unlinked -l 2 -i -p "$qube:" | cut -f1 -d\ )
+
+if [ "$target" = Focused ]; then
+
+ if [ "$(xprop -id "$(xdotool getwindowfocus)" _QUBES_VMNAME | cut -d\" -f2)" = "$qube" ]; then
+
+ (qvm-run -q "$qube" 'scrot -q 100 -u $HOME/Pictures/screenshot' || notify-send -u normal "$err")&
+ else
+ echo 'Go back...' | dmenu-unlinked -p "Warning: Unable to take a screenshot. Please focus a window belonging to $qube." > /dev/null 2>&1
+ fi
+
+elif [ "$target" = All ]; then
+
+ (qvm-run -q "$qube" 'scrot -q 100 $HOME/Pictures/screenshot' || notify-send -u normal "$err")&
+fi
+
diff --git a/qmenu/qmenu_vm/fq_keyboard b/qmenu/qmenu_vm/fq_keyboard
new file mode 100644
index 0000000..7a5c6e8
--- /dev/null
+++ b/qmenu/qmenu_vm/fq_keyboard
@@ -0,0 +1,12 @@
+LAYOUT=$(ls /usr/share/X11/xkb/symbols | dmenu-unlinked -l 50 -p "$qube:") &&
+
+if [ -n "$LAYOUT" ]; then
+
+ [ -d /usr/share/X11/xkb/symbols/"$LAYOUT" ] && LAYOUT="$LAYOUT/"$(ls /usr/share/X11/xkb/symbols/"$LAYOUT" | dmenu-unlinked -l 50 -p "$qube:")
+
+ $(qvm-run -q "$qube" "setxkbmap $LAYOUT" || notify-send -u normal "Error: Failed to set keyboard layout to '$LAYOUT' in $qube! Please check if the selected layout is missing inside the qube itself.")&
+
+ # 'qmenu-vm' does not get informed about the available layouts by the vm itself,
+ # so it is possible that the selected layout is missing inside the vm.
+ # However, getting the info from the vm itself would pose an unneccesary high risk.
+fi
diff --git a/qmenu/qmenu_vm/fq_pm b/qmenu/qmenu_vm/fq_pm
new file mode 100644
index 0000000..313a6b0
--- /dev/null
+++ b/qmenu/qmenu_vm/fq_pm
@@ -0,0 +1,75 @@
+qubes=$(qvm-ls --no-spinner --all --exclude=dom0 -O name,label,template,netvm,default_dispvm,kernel | dmenu-unlinked -l 50 | cut -f1 -d\ )
+
+if [ -n "$qubes" ]; then
+
+ property=$(printf 'Default_DispVM\nKernel\nLabel\nTemplate\nNetVM' | dmenu-unlinked -i -l 5 -p "Select the property you wish to change:" | awk '{print tolower($0)}')
+
+ if [ -n "$property" ]; then
+
+ case $property in
+
+ default_dispvm)
+
+ if [ -z "$LIST_DISPTEMP" ]; then
+
+ # [mediocre] Look up disposable templates in a very slow way because qvm-ls does not offer a flag for them
+ for n in $(qvm-ls --raw-data -O NAME,FLAGS | grep '|a.....-.$' | cut -d '|' -f1); do
+
+ qvm-prefs "$n" template_for_dispvms | grep -q True &&
+
+ LIST_DISPTEMP="$LIST_DISPTEMP\n$n"
+ done
+ fi
+
+ value=$(printf "(Default)\n(None)$LIST_DISPTEMP" | dmenu-unlinked -l 50 -p "Select default dispvm:")
+
+ if [ "$value" = '(None)' ]; then value=None
+ elif [ "$value" = '(Default)' ]; then value=--default; fi;;
+
+ kernel)
+
+ value=$(echo --default | dmenu-unlinked -p "Enter kernel:");;
+
+ label)
+
+ value=$(printf 'Purple\nBlue\nGray\nGreen\nYellow\nOrange\nRed\nBlack' | dmenu-unlinked -i -l 8 -p "Select label:" | awk '{print tolower($0)}');;
+
+ template)
+
+ get_list template
+
+ value=$(printf "(Default)\n$list" | dmenu-unlinked -l 50 -p "Select template:")
+
+ [ "$value" = '(Default)' ] && value=--default;;
+
+ netvm)
+
+ get_list netvm
+
+ value=$(printf "(Default)\n(None)\n$list" | dmenu-unlinked -l 50 -p "Select netvm:")
+
+ if [ "$value" = '(None)' ]; then value=None
+ elif [ "$value" = '(Default)' ]; then value=--default; fi;;
+
+ *)
+ esac
+
+ if [ -n "$value" ]; then
+
+ if nyprompt "Do you want to change the $property of all selected qubes to $value?"; then
+
+ for qube in $qubes; do
+
+ if [ "$property" = label ] || [ "$property" = template ] && qvm-check -q --running "$qube"; then
+
+ echo Proceed... | dmenu-unlinked -p "Warning: Unable to change $property of $qube because it is not shut down." > /dev/null 2>&1
+ else
+ qvm-prefs "$qube" "$property" "$value" ||
+
+ echo Proceed... | dmenu-unlinked -p "Error: Failed to set $property of $qube to '$value'!" > /dev/null 2>&1
+ fi
+ done
+ fi
+ fi
+ fi
+fi
diff --git a/qmenu/qmenu_vm/fqubes_logs b/qmenu/qmenu_vm/fqubes_logs
new file mode 100644
index 0000000..37fd6c8
--- /dev/null
+++ b/qmenu/qmenu_vm/fqubes_logs
@@ -0,0 +1,16 @@
+if [ "$qube" = dom0 ]; then
+
+ logs=/var/log/xen/console/hypervisor.log
+else
+ logs="/var/log/xen/console/guest-$qube.log\n/var/log/xen/console/guest-$qube-dm.log\n/var/log/qubes/guid.$qube.log\n/var/log/qubes/qrexec.$qube.log"
+fi
+
+entry=1
+
+while [ -n "$entry" ]; do
+
+ if entry=$(printf "$logs" | dmenu-unlinked -i -l 4 -p "$qube:"); then
+
+ qubes-log-viewer "$entry"
+ fi
+done
diff --git a/qmenu/qmenu_vm/fqubes_prefs b/qmenu/qmenu_vm/fqubes_prefs
new file mode 100644
index 0000000..2570225
--- /dev/null
+++ b/qmenu/qmenu_vm/fqubes_prefs
@@ -0,0 +1,109 @@
+property=1
+
+while [ -n "$property" ]; do
+
+ property=$(qubes-prefs | dmenu-unlinked -l 16 -p "Global preferences:" | cut -f1 -d\ )
+
+ case $property in
+
+ check_updates_vm)
+
+ value=$(printf 'False\nTrue' | dmenu-unlinked -i -p "Should the system periodically check for domU updates?");;
+
+ clockvm)
+
+ get_list netvm
+
+ value=$(printf "(None)\n$list" | dmenu-unlinked -l 50 -p "Select ClockVM:");;
+
+ default_dispvm)
+
+ if [ -z "$LIST_DISPTEMP" ]; then
+
+ # [mediocre] Look up disposable templates in a very slow way because qvm-ls does not offer a flag for them
+ for n in $(qvm-ls --raw-data -O NAME,FLAGS | grep '|a.....-.$' | cut -d '|' -f1); do
+
+ qvm-prefs "$n" template_for_dispvms | grep -q True &&
+
+ LIST_DISPTEMP="$LIST_DISPTEMP\n$n"
+ done
+ fi
+
+ value=$(printf "(None)$LIST_DISPTEMP" | dmenu-unlinked -l 50 -p "Select default dispvm:");;
+
+ default_kernel)
+
+ value=$(echo '(None)' | dmenu-unlinked -l 2 -p "Enter default kernel:") &&
+
+ nyprompt "Set default kernel to $value?" || unset value;;
+
+ default_netvm)
+
+ get_list netvm
+
+ value=$(printf "(None)\n$list" | dmenu-unlinked -l 50 -p "Select default netvm:");;
+
+ default_pool)
+
+ value=$(qvm-pool --list | sed '1d' | dmenu-unlinked -i -l 30 -p "Select default storage pool:" | cut -f1 -d\ );;
+
+ default_pool_kernel)
+
+ value=$(qvm-pool --list | sed '1d' | dmenu-unlinked -i -l 30 -p "Select default storage pool for kernel volumes:" | cut -f1 -d\ );;
+
+ default_pool_private)
+
+ value=$(qvm-pool --list | sed '1d' | dmenu-unlinked -i -l 30 -p "Select default storage pool for private volumes:" | cut -f1 -d\ );;
+
+ default_pool_root)
+
+ value=$(qvm-pool --list | sed '1d' | dmenu-unlinked -i -l 30 -p "Select default storage pool for root volumes:" | cut -f1 -d\ );;
+
+ default_pool_volatile)
+
+ value=$(qvm-pool --list | sed '1d' | dmenu-unlinked -i -l 30 -p "Select default storage pool for volatile volumes:" | cut -f1 -d\ );;
+
+ default_qrexec_timeout)
+
+ value=$(qubes-prefs default_qrexec_timeout | dmenu-unlinked -p "Enter the time in seconds, after which qrexec connection attempts are deemed a failure:");;
+
+ default_shutdown_timeout)
+
+ value=$(qubes-prefs default_shutdown_timeout | dmenu-unlinked -p "Enter the default time in seconds for qube shutdowns to complete:");;
+
+ default_template)
+
+ get_list template
+
+ value=$(printf "(None)\n$list" | dmenu-unlinked -l 50 -p "Select default template:");;
+
+ management_dispvm)
+
+ get_list mgmt_disptemp
+
+ value=$(printf "(None)\n$list" | dmenu-unlinked -l 50 -p "Select management dispvm:");;
+
+ stats_interval)
+
+ value=$(: | dmenu-unlinked -p "Enter interval in seconds for qube stats reporting:");;
+
+ updatevm)
+
+ get_list netvm
+
+ value=$(printf "(None)\n$list" | dmenu-unlinked -l 50 -p "Select UpdateVM for dom0:");;
+
+ *)
+ esac
+
+ if [ -n "$value" ]; then
+
+ [ "$value" = '(None)' ] && value=''
+
+ qubes-prefs "$property" "$value" ||
+
+ echo Go back... | dmenu-unlinked -p "Error: Failed to set $property to '$value'!" > /dev/null 2>&1
+
+ unset value
+ fi
+done
diff --git a/qmenu/qmenu_vm/fqvm_appmenus b/qmenu/qmenu_vm/fqvm_appmenus
new file mode 100644
index 0000000..4d3fe46
--- /dev/null
+++ b/qmenu/qmenu_vm/fqvm_appmenus
@@ -0,0 +1,64 @@
+option=1
+
+while [ -n "$option" ]; do
+
+ option=$(printf 'Available\nSelected\nRefresh' | dmenu-unlinked -i -l 3 -p "$qube:")
+
+ if [ "$option" = Available ]; then
+
+ applications=1
+
+ while [ -n "$applications" ]; do
+
+ applications=$(qvm-appmenus --get-available --i-understand-format-is-unstable "$qube" | grep -v "$(qvm-appmenus --get-whitelist --i-understand-format-is-unstable "$qube")" | dmenu-unlinked -l 50 -p "$qube:" | cut -f1 -d\ )
+
+ if [ -n "$applications" ]; then
+
+ # Check validity of input to prevent whitelisted-appmenus.list
+ # from being filled with garbage.
+ qvm-appmenus --get-available --i-understand-format-is-unstable "$qube" | cut -f1 -d\ | grep -q "^$applications$" &&
+
+ printf "\n$applications" >> "$HOME"/.local/share/qubes-appmenus/"$qube"/whitelisted-appmenus.list
+
+ applications_modified=1
+ fi
+ done
+
+ elif [ "$option" = Selected ]; then
+
+ applications=1
+
+ while [ -n "$applications" ]; do
+
+ applications=$(qvm-appmenus --get-available --i-understand-format-is-unstable "$qube" | grep "$(qvm-appmenus --get-whitelist --i-understand-format-is-unstable "$qube")" | dmenu-unlinked -l 50 -p "$qube:" | cut -f1 -d\ )
+
+ if [ -n "$applications" ]; then
+
+ for application in $applications; do
+
+ # Look up linenumber above $application in
+ # whitelisted-appmenus.list to delete if it is garbage whitespace.
+ whitespace=$(grep -n1 "^$application$" < "$HOME"/.local/share/qubes-appmenus/"$qube"/whitelisted-appmenus.list | sed '1q;d' | cut -d- -f1)
+
+ sed -i "${whitespace}{/^$/d;}" "$HOME"/.local/share/qubes-appmenus/"$qube"/whitelisted-appmenus.list
+ sed -i "/$application/d" "$HOME"/.local/share/qubes-appmenus/"$qube"/whitelisted-appmenus.list
+
+ applications_modified=1
+ done
+ fi
+ done
+
+ elif [ "$option" = Refresh ]; then
+
+ qvm-appmenus -q --force --update "$qube"
+ fi
+
+ # Only update qvm-appmenus down here, *after* the user
+ # has quit the application selection to improve speed and reduce unnecessary updates.
+ if [ -n "$applications_modified" ]; then
+
+ qvm-appmenus -q --update "$qube"
+
+ unset applications_modified
+ fi
+done
diff --git a/qmenu/qmenu_vm/fqvm_clone b/qmenu/qmenu_vm/fqvm_clone
new file mode 100644
index 0000000..d31b297
--- /dev/null
+++ b/qmenu/qmenu_vm/fqvm_clone
@@ -0,0 +1,39 @@
+clone_name=$(: | dmenu-unlinked -p "Enter the name for the clone of $qube:") &&
+
+ option=1
+
+ while [ -n "$option" ]; do
+
+ option=$(printf 'Create the clone\nAdvanced options' | dmenu-unlinked -i -l 2 -p "$qube:" | cut -f1 -d\ )
+
+ if [ "$option" = Advanced ]; then
+
+ option_adv=1
+
+ while [ -n "$option_adv" ]; do
+
+ option_adv=$(printf 'Class\nPool' | dmenu-unlinked -i -l 2 -p "$clone_name($qube):")
+
+ if [ "$option_adv" = Class ]; then
+
+ class=$(printf 'AppVM\nDispVM\nStandaloneVM\nTemplateVM' | dmenu-unlinked -i -l 4 -p "Choose a class for $clone_name:") &&
+
+ class="-C $class"
+
+ elif [ "$option_adv" = Pool ]; then
+
+ pool=$(qvm-pool --list | sed '1d' | dmenu-unlinked -i -l 10 -p "Select a pool for $clone_name:" | cut -f1 -d\ ) &&
+
+ pool="-P $pool"
+ fi
+ done
+
+ elif [ "$option" = Create ]; then
+
+ unset option
+
+ cp -n "$HOME/.local/share/qubes-notes/$qube" "$HOME/.local/share/qubes-notes/$clone_name"
+
+ qvm-clone $class $pool "$qube" "$clone_name"&
+ fi
+ done
diff --git a/qmenu/qmenu_vm/fqvm_create b/qmenu/qmenu_vm/fqvm_create
new file mode 100644
index 0000000..b1990da
--- /dev/null
+++ b/qmenu/qmenu_vm/fqvm_create
@@ -0,0 +1,77 @@
+name=$(: | dmenu-unlinked -p "Enter the name for the new qube:") &&
+
+ label=$(printf 'Red\nOrange\nYellow\nGreen\nGray\nBlue\nPurple\nBlack' | dmenu-unlinked -i -l 8 -p "Select label for $name:" | awk '{print tolower($0)}')
+
+ if [ -n "$label" ]; then
+
+ QUBEARGS="-l $label"
+
+ class=$(printf 'AppVM\nStandaloneVM\nTemplateVM' | dmenu-unlinked -i -l 4 -p "Choose a class for $name:")
+
+ QUBEARGS="$QUBEARGS -C $class"
+
+ get_list template
+
+ if [ "$class" = 'AppVM' ]; then
+
+ template=$(printf "(Default)\n$list" | dmenu-unlinked -l 50 -p "Select template for $name:")
+
+ [ "$template" = '(Default)' ] && unset template
+
+ else
+ template=$(printf "(None)\n$list" | dmenu-unlinked -l 50 -p "Select template that $name will be copied from:")
+
+ [ "$template" = '(None)' ] && unset template
+ fi
+
+ [ -n "$template" ] && QUBEARGS="$QUBEARGS -t $template"
+
+ get_list netvm
+
+ netvm=$(printf "(Default)\n(None)\n$list" | dmenu-unlinked -l 50 -p "Select netvm for $name:")
+
+ if [ "$netvm" = '(Default)' ]; then netvm=--default
+ elif [ "$netvm" = '(None)' ]; then unset netvm
+ fi
+
+ QUBEARGS="$QUBEARGS --prop netvm=$netvm"
+
+ provides_network=$(printf 'No\nYes' | dmenu-unlinked -i -p "Should $name provide networking for other qubes?")
+
+ if [ "$provides_network" = Yes ]; then
+
+ QUBEARGS="$QUBEARGS --prop provides_network=true"
+ fi
+
+ option=1
+
+ while [ -n "$option" ]; do
+
+ option=$(printf 'Create the new qube\nAdvanced options' | dmenu-unlinked -i -l 2 -p "dom0:" | cut -f1 -d\ )
+
+ if [ "$option" = Advanced ]; then
+
+ option_adv=$(printf Pool | dmenu-unlinked -l 1 -p "$name:")
+
+ if [ "$option_adv" = Pool ]; then
+
+ pool=$(qvm-pool --list | sed '1d' | dmenu-unlinked -i -l 10 -p "Select a pool for $name:" | cut -f1 -d\ )
+
+ [ -n "$pool" ] && pool="-P $pool"
+ fi
+
+ elif [ "$option" = Create ]; then
+
+ qvm-create $QUBEARGS $pool "$name"
+
+ if [ "$class" = 'StandaloneVM' ] && [ -z "$template" ]; then
+
+ qvm-prefs "$name" kernel ''
+ qvm-prefs "$name" virt_mode hvm
+ qvm-prefs "$name" maxmem 0
+ fi
+
+ unset option
+ fi
+ done
+ fi
diff --git a/qmenu/qmenu_vm/fqvm_device b/qmenu/qmenu_vm/fqvm_device
new file mode 100644
index 0000000..a66d6df
--- /dev/null
+++ b/qmenu/qmenu_vm/fqvm_device
@@ -0,0 +1,70 @@
+if ! qvm-check -q --running "$qube"; then
+
+ echo Go back... | dmenu-unlinked -p "$qube needs to be running, in order to attach $(echo "$chosefrom" | cut -f1 -d\ | awk '{print tolower($0)}') devices to it." > /dev/null 2>&1
+else
+ case $chosefrom in
+
+ Audio*) device_type='device mic';;
+
+ Block*) device_type=block;;
+
+ USB*) device_type=usb
+ esac
+
+ devices_list=$(qvm-$device_type)
+
+ device=1
+
+ while [ -n "$device" ]; do
+
+ device=$(echo "$devices_list" | dmenu-unlinked -l 16 -p "$qube:")
+
+ if [ -n "$device" ] && [ "$(echo "$device" | wc -l)" -eq 1 ]; then
+
+ device_id=$(echo "$device" | cut -f1 -d\ )
+
+ if [ "$device_type" = block ]; then
+
+ device_name=$(echo "$device" | awk '{print $2,$3}')
+ holds_qube=$(echo "$device" | awk '{print $4}')
+ else
+ device_name=$(echo "$device" | awk '{print $2}')
+ holds_qube=$(echo "$device" | awk '{print $3}')
+ fi
+
+ if [ -z "$holds_qube" ]; then
+
+ if nyprompt "Attach '$device_name' to $qube?"; then
+
+ unset device
+
+ qvm-$device_type attach -q "$qube" "$device_id" ||
+
+ echo Go back... | dmenu-unlinked -p "Error: Failed to attach device!" > /dev/null 2>&1
+ fi
+
+ elif [ "$holds_qube" = "$qube" ]; then
+
+ if nyprompt "Detach '$device_name' from $qube?"; then
+
+ unset device
+
+ qvm-$device_type detach -q "$holds_qube" "$device_id" ||
+
+ echo Go back... | dmenu-unlinked -p "Error: Failed to detach device!" > /dev/null 2>&1
+ fi
+ else
+ if nyprompt "Detach '$device_name' from $holds_qube and attach it to $qube?"; then
+
+ unset device
+
+ qvm-$device_type detach -q "$holds_qube" "$device_id" &&
+
+ qvm-$device_type attach -q "$qube" "$device_id" ||
+
+ echo Go back... | dmenu-unlinked -p "Error: Failed to handle device!" > /dev/null 2>&1
+ fi
+ fi
+ fi
+ done
+fi
diff --git a/qmenu/qmenu_vm/fqvm_firewall b/qmenu/qmenu_vm/fqvm_firewall
new file mode 100644
index 0000000..357ff28
--- /dev/null
+++ b/qmenu/qmenu_vm/fqvm_firewall
@@ -0,0 +1,81 @@
+rulenumber=1
+
+while [ -n "$rulenumber" ]; do
+
+ rulenumber=$(qvm-firewall "$qube" list | dmenu-unlinked -l 50 -p "$qube:" | cut -f1 -d\ )
+
+ if [ "$(echo "$rulenumber" | wc -w)" -eq 1 ]; then
+
+ # This will equal "NO" if the user selects the top row,
+ # instead of any existing rule.
+ if [ "$rulenumber" != NO ]; then
+
+ option=$(printf "Add new rule above rule $rulenumber\nRemove rule $rulenumber" | dmenu-unlinked -i -l 2 -p "$qube:" | cut -f1 -d\ )
+ else
+ option=Add
+ fi
+
+ if [ "$option" = Remove ]; then
+
+ nyprompt "Remove rule $rulenumber?" &&
+
+ qvm-firewall "$qube" del --rule-no "$rulenumber"
+
+ elif [ "$option" = Add ]; then
+
+ [ -n "$RULEARGS" ] && unset RULEARGS
+
+ action=$(printf 'Accept\nDrop' | dmenu-unlinked -i -l 2 -p "Select action for the new firewall rule:" | awk '{print tolower($0)}')
+
+ if [ -n "$action" ]; then
+
+ # Prompt the user to escape matches they want to leave empty. Leaving a match empty by pressing <Return> will prevent the rule from being created.
+ echo Continue... | dmenu-unlinked -p 'You will now be prompted to select the matches for the new rule. Please skip matches that you want to leave empty by escaping with <Escape> or <C-c>.' > /dev/null 2>&1
+
+ RULEARGS="$action"
+
+ specialtarget=$(: | dmenu-unlinked -p "ACTION=$RULEARGS <specialtarget>") &&
+
+ RULEARGS="$RULEARGS SPECIALTARGET=$specialtarget"
+
+ dsthost=$(: | dmenu-unlinked -p "ACTION=$RULEARGS <dsthost>") &&
+
+ RULEARGS="$RULEARGS DSTHOST=$dsthost"
+
+ proto=$(printf 'tcp\nudp\nicmp' | dmenu-unlinked -l 3 -p "ACTION=$RULEARGS <proto>") &&
+
+ RULEARGS="$RULEARGS PROTO=$proto"
+
+ if [ "$proto" = tcp ] || [ "$proto" = udp ]; then
+
+ dstports=$(: | dmenu-unlinked -p "ACTION=$RULEARGS <dstports>") &&
+
+ RULEARGS="$RULEARGS DSTPORTS=$dstports"
+
+ elif [ "$proto" = icmp ]; then
+
+ icmptype=$(: | dmenu-unlinked -p "ACTION=$RULEARGS <icmptype>") &&
+
+ RULEARGS="$RULEARGS ICMPTYPE=$icmptype"
+ fi
+
+ comment=$(: | dmenu-unlinked -p "ACTION=$RULEARGS <comment>") &&
+
+ RULEARGS="$RULEARGS COMMENT=$comment"
+
+ if nyprompt "Add the following rule to $qube? {{ ACTION=$RULEARGS }}"; then
+
+ [ -n "$beforerule" ] && unset beforerule
+
+ [ "$rulenumber" != NO ] && beforerule=$(echo --before "$rulenumber")
+
+ RULEARGS=$(echo "$RULEARGS" | awk '{print tolower($0)}')
+
+ qvm-firewall "$qube" add $beforerule $RULEARGS ||
+
+ echo Go back... | dmenu-unlinked -p 'Error: Failed to add firewall rule! See 'qvm-firewall --help' for more information.' > /dev/null 2>&1
+ fi
+ fi
+ fi
+ fi
+done
diff --git a/qmenu/qmenu_vm/fqvm_pci b/qmenu/qmenu_vm/fqvm_pci
new file mode 100644
index 0000000..c71d055
--- /dev/null
+++ b/qmenu/qmenu_vm/fqvm_pci
@@ -0,0 +1,70 @@
+if qvm-check -q --running "$qube"; then
+
+ echo Go back... | dmenu-unlinked -p "$qube needs to be powered off, in order to attach or detach PCI devices." > /dev/null 2>&1
+else
+ list_pci=$(qvm-pci)
+
+ qvm-prefs "$qube" maxmem | grep -q ^0 ||
+
+ answer=$(printf 'Continue anyways\nDisable dynamic memory balancing' | dmenu-unlinked -l 2 -i -p "Dynamic memory balancing is enabled in $qube, some devices might not work!" | cut -f1 -d\ )
+
+ [ "$answer" = Disable ] && qvm-prefs "$qube" maxmem 0
+
+ qvm-prefs "$qube" virt_mode | grep -q pvh &&
+
+ answer=$(printf 'Continue anyways\nChange to another virtualisation mode' | dmenu-unlinked -l 2 -i -p "$qube is using PVH for its virtualisation mode, which does not support PCI passthrough!" | cut -f1 -d\ )
+
+ if [ "$answer" = Change ]; then
+
+ virtmode=$(printf 'HVM\nPV' | dmenu-unlinked -l 2 -i -p "Select virtualisation mode for $qube:") &&
+
+ qvm-prefs "$qube" virt_mode "$virtmode"
+ fi
+
+ device=1
+
+ while [ -n "$device" ]; do
+
+ device=$(echo "$list_pci" | dmenu-unlinked -l 30 -p "$qube:")
+
+ if [ -n "$device" ] && [ "$(echo "$device" | wc -l)" -eq 1 ]; then
+
+ device_src=$(echo "$device" | cut -f1 -d\ | sed 's/:.*//')
+ device_bdf=$(echo "$device" | cut -f1 -d\ | sed 's/.*://')
+
+ if echo "$device" | grep -q " $qube$\\| $qube (no-strict-reset=True)$\\| $qube (permissive=True)$"; then
+
+ if nyprompt "Detach \"$device_bdf\" from $qube?"; then
+
+ qvm-pci detach "$qube" "$device_src":"$device_bdf" ||
+
+ echo Go back... | dmenu-unlinked -p "Error: Failed to detach \"$device_bdf\" from $qube!" > /dev/null 2>&1
+
+ list_pci=$(qvm-pci)
+ fi
+ else
+ if nyprompt "Attach \"$device_bdf\" to $qube?"; then
+
+ # Check if there is more than one function
+ # that belongs to the same device.
+ bdf_count=$(echo "$list_pci" | cut -f1 -d\ | grep -c $(echo "$device_bdf" | sed 's/\..*//'))
+
+ if [ "$bdf_count" -gt 1 ]; then
+
+ [ -n "$pci_option" ] && unset pci_option
+
+ answer=$(printf 'No, attach without this option\nYes' | dmenu-unlinked -i -p "\"$device_bdf\" is most likely to be attached with the option 'no-strict-reset' enabled. Do not enable this option if you are unaware of the security implications! Do you want to attach \"$device_bdf\" with the option 'no-strict-reset' set to true?")
+
+ [ "$answer" = Yes ] && pci_option='-o no-strict-reset=True'
+ fi
+
+ qvm-pci attach --persistent $pci_option "$qube" "$device_src:$device_bdf" ||
+
+ echo Go back... | dmenu-unlinked -p "Error: Failed to attach '$device_bdf' to $qube!" > /dev/null 2>&1
+
+ list_pci=$(qvm-pci)
+ fi
+ fi
+ fi
+ done
+fi
diff --git a/qmenu/qmenu_vm/fqvm_prefs b/qmenu/qmenu_vm/fqvm_prefs
new file mode 100644
index 0000000..0609923
--- /dev/null
+++ b/qmenu/qmenu_vm/fqvm_prefs
@@ -0,0 +1,155 @@
+property=1
+
+while [ -n "$property" ]; do
+
+ property=$(qvm-prefs "$qube" | dmenu-unlinked -l 45 -p "$qube:" | cut -f1 -d\ )
+
+ if [ -n "$property" ]; then
+
+ case $property in
+
+ autostart)
+
+ value=$(printf 'False\nTrue' | dmenu-unlinked -i -p "Set autostart of $qube to:");;
+
+ debug)
+
+ value=$(printf 'False\nTrue' | dmenu-unlinked -i -p "Set debug mode of $qube to:");;
+
+ default_dispvm)
+
+ if [ -z "$LIST_DISPTEMP" ]; then
+
+ # [mediocre] Look up disposable templates in a very slow way because qvm-ls does not offer a flag for them
+ for n in $(qvm-ls --raw-data -O NAME,FLAGS | grep '|a.....-.$' | cut -d '|' -f1); do
+
+ qvm-prefs "$n" template_for_dispvms | grep -q True &&
+
+ LIST_DISPTEMP="$LIST_DISPTEMP\n$n"
+ done
+ fi
+
+ value=$(printf "(Default)\n(None)$LIST_DISPTEMP" | dmenu-unlinked -l 50 -p "Select default dispvm for $qube:");;
+
+ default_user)
+
+ value=$(echo --default | dmenu-unlinked -p "Enter the name of the default user for $qube:");;
+
+ include_in_backups)
+
+ value=$(printf 'False\nTrue' | dmenu-unlinked -i -p "Include $qube in backups?");;
+
+ kernel)
+
+ value=$(printf "(Default)\n(None)" | dmenu-unlinked -l 2 -p "Enter the kernel to be used by $qube:");;
+
+ kernelopts)
+
+ value=$(echo --default | dmenu-unlinked -p "Enter the kernel options for $qube:") &&
+
+ nyprompt "Set kernel options for $qube to \"$value\"?" || unset value;;
+
+ label)
+
+ if qvm-check -q --running "$qube"; then
+
+ echo Go back... | dmenu-unlinked -p "$qube needs to be powered off, in order to change its label." > /dev/null 2>&1
+ else
+ value=$(printf 'Purple\nBlue\nGray\nGreen\nYellow\nOrange\nRed\nBlack' | dmenu-unlinked -i -l 8 -p "Select label for $qube:" | awk '{print tolower($0)}')
+ fi;;
+
+ mac)
+
+ value=$(echo --default | dmenu-unlinked -p "Enter the MAC address for $qube:") &&
+
+ nyprompt "Set MAC address of $qube to $value?" || unset value;;
+
+ maxmem)
+
+ value=$(echo --default | dmenu-unlinked -p "Enter the maximum amount of memory in MB to be allocated to $qube. Setting it to 0 will disable dynamic memory balancing.") &&
+
+ if nyprompt "Set maximum memory of $qube to $value MB?"; then
+
+ # For linux qubes, initial memory can not be less than one tenth of maxmem
+ # so we will automatically set the minimum allowed value.
+ # Users can still overwrite this manually via fqvm_prefs - memory.
+
+ minmem=$(( $value / 10 ))
+
+ [ "$(qvm-prefs $qube memory)" -lt "$minmem" ] && qvm-prefs "$qube" memory "$minmem"
+ else
+ unset value
+ fi;;
+
+ memory)
+
+ value=$(echo --default | dmenu-unlinked -p "Enter the amount of initial memory in MB to be allocated to $qube:") &&
+
+ nyprompt "Set initial memory of $qube to $value MB?" || unset value;;
+
+ netvm)
+
+ get_list netvm
+
+ value=$(printf "(Default)\n(None)\n$list" | dmenu-unlinked -l 50 -p "Select netvm for $qube:");;
+
+ provides_network)
+
+ value=$(printf 'False\nTrue' | dmenu-unlinked -i -p "Should $qube provide networking to other qubes?");;
+
+ qrexec_timeout)
+
+ value=$(echo --default | dmenu-unlinked -p "Enter the time in seconds, after which qrexec connection attempts are deemed a failure for $qube:") &&
+
+ nyprompt "Set qrexec timeout for $qube to $value seconds?" || unset value;;
+
+ shutdown_timeout)
+
+ value=$(echo --default | dmenu-unlinked -p "Enter the time in seconds to wait for shutdown, after which $qube may be forcefully powered off:") &&
+
+ nyprompt "Set shutdown timeout for $qube to $value seconds?" || unset value;;
+
+ template)
+
+ if qvm-check -q --running "$qube"; then
+
+ echo Go back... | dmenu-unlinked -p "$qube needs to be powered off, in order to change its template." > /dev/null 2>&1
+ else
+ get_list template
+
+ value=$(printf "(Default)\n$list" | dmenu-unlinked -l 50 -p "Select template for $qube:") &&
+
+ (sleep 2 && qvm-appmenus -q --update "$qube"&)
+ fi;;
+
+ template_for_dispvms)
+
+ value=$(printf 'False\nTrue' | dmenu-unlinked -i -p "Should $qube be used as a template for disposable qubes?");;
+
+ vcpus)
+
+ value=$(echo --default | dmenu-unlinked -p "Enter the number of CPU cores that should be made available to $qube:") &&
+
+ nyprompt "Set number of CPU cores available to $qube to $value?" || unset value;;
+
+ virt_mode)
+
+ value=$(printf 'PVH\nHVM\nPV' | dmenu-unlinked -i -l 3 -p "Select virtualisation mode for $qube:");;
+
+ *)
+ esac
+
+ if [ -n "$value" ]; then
+
+ if [ "$value" = '(None)' ]; then value=''
+ elif [ "$value" = '(Default)' ]; then value=--default
+ fi
+
+ qvm-prefs "$qube" "$property" "$value" ||
+
+ echo Go back... | dmenu-unlinked -p "Error: Failed to set $property to '$value'!" > /dev/null 2>&1
+
+ unset value
+ fi
+ fi
+done
diff --git a/qmenu/qmenu_vm/fqvm_remove b/qmenu/qmenu_vm/fqvm_remove
new file mode 100644
index 0000000..91ff8dd
--- /dev/null
+++ b/qmenu/qmenu_vm/fqvm_remove
@@ -0,0 +1,18 @@
+if qvm-check -q --running "$qube"; then
+
+ echo Go back... | dmenu-unlinked -p "$qube needs to be powered off, in order to be removed." > /dev/null 2>&1
+else
+ confirmation=$(: | dmenu-unlinked -p "Enter the name of the qube '$qube' in order to remove it:")
+
+ if [ "$qube" = "$confirmation" ]; then
+
+ if nyprompt "Are you sure you want to remove '$qube' permanently?"; then
+
+ unset chosefrom
+
+ rm -f "$HOME/.local/share/qubes-notes/$qube"
+
+ $(qvm-remove -f "$qube" || notify-send -u normal "Error: Failed to remove "$qube"!")&
+ fi
+ fi
+fi
diff --git a/qmenu/qmenu_vm/fqvm_run b/qmenu/qmenu_vm/fqvm_run
new file mode 100644
index 0000000..18db4f3
--- /dev/null
+++ b/qmenu/qmenu_vm/fqvm_run
@@ -0,0 +1,14 @@
+if user=$(printf 'Default user\nRoot' | dmenu-unlinked -i -l 2 -p "Run commands as:"); then
+
+ [ "$user" = Root ] && as_root='-u root' && user=# || user=$
+
+ commandtr=1
+
+ while [ -n "$commandtr" ]; do
+
+ if commandtr=$(: | dmenu-unlinked -p "[@$qube]$user"); then
+
+ qvm-run -q $as_root "$qube" "$commandtr"&
+ fi
+ done
+fi
diff --git a/qmenu/qmenu_vm/fqvm_service b/qmenu/qmenu_vm/fqvm_service
new file mode 100644
index 0000000..cc8baaa
--- /dev/null
+++ b/qmenu/qmenu_vm/fqvm_service
@@ -0,0 +1,39 @@
+services=1
+
+while [ -n "$services" ]; do
+
+ services=$(qvm-service "$qube" --list | dmenu-unlinked -l 32 -p "$qube:" | cut -f1 -d\ )
+
+ if [ -n "$services" ]; then
+
+ for service in $services; do
+
+ qvm-service "$qube" --list | grep -q "^$service " &&
+
+ unset='\nUnset'
+
+ value=$(printf "Disable\nEnable$unset" | dmenu-unlinked -l 3 -i -p "Select value for '$service':")
+
+ if [ "$value" = Disable ]; then
+
+ qvm-service "$qube" "$service" off ||
+
+ echo Go back... | dmenu-unlinked -p "Error: Failed to deactivate '$service'!" > /dev/null 2>&1
+
+ elif [ "$value" = Enable ]; then
+
+ qvm-service "$qube" "$service" on ||
+
+ echo Go back... | dmenu-unlinked -p "Error: Failed to activate '$service'!" > /dev/null 2>&1
+
+ elif [ "$value" = Unset ]; then
+
+ qvm-service "$qube" "$service" --unset ||
+
+ echo Go back... | dmenu-unlinked -p "Error: Failed to unset '$service'!" > /dev/null 2>&1
+ fi
+
+ [ -n "$unset" ] && unset unset
+ done
+ fi
+done
diff --git a/qmenu/qmenu_vm/fqvm_tags b/qmenu/qmenu_vm/fqvm_tags
new file mode 100644
index 0000000..6c6ee6f
--- /dev/null
+++ b/qmenu/qmenu_vm/fqvm_tags
@@ -0,0 +1,19 @@
+tags=1
+
+while [ -n "$tags" ]; do
+
+ tags=$(qvm-tags "$qube" list | dmenu-unlinked -l 15 -p "$qube:" | cut -f1 -d\ )
+
+ if [ -n "$tags" ]; then
+
+ for tag in $tags; do
+
+ if qvm-tags "$qube" list | grep -q "^$tag$"; then
+
+ nyprompt "Remove tag '$tag' from $qube?" && qvm-tags "$qube" del "$tag"
+ else
+ qvm-tags "$qube" add "$tag"
+ fi
+ done
+ fi
+done
diff --git a/qmenu/qmenu_vm/fqvm_volume b/qmenu/qmenu_vm/fqvm_volume
new file mode 100644
index 0000000..8b762e0
--- /dev/null
+++ b/qmenu/qmenu_vm/fqvm_volume
@@ -0,0 +1,26 @@
+qube_class=$(qvm-ls --raw-data -O flags "$qube" | cut -c 1)
+
+if [ "$qube_class" = t ] || [ "$qube_class" = s ] || [ "$qube_class" = S ]; then
+
+ volume=$(printf 'Root\nPrivate' | dmenu-unlinked -l 2 -i -p "$qube:" | awk '{print tolower($0)}')
+else
+ volume=private
+fi
+
+if [ -n "$volume" ]; then
+
+ current_storage=$(( $(qvm-volume info "$qube:$volume" size) / 1048576 ))
+
+ mebibyte=$(echo "$current_storage" | dmenu-unlinked -p "Enter the maximum size of $volume storage in MiB to be allocated to $qube:") &&
+
+ if [ "$mebibyte" -gt "$current_storage" ]; then
+
+ byte=$(( mebibyte * 1048576 ))
+
+ nyprompt "Increase the maximum $volume storage size of $qube from $current_storage MiB to $mebibyte MiB?" &&
+
+ (qvm-volume extend "$qube:$volume" "$byte"&)
+ else
+ echo Go back... | dmenu-unlinked -p "Warning: Unable to decrease the maximum $volume storage!" > /dev/null 2>&1
+ fi
+fi