Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions emhttp/languages/en_US/helptext.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1711,7 +1711,11 @@ Stop VMs from Autostarting\Starting when VM Manager starts or open is run from t
:end

:vms_libvirt_volume_help:
This is the libvirt volume.
This is the libvirt volume/directory.
:end

:vms_libvirt_secondary_volume_help:
This is a location for storing previous versions of xml and nvram at change.
:end
Comment on lines +1717 to 1719
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix grammar: "at change" is awkward.

The phrase "at change" should be replaced with clearer alternatives. This issue was previously flagged and remains unresolved.

📝 Suggested fix
  :vms_libvirt_secondary_volume_help:
- This is a location for storing previous versions of xml and nvram at change.
+ This is a location for storing previous versions of XML and NVRAM when changes are made.

Or alternatively:

- This is a location for storing previous versions of xml and nvram at change.
+ This is a location for storing previous versions of XML and NVRAM on change.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
:vms_libvirt_secondary_volume_help:
This is a location for storing previous versions of xml and nvram at change.
:end
:vms_libvirt_secondary_volume_help:
This is a location for storing previous versions of XML and NVRAM when changes are made.
:end
🤖 Prompt for AI Agents
In @emhttp/languages/en_US/helptext.txt around lines 1717 - 1719, The help text
for :vms_libvirt_secondary_volume_help: is grammatically awkward—replace "at
change" with a clearer phrase such as "when changed" or "on change" (e.g., "This
is a location for storing previous versions of XML and NVRAM when changed.") and
ensure XML and NVRAM are properly capitalized; update the string for
:vms_libvirt_secondary_volume_help: accordingly.


:vms_libvirt_vdisk_size_help:
Expand All @@ -1720,7 +1724,11 @@ To resize an existing image file, specify the new size here. Next time the Libvi
:end

:vms_libvirt_location_help:
You must specify an image file for Libvirt. The system will automatically create this file when the Libvirt service is first started.
You must specify an image file/directory for Libvirt. The system will automatically create this file/directory when the Libvirt service is first started.
:end

:vms_libvirt_secondary_location_help:
This is a directory for storing previous versions of xml and nvram at change. Does not need to be specified.
:end
Comment on lines +1730 to 1732
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix grammar: "at change" is awkward.

The phrase "at change" should be replaced with clearer alternatives. This issue was previously flagged and remains unresolved. Additionally, "XML" and "NVRAM" should be capitalized for consistency with the rest of the documentation.

📝 Suggested fix
  :vms_libvirt_secondary_location_help:
- This is a directory for storing previous versions of xml and nvram at change. Does not need to be specified.
+ This is a directory for storing previous versions of XML and NVRAM when changes are made. Does not need to be specified.

Or alternatively:

- This is a directory for storing previous versions of xml and nvram at change. Does not need to be specified.
+ This is a directory for storing previous versions of XML and NVRAM on change; it does not need to be specified.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
:vms_libvirt_secondary_location_help:
This is a directory for storing previous versions of xml and nvram at change. Does not need to be specified.
:end
:vms_libvirt_secondary_location_help:
This is a directory for storing previous versions of XML and NVRAM when changes are made. Does not need to be specified.
:end
🤖 Prompt for AI Agents
In @emhttp/languages/en_US/helptext.txt around lines 1730 - 1732, Update the
help text for :vms_libvirt_secondary_location_help: to fix grammar and
capitalization by replacing "at change" with a clearer phrase such as "when
changed" or "on change", and capitalize "XML" and "NVRAM" (e.g., "This is a
directory for storing previous versions of XML and NVRAM when changed. Does not
need to be specified.").


:vms_libvirt_storage_help:
Expand Down
13 changes: 8 additions & 5 deletions emhttp/plugins/dynamix.vm.manager/VMSettings.page
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ $libvirt_log = file_exists("/var/log/libvirt/libvirtd.log");
<input type="hidden" name="#file" value="<?=htmlspecialchars($domain_cfgfile)?>">
<input type="hidden" name="#command" value="/plugins/dynamix/scripts/emcmd">
<input type="hidden" name="#arg[1]" value="cmdStatus=Apply">

<input type="hidden" id="OLD_IMAGE_FILE" name="OLD_IMAGE_FILE" value="<?=htmlspecialchars($domain_cfg['IMAGE_FILE']);?>">
_(Enable VMs)_:
: <select id="SERVICE" name="SERVICE">
<?= mk_option($libvirt_service, 'disable', _('No'))?>
Expand All @@ -150,6 +150,7 @@ _(Disable Autostart/Start option for VMs)_:
<?if ($libvirt_up):?>
<?$libvirt_info = libvirt_version('libvirt')?>
<?$qemu_info = $lv->get_connect_information()?>

_(Libvirt version)_:
: <?=$libvirt_info['libvirt.major'].'.'.$libvirt_info['libvirt.minor'].'.'.$libvirt_info['libvirt.release']?>

Expand All @@ -161,14 +162,15 @@ _(Libvirt storage location)_:

:vms_libvirt_volume_help:

<?else: /* Libvirt is stopped */ ?>
_(Libvirt vdisk size)_ (_(GB)_):
<?else : /* Libvirt is stopped */ ?>

_(Libvirt vdisk size)_ (_(GB)_):
: <input type="number" id="IMAGE_SIZE" name="IMAGE_SIZE" min="1" value="<?=htmlspecialchars($domain_cfg['IMAGE_SIZE']);?>" required="required" /><span id="SIZE_ERROR" class="errortext"></span>

:vms_libvirt_vdisk_size_help:

_(Libvirt storage location)_:
: <input type="text" id="IMAGE_FILE" name="IMAGE_FILE" autocomplete="off" spellcheck="false" value="<?=htmlspecialchars($domain_cfg['IMAGE_FILE']);?>" placeholder="e.g. /mnt/user/system/libvirt/libvirt.img" data-pickcloseonfile="true" data-pickfilter="img" data-pickroot="/mnt" data-pickfolders="true" required pattern="^[^\\]*libvirt\.img$">
: <input type="text" id="IMAGE_FILE" name="IMAGE_FILE" autocomplete="off" spellcheck="false" value="<?=htmlspecialchars($domain_cfg['IMAGE_FILE']);?>" placeholder="e.g. /mnt/user/system/libvirt/libvirt.img" data-pickcloseonfile="true" data-pickfilter="img" data-pickroot="/mnt" data-pickfolders="true" >
<?if (file_exists($domain_cfg['IMAGE_FILE'])):?>
<span id="deletePanel"><label><input type="checkbox" id="deleteCheckbox" /> _(Delete Image File)_</label></span><?endif;?>
<?if (!$started):?>
Expand All @@ -179,6 +181,7 @@ _(Libvirt storage location)_:
:vms_libvirt_location_help:

<?endif;?>

_(Default VM storage path)_:
: <input type="text" id="domaindir" name="DOMAINDIR" autocomplete="off" spellcheck="false" data-pickfolders="true" data-pickfilter="HIDE_FILES_FILTER" data-pickroot="/mnt" value="<?=htmlspecialchars($domain_cfg['DOMAINDIR'])?>" placeholder="_(Click to Select)_" pattern="^[^\\]*/$" onchange="validatePath(this)">
<?if (!$started):?>
Expand Down Expand Up @@ -299,7 +302,7 @@ _(VFIO allow unsafe interrupts)_:

<?endif;?>

<?if ($libvirt_up && trim(shell_exec('stat -c %T -f /etc/libvirt'))=='btrfs'):?>
<?if ($libvirt_up && trim(shell_exec('stat -c %T -f /etc/libvirt'))=='btrfs' && strpos($domain_cfg['IMAGE_FILE'],".img")) :?>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix imprecise string matching for .img check.

The condition strpos($domain_cfg['IMAGE_FILE'],".img") has potential issues:

  1. It will return false when ".img" is at position 0, though this is unlikely for a path
  2. More importantly, it will match ".img" appearing anywhere in the path, including in directory names (e.g., /mnt/user/my.images/libvirt/ or /path/.img-backup/libvirt/)

Since the intent is to show this section only for image files (not directories), consider using a more precise check:

-<?if ($libvirt_up && trim(shell_exec('stat -c %T -f /etc/libvirt'))=='btrfs' && strpos($domain_cfg['IMAGE_FILE'],".img")) :?>
+<?if ($libvirt_up && trim(shell_exec('stat -c %T -f /etc/libvirt'))=='btrfs' && substr($domain_cfg['IMAGE_FILE'], -4) === '.img') :?>

This checks if the path ends with .img rather than containing it anywhere.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<?if ($libvirt_up && trim(shell_exec('stat -c %T -f /etc/libvirt'))=='btrfs' && strpos($domain_cfg['IMAGE_FILE'],".img")) :?>
<?if ($libvirt_up && trim(shell_exec('stat -c %T -f /etc/libvirt'))=='btrfs' && substr($domain_cfg['IMAGE_FILE'], -4) === '.img') :?>
🤖 Prompt for AI Agents
In emhttp/plugins/dynamix.vm.manager/VMSettings.page around line 303, the
current check strpos($domain_cfg['IMAGE_FILE'],".img") is imprecise (matches
anywhere and fails when at position 0); replace it with an end-of-string match
to ensure the path is an image file — for example, use a case-insensitive regex
or a suffix check such as preg_match('/\.img$/i',
trim($domain_cfg['IMAGE_FILE'])) or
strtolower(substr(trim($domain_cfg['IMAGE_FILE']), -4)) === '.img' so only paths
that actually end with ".img" trigger the condition.

<div class="advanced" markdown="1">
<div class="title"><span class="left"><i class="title fa fa-list"></i>_(Libvirt volume info)_</span></div>
_(btrfs filesystem show)_:
Expand Down
99 changes: 99 additions & 0 deletions emhttp/plugins/dynamix.vm.manager/include/fs_helpers.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php
/* Filesystem helper utilities for dynamix.vm.manager
* - files_identical($a,$b)
* - copy_if_different($src,$dst, $dry_run=false)
* - dir_copy($src,$dst) (recursive, skips identical files)
* - dir_remove($dir) (recursive remove)
*/

function files_identical($a, $b) {
if (!file_exists($a) || !file_exists($b)) return false;
if (filesize($a) !== filesize($b)) return false;
$ha = @md5_file($a);
$hb = @md5_file($b);
if ($ha === false || $hb === false) return false;
return $ha === $hb;
}

function copy_if_different($src, $dst, $dry_run = false) {
$result = [
'src' => $src,
'dst' => $dst,
'would_copy' => false,
'copied' => false,
'error' => null
];

if (!file_exists($src)) {
$result['error'] = 'source not found';
return $result;
}

$dst_dir = dirname($dst);
if (!is_dir($dst_dir)) {
if ($dry_run) {
$result['would_copy'] = true;
return $result;
}
if (!@mkdir($dst_dir, 0755, true)) {
$result['error'] = 'failed to create dest dir';
return $result;
}
}

if (file_exists($dst)) {
if (files_identical($src, $dst)) {
return $result; // identical, nothing to do
}
$result['would_copy'] = true;
} else {
$result['would_copy'] = true;
}

if ($dry_run) return $result;

if (@copy($src, $dst)) {
$result['copied'] = true;
} else {
$result['error'] = 'copy_failed';
}

return $result;
}

function dir_copy($src, $dst) {
if (!is_dir($src)) return false;
if (!is_dir($dst)) {
if (!@mkdir($dst, 0755, true)) return false;
}
$items = scandir($src);
foreach ($items as $item) {
if ($item === '.' || $item === '..') continue;
$s = $src . DIRECTORY_SEPARATOR . $item;
$d = $dst . DIRECTORY_SEPARATOR . $item;
if (is_dir($s)) {
if (!dir_copy($s, $d)) return false;
} else {
if (file_exists($d)) {
if (files_identical($s, $d)) continue;
}
if (!@copy($s, $d)) return false;
}
}
return true;
}

function dir_remove($dir) {
if (!is_dir($dir)) return false;
$items = scandir($dir);
foreach ($items as $item) {
if ($item === '.' || $item === '..') continue;
$path = $dir . DIRECTORY_SEPARATOR . $item;
if (is_dir($path)) {
dir_remove($path);
} else {
@unlink($path);
}
}
return @rmdir($dir);
}
Comment on lines +86 to +99
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Recursive removal doesn't propagate child failure status.

When dir_remove($path) fails on a nested directory (line 93), the return value is ignored and iteration continues. This could leave a partially deleted directory while returning true from parent calls if only the final rmdir succeeds.

Suggested fix to propagate failures
 function dir_remove($dir) {
     if (!is_dir($dir)) return false;
     $items = scandir($dir);
+    if ($items === false) return false;
     foreach ($items as $item) {
         if ($item === '.' || $item === '..') continue;
         $path = $dir . DIRECTORY_SEPARATOR . $item;
         if (is_dir($path)) {
-            dir_remove($path);
+            if (!dir_remove($path)) return false;
         } else {
-            @unlink($path);
+            if (!@unlink($path)) return false;
         }
     }
     return @rmdir($dir);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function dir_remove($dir) {
if (!is_dir($dir)) return false;
$items = scandir($dir);
foreach ($items as $item) {
if ($item === '.' || $item === '..') continue;
$path = $dir . DIRECTORY_SEPARATOR . $item;
if (is_dir($path)) {
dir_remove($path);
} else {
@unlink($path);
}
}
return @rmdir($dir);
}
function dir_remove($dir) {
if (!is_dir($dir)) return false;
$items = scandir($dir);
if ($items === false) return false;
foreach ($items as $item) {
if ($item === '.' || $item === '..') continue;
$path = $dir . DIRECTORY_SEPARATOR . $item;
if (is_dir($path)) {
if (!dir_remove($path)) return false;
} else {
if (!@unlink($path)) return false;
}
}
return @rmdir($dir);
}
🤖 Prompt for AI Agents
In @emhttp/plugins/dynamix.vm.manager/include/fs_helpers.php around lines 86 -
99, The dir_remove function currently ignores the return value of recursive
calls and unlink operations, which can cause parent calls to report success even
if child deletions failed; update dir_remove to check the return value of
dir_remove($path) for directories and the result of @unlink($path) for files,
and if any child deletion returns false immediately return false (or otherwise
propagate failure) so that failures bubble up instead of being silenced,
ensuring the final return from dir_remove reflects any nested failure rather
than only the final rmdir result.

62 changes: 62 additions & 0 deletions emhttp/plugins/dynamix.vm.manager/scripts/libvirt_init
Original file line number Diff line number Diff line change
Expand Up @@ -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#\"}"
Comment on lines +12 to +19
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Potential command injection via eval on config file.

Using eval to parse config values is risky if domain.cfg is writable by untrusted processes or contains malformed content. A safer approach uses direct parsing without shell interpretation.

Safer alternative using grep/sed
-# Read values from domain.cfg
-eval $(grep -E '^(IMAGE_FILE|OLD_IMAGE_FILE)=' "$DOMAIN_CFG")
+# Read values from domain.cfg safely (no eval)
+IMAGE_FILE=$(grep -E '^IMAGE_FILE=' "$DOMAIN_CFG" | head -1 | cut -d= -f2-)
+OLD_IMAGE_FILE=$(grep -E '^OLD_IMAGE_FILE=' "$DOMAIN_CFG" | head -1 | cut -d= -f2-)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# 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#\"}"
# Read values from domain.cfg safely (no eval)
IMAGE_FILE=$(grep -E '^IMAGE_FILE=' "$DOMAIN_CFG" | head -1 | cut -d= -f2-)
OLD_IMAGE_FILE=$(grep -E '^OLD_IMAGE_FILE=' "$DOMAIN_CFG" | head -1 | cut -d= -f2-)
# Remove quotes
IMAGE_FILE="${IMAGE_FILE%\"}"
IMAGE_FILE="${IMAGE_FILE#\"}"
OLD_IMAGE_FILE="${OLD_IMAGE_FILE%\"}"
OLD_IMAGE_FILE="${OLD_IMAGE_FILE#\"}"
🤖 Prompt for AI Agents
In @emhttp/plugins/dynamix.vm.manager/scripts/libvirt_init around lines 12 - 19,
The current use of eval to read IMAGE_FILE and OLD_IMAGE_FILE from DOMAIN_CFG is
unsafe and allows command injection; instead, parse DOMAIN_CFG without shell
evaluation by extracting the values for the keys IMAGE_FILE and OLD_IMAGE_FILE
(e.g., using grep/sed/awk to capture the RHS only), assign those captured
strings to IMAGE_FILE and OLD_IMAGE_FILE, and then strip surrounding quotes as
you already do; update the code around the IMAGE_FILE, OLD_IMAGE_FILE, and
DOMAIN_CFG handling to remove eval and use direct parsing to safely set those
variables.


# 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"
Comment on lines +37 to +44
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Mount operations lack error checking; failure leaves stale mount point.

If mount fails on line 41, the script continues to rsync from an empty mount point. Similarly, if the mount point already has something mounted, this could cause data corruption.

Suggested hardening with error checks
       log "Mounting $OLD_IMAGE_FILE to $TMP_MNT"
       mkdir -p "$TMP_MNT"
-      mount "$OLD_IMAGE_FILE" "$TMP_MNT"
+      if ! mount "$OLD_IMAGE_FILE" "$TMP_MNT"; then
+        log "ERROR: Failed to mount $OLD_IMAGE_FILE"
+        rm -rf "$TMP_MNT"
+        exit 1
+      fi
       log "Copying full contents from image to directory $IMAGE_FILE"
-      rsync -a --exclude="$OLD_IMG_FILE_NAME" "$TMP_MNT/" "$IMAGE_FILE/"
-      umount "$TMP_MNT"
+      if ! rsync -a --exclude="$OLD_IMG_FILE_NAME" "$TMP_MNT/" "$IMAGE_FILE/"; then
+        log "WARNING: rsync encountered errors"
+      fi
+      umount "$TMP_MNT" || log "WARNING: Failed to unmount $TMP_MNT"
+      rmdir "$TMP_MNT" 2>/dev/null

Apply similar error handling to the other mount/umount blocks (lines 46-51).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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"
cp -p "$OLD_IMAGE_FILE" "$BACKUP_PATH"
log "Mounting $OLD_IMAGE_FILE to $TMP_MNT"
mkdir -p "$TMP_MNT"
if ! mount "$OLD_IMAGE_FILE" "$TMP_MNT"; then
log "ERROR: Failed to mount $OLD_IMAGE_FILE"
rm -rf "$TMP_MNT"
exit 1
fi
log "Copying full contents from image to directory $IMAGE_FILE"
if ! rsync -a --exclude="$OLD_IMG_FILE_NAME" "$TMP_MNT/" "$IMAGE_FILE/"; then
log "WARNING: rsync encountered errors"
fi
umount "$TMP_MNT" || log "WARNING: Failed to unmount $TMP_MNT"
rmdir "$TMP_MNT" 2>/dev/null
🤖 Prompt for AI Agents
In @emhttp/plugins/dynamix.vm.manager/scripts/libvirt_init around lines 37 - 44,
The mount/rsync block using variables OLD_IMAGE_FILE, TMP_MNT, IMAGE_FILE and
commands mount/rsync/umount must add error checks: verify TMP_MNT is not already
mounted (use mountpoint or check /proc/mounts) before calling mount, check the
exit status of mount and abort with a logged error if it fails, only run rsync
when mount succeeded, and ensure umount is executed in a cleanup path (trap or
conditional) to avoid leaving a stale mount; apply the same pattern to the other
mount/umount blocks (the one around lines 46-51) and include clear error logs
mentioning the relevant variables (OLD_IMAGE_FILE, TMP_MNT, IMAGE_FILE) so
failures are visible.

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"
Expand Down Expand Up @@ -36,3 +94,7 @@ if [ -s /var/log/vfio-pci-errors ]; then
echo "vfio-pci bind error" > /run/libvirt/qemu/autostarted
/usr/local/emhttp/webGui/scripts/notify -e "VM Autostart disabled" -s "vfio-pci-errors " -d "VM Autostart disabled due to vfio-bind error" -m "Please review /var/log/vfio-pci-errors" -i "alert" -l "/VMs"
fi

# Copy XML from VM Directories to QEMU directory/
/usr/local/emhttp/plugins/dynamix.vm.manager/scripts/libvirtrestore
#
3 changes: 2 additions & 1 deletion emhttp/plugins/dynamix.vm.manager/scripts/libvirtconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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/",
Expand Down
Loading
Loading