_(Libvirt volume info)_
_(btrfs filesystem show)_:
diff --git a/emhttp/plugins/dynamix.vm.manager/scripts/libvirt_init b/emhttp/plugins/dynamix.vm.manager/scripts/libvirt_init
index 4b98d16f0c..1189383f11 100755
--- a/emhttp/plugins/dynamix.vm.manager/scripts/libvirt_init
+++ b/emhttp/plugins/dynamix.vm.manager/scripts/libvirt_init
@@ -5,6 +5,64 @@
# run & log functions
. /etc/rc.d/rc.runlog
+
+# Sync domain data if IMAGE_FILE and OLD_IMAGE_FILE differ
+DOMAIN_CFG=/boot/config/domain.cfg
+
+# Read values from domain.cfg
+eval $(grep -E '^(IMAGE_FILE|OLD_IMAGE_FILE)=' "$DOMAIN_CFG")
+
+# Remove quotes
+IMAGE_FILE="${IMAGE_FILE%\"}"
+IMAGE_FILE="${IMAGE_FILE#\"}"
+OLD_IMAGE_FILE="${OLD_IMAGE_FILE%\"}"
+OLD_IMAGE_FILE="${OLD_IMAGE_FILE#\"}"
+
+# Proceed only if both variables are set and OLD_IMAGE_FILE exists
+if [ -n "$IMAGE_FILE" ] && [ -n "$OLD_IMAGE_FILE" ] && [ "$IMAGE_FILE" != "$OLD_IMAGE_FILE" ]; then
+ if [ ! -e "$OLD_IMAGE_FILE" ]; then
+ log "OLD_IMAGE_FILE not found: $OLD_IMAGE_FILE — skipping sync"
+ else
+ log "IMAGE_FILE and OLD_IMAGE_FILE differ, syncing..."
+
+ TMP_MNT=/etc/libvirt-sync
+ IMG_FILE_NAME=$(basename "$IMAGE_FILE")
+ OLD_IMG_FILE_NAME=$(basename "$OLD_IMAGE_FILE")
+ TIMESTAMP=$(date +%Y%m%d-%H%M%S)
+
+ if [[ "$OLD_IMAGE_FILE" == *.img ]]; then
+ # Backup image before mounting
+ BACKUP_PATH="${OLD_IMAGE_FILE%.img}.bak-${TIMESTAMP}.img"
+ log "Creating backup of OLD_IMAGE_FILE: $BACKUP_PATH"
+ cp -p "$OLD_IMAGE_FILE" "$BACKUP_PATH"
+
+ log "Mounting $OLD_IMAGE_FILE to $TMP_MNT"
+ mkdir -p "$TMP_MNT"
+ mount "$OLD_IMAGE_FILE" "$TMP_MNT"
+ log "Copying full contents from image to directory $IMAGE_FILE"
+ rsync -a --exclude="$OLD_IMG_FILE_NAME" "$TMP_MNT/" "$IMAGE_FILE/"
+ umount "$TMP_MNT"
+ elif [[ "$IMAGE_FILE" == *.img ]]; then
+ log "Mounting $IMAGE_FILE to $TMP_MNT"
+ mkdir -p "$TMP_MNT"
+ mount "$IMAGE_FILE" "$TMP_MNT"
+ log "Copying full contents from directory $OLD_IMAGE_FILE to image"
+ rsync -a --exclude="$IMG_FILE_NAME" --exclude='*.bak-*.img' "$OLD_IMAGE_FILE/" "$TMP_MNT/"
+ umount "$TMP_MNT"
+ else
+ log "Both IMAGE_FILE and OLD_IMAGE_FILE are directories, copying full contents"
+ rsync -a --exclude="$IMG_FILE_NAME" "$OLD_IMAGE_FILE/" "$IMAGE_FILE/"
+ fi
+
+ # Update OLD_IMAGE_FILE in domain.cfg
+ log "Updating OLD_IMAGE_FILE in $DOMAIN_CFG"
+ sed -i "s|^OLD_IMAGE_FILE=.*|OLD_IMAGE_FILE=\"$IMAGE_FILE\"|" "$DOMAIN_CFG"
+ fi
+else
+ log "IMAGE_FILE and OLD_IMAGE_FILE match, or one is unset — skipping sync"
+fi
+
+
# missing qemu directory would indicate new libvirt image file created
if [ ! -d /etc/libvirt/qemu ]; then
log "initializing /etc/libvirt"
diff --git a/emhttp/plugins/dynamix.vm.manager/scripts/libvirtconfig b/emhttp/plugins/dynamix.vm.manager/scripts/libvirtconfig
index 12ae126341..5d6e0f269e 100755
--- a/emhttp/plugins/dynamix.vm.manager/scripts/libvirtconfig
+++ b/emhttp/plugins/dynamix.vm.manager/scripts/libvirtconfig
@@ -15,7 +15,8 @@
$cfgfile = "/boot/config/domain.cfg";
$cfg_defaults = [
"SERVICE" => "disable",
- "IMAGE_FILE" => "/mnt/user/system/libvirt/libvirt.img",
+ "IMAGE_FILE" => "/mnt/user/system/libvirt/",
+ "OLD_IMAGE_FILE" => "/mnt/user/system/libvirt/",
"IMAGE_SIZE" => "1",
"DEBUG" => "no",
"DOMAINDIR" => "/mnt/user/domains/",
diff --git a/emhttp/plugins/dynamix.vm.manager/scripts/libvirtcopy b/emhttp/plugins/dynamix.vm.manager/scripts/libvirtcopy
new file mode 100755
index 0000000000..222ce20051
--- /dev/null
+++ b/emhttp/plugins/dynamix.vm.manager/scripts/libvirtcopy
@@ -0,0 +1,150 @@
+#!/usr/bin/php
+
+
+
+/* ---------------------------------------------------------
+ * Read default VM directory from Unraid domain.cfg
+ * --------------------------------------------------------- */
+function get_default_domain_dir() {
+ $cfg = '/boot/config/domain.cfg';
+
+ if (!file_exists($cfg)) {
+ return null;
+ }
+
+ $lines = file($cfg, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
+ if ($lines === false) {
+ return null;
+ }
+
+ foreach ($lines as $line) {
+ $line = trim($line);
+
+ if ($line === '' || $line[0] === '#') {
+ continue;
+ }
+
+ if (preg_match('/^DOMAINDIR="([^"]+)"/', $line, $m)) {
+ return rtrim($m[1], '/');
+ }
+ }
+
+ return null;
+}
+
+/* ---------------------------------------------------------
+ * Connect to libvirt
+ * --------------------------------------------------------- */
+$lv = libvirt_connect('qemu:///system', false);
+if (!$lv) {
+ die("Failed to connect to libvirt\n");
+}
+
+/* Running VMs (or all, if you prefer libvirt_list_all_domains) */
+$domains = libvirt_list_domains($lv);
+if ($domains === false) {
+ die("Failed to list domains\n");
+}
+
+$default_domain_dir = get_default_domain_dir();
+
+$vms = [];
+
+/* ---------------------------------------------------------
+ * Enumerate VMs
+ * --------------------------------------------------------- */
+foreach ($domains as $dom) {
+
+ $domget = libvirt_domain_lookup_by_name($lv, $dom);
+ if ($domget === false) {
+ continue;
+ }
+
+ $xml = libvirt_domain_get_xml_desc($domget, 0);
+ if ($xml === false) {
+ continue;
+ }
+
+ $sx = new SimpleXMLElement($xml);
+
+ $vm_name = (string)$sx->name;
+ $uuid = (string)$sx->uuid;
+
+ /* -----------------------------------------------------
+ * Read storage metadata (Unraid vmtemplate)
+ * ----------------------------------------------------- */
+ $metadata_storage = null;
+
+ if (isset($sx->metadata)) {
+ foreach ($sx->metadata->children() as $child) {
+ if ($child->getName() === 'vmtemplate') {
+ $metadata_storage = trim((string)$child['storage']);
+ break;
+ }
+ }
+ }
+
+ /* -----------------------------------------------------
+ * Resolve filesystem path
+ * Treat empty, null, or "default" as DOMAINDIR
+ * ----------------------------------------------------- */
+ if ($metadata_storage === null || $metadata_storage === '' || strtolower($metadata_storage) === 'default') {
+ /* TRUE default storage */
+ $path_root = $default_domain_dir; // e.g. /mnt/user/domains2
+ $storage_name = 'default';
+ } else {
+ /* Explicit Unraid pool */
+ $path_root = '/mnt/user/' . $metadata_storage;
+ $storage_name = $metadata_storage;
+ }
+
+
+ $path = $path_root
+ ? $path_root . '/' . $vm_name
+ : null;
+
+
+ /* Filesystem existence check (remove quotes for is_dir) */
+ $exists = ($path_root && is_dir($path_root . '/' . $vm_name));
+
+ /* -----------------------------------------------------
+ * Store result
+ * ----------------------------------------------------- */
+ $vms[$vm_name] = [
+ 'uuid' => $uuid,
+ 'storage' => $storage_name,
+ 'path' => $path,
+ 'path_shell' => $path ? escapeshellarg($path) : null,
+ 'exists' => $exists,
+ ];
+}
+
+/* ---------------------------------------------------------
+ * Output
+ * --------------------------------------------------------- */
+#print_r($vms);
+
+file_put_contents("/boot/config/plugins/dynamix.vm.manager/vms.json",json_encode($vms,JSON_PRETTY_PRINT));
+
+
+foreach ($vms as $vm => $vmdetail) {
+ file_put_contents("/tmp/Stopcopy","");
+ $from_file = "/etc/libvirt/qemu/$vm.xml";
+ $to_file = $vmdetail['path']."/$vm.xml";
+ #echo " from:$from_file to:$to_file";
+ if ($vmdetail['exists']) {
+ file_put_contents("/tmp/Stopcopy","$vm from:$from_file to:$to_file\n",FILE_APPEND); #echo " from:$from_file to:$to_file";
+ #copy($from_file,$to_file);
+ } else file_put_contents("/tmp/Stopcopy","Nocpy $vm from:$from_file to:$to_file\n",FILE_APPEND); #echo " from:$from_file to:$to_file";
+}
+?>
diff --git a/emhttp/plugins/dynamix.vm.manager/scripts/libvirtrestore b/emhttp/plugins/dynamix.vm.manager/scripts/libvirtrestore
new file mode 100755
index 0000000000..e153fc6bd5
--- /dev/null
+++ b/emhttp/plugins/dynamix.vm.manager/scripts/libvirtrestore
@@ -0,0 +1,28 @@
+#!/usr/bin/php
+
+
+
+$vmsjson = file_get_contents("/boot/config/plugins/dynamix.vm.manager/vms.json");
+$vms = json_decode($vmsjson,true);
+
+foreach ($vms as $vm => $vmdetail) {
+ file_put_contents("/tmp/Stopcopy","");
+ $to_file = "/etc/libvirt/qemu/$vm.xml";
+ $from_file = $vmdetail['path']."/$vm.xml";
+ #echo " from:$from_file to:$to_file";
+ if (file_exists($from_file)) {
+ file_put_contents("/tmp/libvirtrestore","$vm from:$from_file to:$to_file\n",FILE_APPEND); #echo " from:$from_file to:$to_file";
+ #copy($from_file,$to_file);
+ } else file_put_contents("/tmp/libvirtrestore","Nocpy $vm from:$from_file to:$to_file\n",FILE_APPEND); #echo " from:$from_file to:$to_file";
+}
+?>
diff --git a/emhttp/plugins/dynamix.vm.manager/scripts/savehook.php b/emhttp/plugins/dynamix.vm.manager/scripts/savehook.php
new file mode 100644
index 0000000000..f06e25279d
--- /dev/null
+++ b/emhttp/plugins/dynamix.vm.manager/scripts/savehook.php
@@ -0,0 +1,12 @@
+#!/usr/bin/env php
+
diff --git a/etc/rc.d/rc.libvirt b/etc/rc.d/rc.libvirt
index ae1c88e7b7..a53c4d237e 100755
--- a/etc/rc.d/rc.libvirt
+++ b/etc/rc.d/rc.libvirt
@@ -247,6 +247,9 @@ libvirtd_start(){
}
libvirtd_stop(){
+ # Save VM locations
+ /usr/local/emhttp/plugins/dynamix.vm.manager/scripts/libvirtcopy
+ #
log "Stopping $DAEMON..."
if [[ ! -f $LIBVIRTD_PIDFILE ]]; then
log "$DAEMON... Already stopped."