Merge remote-tracking branch 'upstream/master' into Add-Clone-VM-option-

This commit is contained in:
SimonFair
2023-06-04 17:10:58 +01:00
715 changed files with 11211 additions and 4069 deletions
+10 -1
View File
@@ -49,5 +49,14 @@ Temporary Items
# Sublime text SFTP configuration file
sftp-config.json
# =========================
# Exclude these dirs commonly found in /usr/local
bin/
games/
info/
lib64/
man/
share/
# Auto-generated when emhttpd/webGUI start
languages/en_US/helptext.dot
emhttp/languages/en_US/helptext.dot
View File
View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

@@ -489,7 +489,7 @@ If no suffix, a count of 1024-byte blocks is assumed.
:share_edit_global1_help:
A *Share*, also called a *User Share*, is simply the name of a top-level directory that exists on one or more of your
storage devices (array and cache). Each share can be exported for network access. When browsing a share, we return the
storage volumes (array disks and pools). Each share can be exported for network access. When browsing a share, we return the
composite view of all files and subdirectories for which that top-level directory exists on each storage device.
*Read settings from* is used to preset the settings of the new share with the settings of an existing share.
@@ -607,9 +607,7 @@ disable these services while mover is running.
:share_edit_copy_on_write_help:
Set to **No** to cause the *btrfs* NOCOW (No Copy-on-Write) attribute to be set on the share directory
when created on a device formatted with *btrfs* file system. Once set, newly created files and
subdirectories on the device will inherit the NOCOW attribute. We recommend this setting for shares
used to store vdisk images, including the Docker loopback image file. This setting has no effect
on non-btrfs file systems.
subdirectories on the device will inherit the NOCOW attribute. This setting has no effect on non-btrfs file systems.
Set to **Auto** for normal operation, meaning COW **will** be in effect on devices formatted with *btrfs*.
:end
@@ -622,6 +620,32 @@ Share does *not* contain any data and may be deleted if not needed any longer.
Share can *not* be deleted as long as it contains data. Be aware that some data can be hidden. See also [SMB Settings](/Settings/SMB) -> Hide "dot" files.
:end
:share_edit_exclusive_access_help:
When set to "Yes" indicates a synmlink directly to a pool has been set up for the share in the /mnt/user tree.
Refer to [Global Share Settings](Settings/ShareSettings) -> Permit exclusive shares.
:end
:share_edit_primary_storage_help:
**Primary storage** is where *new files and folders* are created. If
Primary storage is below the minimum free space setting then new files
and folders will be created in **Secondary storage**, if configured.
**Important:** For *Exclusive access* shares, the Min free space
settings are ignored.
:end
:share_edit_secondary_storage_help:
**Secondary storage** is where new files and directories are created if no
room on Primary storage. When both Primary and Secondary storage are
configured the 'mover' will transfer files between them.
:end
:share_edit_mover_action_help:
This defines the direction of file transfer between Primary and
Secondary storage when both are configured.
:end
:smb_security_help:
*Read settings from* is used to preset the SMB security settings of the current selected share with the settings of an existing share.
@@ -1420,6 +1444,19 @@ Uncheck all disks in order to not exclude any disks
disks which represent a subset of the Included/Excluded disks defined here.
:end
:shares_exclusive_shares_help:
If set to Yes, share directories under /mnt/user are actually symlinks to the share directory on a storage volume
provided the following conditions are met:
* The Primary storage for a share is set to a pool.
* The Secondary storage for a share is set to **none**.
* The share exists on a single volume.
* The share is **not** exported over NFS.
The advantage of *exclusive* shares is that transfers bypass the FUSE layer which may significantly
increase I/O performance.
:end
:shares_tunable_hard_links_help:
If set to Yes then support the link() operation.
@@ -1602,6 +1639,11 @@ When shutting down the server, this defines how long to wait in seconds for *gra
NOTE: It's recommended to shut down guest VMs from within the VM.
:end
:vms_console_help:
For setting the console options to show on context menus. Web will show only inbuild web clients(VNC and SPICE),
Virtual Manager Remote Viewer will only show the Remote Viewer option. Both will show both Web and Remote Viewer.
:end
:vms_acs_override_help:
*PCIe ACS override* allows various hardware components to expose themselves as isolated devices.
Typically it is sufficient to isolate *Downstream* ports.
@@ -1814,6 +1856,18 @@ Select the gateway from the dropdown list or enter a valid IPv4/IPv6 address as
The metric value is optional, it defaults to 1. Use it to select the preferred gateway when more than one entry of the same route exists.
:end
:eth_network_extra_include_help:
Enter one or more interface names or IP addresses which will be included in the list of listening interfaces for local system services.
This is particularly useful when you have created custom interfaces (e.g. tailscale VPN tunnel) which are used to access local system services.
:end
:eth_network_extra_exclude_help:
Enter one or more interface names or IP addresses which will be excluded from the list of listening interfaces for local system services.
This can be used to exclude dedicated local interfaces (e.g. p-t-p connections) or exclude dynamic interfaces (WireGuard tunnels) from using local system services.
:end
:apc_ups_daemon_help:
Set to 'Yes' to enable apcupsd and start the daemon, set to 'No' to disable apcupsd and stop the daemon.
:end
@@ -2229,11 +2283,11 @@ Provide a comma separated list of urls that are allowed to access the unraid-api
:end
:myservers_remote_t2fa_help:
When Transparent 2FA for Remote Access is enabled, you will access your server by clicking the "Remote Access" link on the <a href="https://forums.unraid.net/my-servers/" target="_blank" rel="noopener noreferrer">My Servers dashboard</a>. The system will transparently request a 2FA token from your server and embed it in the server's login form. Your server will deny any Remote Access login attempt that does not include a valid token. Each token can be used only once, and is only valid for five minutes.
When Transparent 2FA for Remote Access is enabled, you will access your server by clicking the "Remote Access" link on the <a href="https://connect.myunraid.net/" target="_blank" rel="noopener noreferrer">Go to Connect</a>. The system will transparently request a 2FA token from your server and embed it in the server's login form. Your server will deny any Remote Access login attempt that does not include a valid token. Each token can be used only once, and is only valid for five minutes.
:end
:myservers_local_t2fa_help:
When Transparent 2FA for Local Access is enabled, you will access your server by clicking the "Local Access" link on the <a href="https://forums.unraid.net/my-servers/" target="_blank" rel="noopener noreferrer">My Servers dashboard</a>. The system will transparently request a 2FA token from your server and embed it in the server's login form. Your server will deny any Local Access login attempt that does not include a valid token. Each token can be used only once, and is only valid for five minutes.
When Transparent 2FA for Local Access is enabled, you will access your server by clicking the "Local Access" link on the <a href="https://connect.myunraid.net/" target="_blank" rel="noopener noreferrer">Go to Connect</a>. The system will transparently request a 2FA token from your server and embed it in the server's login form. Your server will deny any Local Access login attempt that does not include a valid token. Each token can be used only once, and is only valid for five minutes.
This is fairly extreme for Local Access, and in most cases is not needed. It requires a solid Internet connection. If you need to access the webGUI while the Internet is down, SSH to the server and run 'use_ssl no', this will give you access via http://<?=$nginx['NGINX_LANIP']?><?=$nginx['NGINX_PORT'] !== '80' ? ':'.$nginx['NGINX_PORT'] : '' ?>
:end
View File
+1 -1
View File
@@ -78,7 +78,7 @@ function addLog(logLine) {
}
function addCloseButton() {
var done = location.search.split('&').pop().split('=')[1];
addLog("<p class='centered'><button class='logLine' type='button' onclick='" + (top.Shadowbox ? "top.Shadowbox" : "window") + ".close()'>"+decodeURI(done)+"</button></p>");
addLog("<p class='centered'><button class='logLine' type='button' onclick='" + (parent.Shadowbox ? "parent.Shadowbox" : "window") + ".close()'>"+decodeURI(done)+"</button></p>");
}
</script>
</head>
View File
View File
@@ -1,6 +1,7 @@
Menu="Tasks:60"
Type="xmenu"
Code="e90b"
Lock="true"
Cond="exec(\"grep -o '^DOCKER_ENABLED=.yes' /boot/config/docker.cfg 2>/dev/null\")"
---
<?PHP
@@ -40,9 +40,11 @@ th.nine{width:9%}
th.load{width:140px}
input.wait{width:24px;margin:0 4px;padding:0 5px;border:none;box-shadow:none;background-color:transparent}
table tbody td{line-height:normal}
i.mover{margin-right:8px;display:none}
#resetsort{margin-left:12px;display:inline-block;width:32px}
</style>
<table id="docker_containers" class="tablesorter shift">
<thead><tr><th><a href="#" style="cursor:hand;margin-left:12px;display:inline-block;width:32px" onclick="resetSorting()" title="_(Reset sorting)_"><i class="fa fa-th-list"></i></a>_(Application)_</th><th>_(Version)_</th><th>_(Network)_</th><th>_(Port Mappings)_ <small>(_(App to Host)_)</small></th><th>_(Volume Mappings)_ <small>(_(App to Host)_)</small></th><th class="load advanced">_(CPU & Memory load)_</th><th class="nine">_(Autostart)_</th><th class="five">_(Uptime)_</th></tr></thead>
<thead><tr><th><a id="resetsort" class="nohand" onclick="resetSorting()" title="_(Reset sorting)_"><i class="fa fa-th-list"></i></a>_(Application)_</th><th>_(Version)_</th><th>_(Network)_</th><th>_(Port Mappings)_ <small>(_(App to Host)_)</small></th><th>_(Volume Mappings)_ <small>(_(App to Host)_)</small></th><th class="load advanced">_(CPU & Memory load)_</th><th class="nine">_(Autostart)_</th><th class="five">_(Uptime)_</th></tr></thead>
<tbody id="docker_list"><tr><td colspan='8'></td></tr></tbody>
</table>
<input type="button" onclick="addContainer()" value="_(Add Container)_" style="display:none">
@@ -64,18 +66,17 @@ $('.title').append("<span id='busy' class='red-text strong' style='display:none;
<?else:?>
$('.tabs').append("<span id='busy' class='red-text strong' style='display:none;position:relative;top:<?=$top?>px;left:40px;font-size:1.4rem;letter-spacing:2px'><?=$busy?></span>");
<?endif;?>
<?if ($display['resize']):?>
<?if (_var($display,'resize')):?>
function resize() {
var width = [];
$('#docker_list').height(Math.max(window.innerHeight-340,330));
$('#docker_containers thead,#docker_containers tbody').removeClass('fixed');
$('#docker_containers tbody tr:first-child td').each(function(){width.push($(this).width());});
$('#docker_containers thead tr th').each(function(i){$(this).width(width[i]);});
$('#docker_containers tbody tr td').each(function(i){$(this).width(width[i]);});
$('#docker_containers thead tr th').each(function(){$(this).width($(this).width());});
$('#docker_containers tbody tr td').each(function(){$(this).width($(this).width());});
$('#docker_containers thead,#docker_containers tbody').addClass('fixed');
}
<?endif;?>
function resetSorting() {
if ($.cookie('lockbutton')==null) return;
$('input[type=button]').prop('disabled',true);
$.post('/plugins/dynamix.docker.manager/include/UserPrefs.php',{reset:true},function(){loadlist();});
}
@@ -90,19 +91,18 @@ function listview() {
if (auto && more) wait.show(); else wait.hide();
});
}
var sortableHelper = function(e,ui){
ui.children().each(function(){$(this).width($(this).width());});
return ui;
};
function LockButton() {
if ($.cookie('lockbutton') == null) {
if ($.cookie('lockbutton')==null) {
$.cookie('lockbutton','lockbutton');
$('#resetsort').removeClass('nohand').addClass('hand');
$('i.mover').show();
$('#docker_list .sortable').css({'cursor':'move'});
<?if ($themes1):?>
$('div.nav-item.LockButton').find('a').prop('title',"_(Lock sortable items)_");
$('div.nav-item.LockButton').find('b').removeClass('icon-u-lock red-text').addClass('icon-u-lock-open green-text');
$('div.nav-item.LockButton').find('b').removeClass('icon-u-lock green-text').addClass('icon-u-lock-open red-text');
<?endif;?>
$('div.nav-item.LockButton').find('span').text("_(Lock sortable items)_");
$('#docker_list').sortable({helper:sortableHelper,items:'.sortable',cursor:'grab',axis:'y',containment:'parent',cancel:'span.docker_readmore,input',delay:100,opacity:0.5,zIndex:9999,forcePlaceholderSize:true,
$('#docker_list').sortable({helper:'clone',items:'.sortable',cursor:'grab',axis:'y',containment:'parent',cancel:'span.docker_readmore,input',delay:100,opacity:0.5,zIndex:9999,forcePlaceholderSize:true,
update:function(e,ui){
var row = $('#docker_list').find('tr:first');
var names = ''; var index = '';
@@ -111,9 +111,12 @@ function LockButton() {
}});
} else {
$.removeCookie('lockbutton');
$('#resetsort').removeClass('hand').addClass('nohand');
$('i.mover').hide();
$('#docker_list .sortable').css({'cursor':'default'});
<?if ($themes1):?>
$('div.nav-item.LockButton').find('a').prop('title',"_(Unlock sortable items)_");
$('div.nav-item.LockButton').find('b').removeClass('icon-u-lock-open green-text').addClass('icon-u-lock red-text');
$('div.nav-item.LockButton').find('b').removeClass('icon-u-lock-open red-text').addClass('icon-u-lock green-text');
<?endif;?>
$('div.nav-item.LockButton').find('span').text("_(Unlock sortable items)_");
$('#docker_list').sortable('destroy');
@@ -126,7 +129,7 @@ function loadlist(init) {
var data = d.split(/\0/);
$('#docker_list').html(data[0]);
$('head').append('<script>'+data[1]+'<\/script>');
<?if ($display['resize']):?>
<?if (_var($display,'resize')):?>
resize();
if (init) $(window).bind('resize',function(){resize();});
<?endif;?>
@@ -145,7 +148,6 @@ function loadlist(init) {
$.post('/plugins/dynamix.docker.manager/include/UpdateConfig.php',{action:'wait',container:$(this).attr('container'),wait:$(this).val()});
});
if ($.cookie('docker_listview_mode')=='advanced') {$('.advanced').show(); $('.basic').hide();}
context.init({preventDoubleContext:false,left:true,above:false});
$('input[type=button]').prop('disabled',false).show('slow');
var update = false, rebuild = false;
for (var i=0,ct; ct=docker[i]; i++) {
@@ -170,7 +172,7 @@ dockerload.on('message', function(msg){
for (var i=0,row; row=data[i]; i++) {
var id = row.split(';');
var w1 = Math.round(Math.min(id[1].slice(0,-1)/<?=count($cpus)*count(preg_split('/[,-]/',$cpus[0]))?>,100)*100)/100+'%';
$('.cpu-'+id[0]).text(w1.replace('.','<?=$display['number'][0]?>'));
$('.cpu-'+id[0]).text(w1.replace('.','<?=_var($display,'number','.,')[0]?>'));
$('.mem-'+id[0]).text(id[2]);
$('#cpu-'+id[0]).css('width',w1);
}
@@ -21,7 +21,7 @@ Tag="icon-docker"
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
$DockerClient = new DockerClient();
$DockerStopped = pgrep('dockerd')===false;
exec("/etc/rc.d/rc.docker status >/dev/null",$dummy,$DockerStopped);
function strposX($s, $c, $n=1) {
$p = 0;
@@ -58,9 +58,9 @@ foreach ($custom as $network) {
[$eth,$vlan] = my_explode('.',$network);
$eth = str_replace(['bond','br'],'eth',$eth);
if (!$vlan) {
$protocol[$network] = $$eth['PROTOCOL:0'] ?? 'ipv4';
$protocol[$network] = _var($$eth,'PROTOCOL:0','ipv4');
} else {
foreach ($$eth as $key => $value) if (strpos($key,'VLANID')!==false && $value==$vlan) {$protocol[$network] = $$eth[str_replace('VLANID','PROTOCOL',$key)] ?? 'ipv4'; break;}
foreach ($$eth as $key => $value) if (strpos($key,'VLANID')!==false && $value==$vlan) {$protocol[$network] = _var($$eth,str_replace('VLANID','PROTOCOL',$key),'ipv4'); break;}
}
if ($ip4 && $route4) {
$include[$network] = $route4;
@@ -137,15 +137,15 @@ span.disabled{color:#404040}
<input type="hidden" name="#arg[1]" value="cmdStatus=Apply">
<input type="hidden" name="#cleanup" value="true">
<input type="hidden" name="DOCKER_CUSTOM_NETWORKS" value="<?=implode(' ',$unset)?> ">
<input type="hidden" name="DOCKER_IMAGE_FILE" value="<?=$dockercfg['DOCKER_IMAGE_FILE']??''?>">
<input type="hidden" name="DOCKER_IMAGE_FILE" value="<?=_var($dockercfg,'DOCKER_IMAGE_FILE')?>">
_(Enable Docker)_:
: <select id="DOCKER_ENABLED" name="DOCKER_ENABLED">
<?=mk_option($dockercfg['DOCKER_ENABLED']??'', 'no', _('No'))?>
<?=mk_option($dockercfg['DOCKER_ENABLED']??'', 'yes', _('Yes'))?>
<?=mk_option(_var($dockercfg,'DOCKER_ENABLED'), 'no', _('No'))?>
<?=mk_option(_var($dockercfg,'DOCKER_ENABLED'), 'yes', _('Yes'))?>
</select>
<?if ($var['fsState'] != "Started"):?>
<span id="arraystopped"><i class="fa fa-warning icon warning"></i> <?=($dockercfg['DOCKER_ENABLED']=='yes') ? '_(Docker will be available after Array is Started)_' : '_(Apply to activate Docker after Array is Started)_'?></span>
<?elseif (!is_dir(dirname($dockercfg['DOCKER_IMAGE_FILE']??'')) || !is_dir($dockercfg['DOCKER_APP_CONFIG_PATH']??'')):?>
<?if (_var($var,'fsState')!="Started"):?>
<span id="arraystopped"><i class="fa fa-warning icon warning"></i> <?=(_var($dockercfg,'DOCKER_ENABLED')=='yes') ? '_(Docker will be available after Array is Started)_' : '_(Apply to activate Docker after Array is Started)_'?></span>
<?elseif (!is_dir(dirname(_var($dockercfg,'DOCKER_IMAGE_FILE'))) || !is_dir(_var($dockercfg,'DOCKER_APP_CONFIG_PATH'))):?>
<span class="basic"><i class="fa fa-warning icon warning"></i> _(One or more paths do not exist)_ (<a href="#" onclick="$('.advancedview').switchButton('option','checked',true); return false">_(view)_</a>)</span>
<?endif;?>
@@ -154,15 +154,15 @@ _(Enable Docker)_:
_(Enable container table readmore-js)_:
: <select id="DOCKER_READMORE" name="DOCKER_READMORE">
<?=mk_option($dockercfg['DOCKER_READMORE']??'', 'yes', _('Yes'))?>
<?=mk_option($dockercfg['DOCKER_READMORE']??'', 'no', _('No'))?>
<?=mk_option(_var($dockercfg,'DOCKER_READMORE'), 'yes', _('Yes'))?>
<?=mk_option(_var($dockercfg,'DOCKER_READMORE'), 'no', _('No'))?>
</select>
:docker_readmore_help:
_(Docker Stop Timeout)_:
: <input class='narrow' id="DOCKER_TIMEOUT" type="number" name="DOCKER_TIMEOUT" min='1' value="<?=$dockercfg['DOCKER_TIMEOUT']??''?>">_(seconds)_
: <input class='narrow' id="DOCKER_TIMEOUT" type="number" name="DOCKER_TIMEOUT" min='1' value="<?=_var($dockercfg,'DOCKER_TIMEOUT')?>">_(seconds)_
:docker_timeout_help:
@@ -170,24 +170,24 @@ _(Docker Stop Timeout)_:
_(Docker data-root)_:
: <select id="DOCKER_IMAGE_TYPE" name="DOCKER_IMAGE_TYPE" onchange="updateLocation(this.value)">
<?=mk_option($dockercfg['DOCKER_IMAGE_TYPE']??'', '', _('btrfs vDisk'))?>
<?=mk_option($dockercfg['DOCKER_IMAGE_TYPE']??'', 'xfs', _('xfs vDisk'))?>
<?=mk_option($dockercfg['DOCKER_IMAGE_TYPE']??'', 'folder', _('directory'))?>
<?=mk_option(_var($dockercfg,'DOCKER_IMAGE_TYPE'), '', _('btrfs vDisk'))?>
<?=mk_option(_var($dockercfg,'DOCKER_IMAGE_TYPE'), 'xfs', _('xfs vDisk'))?>
<?=mk_option(_var($dockercfg,'DOCKER_IMAGE_TYPE'), 'folder', _('directory'))?>
</select>
:docker_vdisk_type_help:
<div markdown="1" id="vdisk_file" style="display:none">
_(Docker vDisk size)_:
: <input id="DOCKER_IMAGE_SIZE" type="number" name="DOCKER_IMAGE_SIZE" value="<?=$dockercfg['DOCKER_IMAGE_SIZE']??''?>" class="narrow" required>GB<span id="SIZE_ERROR" class="errortext"></span>
: <input id="DOCKER_IMAGE_SIZE" type="number" name="DOCKER_IMAGE_SIZE" value="<?=_var($dockercfg,'DOCKER_IMAGE_SIZE')?>" class="narrow" required>GB<span id="SIZE_ERROR" class="errortext"></span>
:docker_vdisk_size_help:
_(Docker vDisk location)_:
: <input type="text" id="DOCKER_IMAGE_FILE1" name="DOCKER_IMAGE_FILE1" autocomplete="off" spellcheck="false" value="<?=$dockercfg['DOCKER_IMAGE_FILE']??''?>" placeholder="_(e.g.)_ /mnt/user/system/docker.img" data-pickcloseonfile="true" data-pickfilter="img" data-pickroot="/mnt" data-pickfolders="true" disabled required pattern="^[^\\]*(docker-xfs\.img|docker\.img)$">
: <input type="text" id="DOCKER_IMAGE_FILE1" name="DOCKER_IMAGE_FILE1" autocomplete="off" spellcheck="false" value="<?=_var($dockercfg,'DOCKER_IMAGE_FILE')?>" placeholder="_(e.g.)_ /mnt/user/system/docker.img" data-pickcloseonfile="true" data-pickfilter="img" data-pickroot="/mnt" data-pickfolders="true" disabled required pattern="^[^\\]*(docker-xfs\.img|docker\.img)$">
<span class="deleteLabel"><label><input type="checkbox" class="deleteCheckbox"> _(Delete vDisk file)_</label></span>
<?if ($var['fsState'] != "Started"):?><span><i class="fa fa-warning icon warning"></i> _(Modify with caution: unable to validate path until Array is Started)_</span>
<?elseif (!is_dir(dirname($dockercfg['DOCKER_IMAGE_FILE']??''))):?><span class="nonexist"><i class="fa fa-warning icon warning"></i> _(Path does not exist)_</span>
<?elseif (!is_dir(dirname(_var($dockercfg,'DOCKER_IMAGE_FILE')))):?><span class="nonexist"><i class="fa fa-warning icon warning"></i> _(Path does not exist)_</span>
<?endif;?><span id="IMAGE_ERROR1" class="errortext"></span>
:docker_vdisk_location_help:
@@ -195,20 +195,20 @@ _(Docker vDisk location)_:
</div>
<div markdown="1" id="vdisk_dir" style="display:none">
_(Docker directory)_:
: <input type="text" id="DOCKER_IMAGE_FILE2" name="DOCKER_IMAGE_FILE2" autocomplete="off" spellcheck="false" value="<?=$dockercfg['DOCKER_IMAGE_FILE']??''?>" placeholder="_(e.g.)_ /mnt/user/system/docker" data-pickcloseonfile="true" data-pickfilter="HIDE_FILES_FILTER" data-pickroot="/mnt" data-pickfolders="true" disabled required pattern="^[^\\]*/$">
: <input type="text" id="DOCKER_IMAGE_FILE2" name="DOCKER_IMAGE_FILE2" autocomplete="off" spellcheck="false" value="<?=_var($dockercfg,'DOCKER_IMAGE_FILE')?>" placeholder="_(e.g.)_ /mnt/user/system/docker" data-pickcloseonfile="true" data-pickfilter="HIDE_FILES_FILTER" data-pickroot="/mnt" data-pickfolders="true" disabled required pattern="^[^\\]*/$">
<span class="deleteLabel"><label><input type="checkbox" class="deleteCheckbox"> _(Delete directory)_</label></span>
<?if ($var['fsState'] != "Started"):?><span><i class="fa fa-warning icon warning"></i> _(Modify with caution: unable to validate path until Array is Started)_</span>
<?elseif (!is_dir(dirname($dockercfg['DOCKER_IMAGE_FILE']??''))):?><span class="nonexist"><i class="fa fa-warning icon warning"></i> _(Path does not exist)_</span>
<?elseif (!is_dir(dirname(_var($dockercfg,'DOCKER_IMAGE_FILE')))):?><span class="nonexist"><i class="fa fa-warning icon warning"></i> _(Path does not exist)_</span>
<?endif;?><span id="IMAGE_ERROR2" class="errortext"></span>
:docker_vdisk_directory_help:
</div>
_(Default appdata storage location)_:
: <input type="text" id="DOCKER_APP_CONFIG_PATH" name="DOCKER_APP_CONFIG_PATH" autocomplete="off" spellcheck="false" value="<?=$dockercfg['DOCKER_APP_CONFIG_PATH']??''?>" placeholder="_(e.g.)_ /mnt/user/appdata/" data-pickfilter="HIDE_FILES_FILTER" data-pickroot="<?=is_dir('/mnt/user')?'/mnt/user':'/mnt'?>" data-pickfolders="true" pattern="^[^\\]*/$">
: <input type="text" id="DOCKER_APP_CONFIG_PATH" name="DOCKER_APP_CONFIG_PATH" autocomplete="off" spellcheck="false" value="<?=_var($dockercfg,'DOCKER_APP_CONFIG_PATH')?>" placeholder="_(e.g.)_ /mnt/user/appdata/" data-pickfilter="HIDE_FILES_FILTER" data-pickroot="<?=is_dir('/mnt/user')?'/mnt/user':'/mnt'?>" data-pickfolders="true" pattern="^[^\\]*/$">
<?if ($var['fsState'] != "Started"):?>
<span><i class="fa fa-warning icon warning"></i> _(Modify with caution: unable to validate path until Array is Started)_</span>
<?elseif (!is_dir($dockercfg['DOCKER_APP_CONFIG_PATH']??'')):?>
<?elseif (!is_dir(_var($dockercfg,'DOCKER_APP_CONFIG_PATH'))):?>
<span class="nonexist"><i class="fa fa-warning icon warning"></i> _(Path does not exist)_</span>
<?endif;?>
@@ -217,8 +217,8 @@ _(Default appdata storage location)_:
<div markdown="1" class="advanced">
_(Docker LOG rotation)_:
: <select name="DOCKER_LOG_ROTATION" onchange="showLogOptions(this.value)">
<?=mk_option($dockercfg['DOCKER_LOG_ROTATION']??'', 'no', _('Disabled'))?>
<?=mk_option($dockercfg['DOCKER_LOG_ROTATION']??'', 'yes', _('Enabled'))?>
<?=mk_option(_var($dockercfg,'DOCKER_LOG_ROTATION'), 'no', _('Disabled'))?>
<?=mk_option(_var($dockercfg,'DOCKER_LOG_ROTATION'), 'yes', _('Enabled'))?>
</select>
:docker_log_rotation_help:
@@ -226,21 +226,21 @@ _(Docker LOG rotation)_:
<div markdown="1" id="DOCKER_LOG_OPTIONS" style="display:none">
_(Docker LOG maximum file size)_:
: <select name="DOCKER_LOG_SIZE">
<?=mk_option($dockercfg['DOCKER_LOG_SIZE']??'', '10m', '10 '._('MB'))?>
<?=mk_option($dockercfg['DOCKER_LOG_SIZE']??'', '20m', '20 '._('MB'))?>
<?=mk_option($dockercfg['DOCKER_LOG_SIZE']??'', '50m', '50 '._('MB'))?>
<?=mk_option($dockercfg['DOCKER_LOG_SIZE']??'', '100m', '100 '._('MB'))?>
<?=mk_option($dockercfg['DOCKER_LOG_SIZE']??'', '500m', '500 '._('MB'))?>
<?=mk_option($dockercfg['DOCKER_LOG_SIZE']??'', '1g', '1000 '._('MB'))?>
<?=mk_option(_var($dockercfg,'DOCKER_LOG_SIZE'), '10m', '10 '._('MB'))?>
<?=mk_option(_var($dockercfg,'DOCKER_LOG_SIZE'), '20m', '20 '._('MB'))?>
<?=mk_option(_var($dockercfg,'DOCKER_LOG_SIZE'), '50m', '50 '._('MB'))?>
<?=mk_option(_var($dockercfg,'DOCKER_LOG_SIZE'), '100m', '100 '._('MB'))?>
<?=mk_option(_var($dockercfg,'DOCKER_LOG_SIZE'), '500m', '500 '._('MB'))?>
<?=mk_option(_var($dockercfg,'DOCKER_LOG_SIZE'), '1g', '1000 '._('MB'))?>
</select>
:docker_log_file_size_help:
_(Docker LOG number of files)_:
: <select name="DOCKER_LOG_FILES">
<?=mk_option($dockercfg['DOCKER_LOG_FILES']??'', '1', '1')?>
<?=mk_option($dockercfg['DOCKER_LOG_FILES']??'', '2', '2')?>
<?=mk_option($dockercfg['DOCKER_LOG_FILES']??'', '3', '3')?>
<?=mk_option(_var($dockercfg,'DOCKER_LOG_FILES'), '1', '1')?>
<?=mk_option(_var($dockercfg,'DOCKER_LOG_FILES'), '2', '2')?>
<?=mk_option(_var($dockercfg,'DOCKER_LOG_FILES'), '3', '3')?>
</select>
:docker_log_file_number_help:
@@ -248,32 +248,32 @@ _(Docker LOG number of files)_:
</div>
_(Template Authoring Mode)_:
: <select id="DOCKER_AUTHORING_MODE" name="DOCKER_AUTHORING_MODE">
<?=mk_option($dockercfg['DOCKER_AUTHORING_MODE']??'', 'no', _('No'))?>
<?=mk_option($dockercfg['DOCKER_AUTHORING_MODE']??'', 'yes', _('Yes'))?>
<?=mk_option(_var($dockercfg,'DOCKER_AUTHORING_MODE'), 'no', _('No'))?>
<?=mk_option(_var($dockercfg,'DOCKER_AUTHORING_MODE'), 'yes', _('Yes'))?>
</select>
:docker_authoring_mode_help:
_(Docker custom network type)_:
: <select name="DOCKER_NETWORK_TYPE">
<?=mk_option($dockercfg['DOCKER_NETWORK_TYPE']??'', '1', _('ipvlan'))?>
<?=mk_option($dockercfg['DOCKER_NETWORK_TYPE']??'', '', _('macvlan'))?>
<?=mk_option(_var($dockercfg,'DOCKER_NETWORK_TYPE'), '1', _('ipvlan'))?>
<?=mk_option(_var($dockercfg,'DOCKER_NETWORK_TYPE'), '', _('macvlan'))?>
</select>&nbsp;_(Please read the Help carefully)_. _(Misconfiguration can cause problems)_.
:docker_custom_network_type_help:
_(Host access to custom networks)_:
: <select name="DOCKER_ALLOW_ACCESS">
<?=mk_option($dockercfg['DOCKER_ALLOW_ACCESS']??'', '', _('Disabled'))?>
<?=mk_option($dockercfg['DOCKER_ALLOW_ACCESS']??'', 'yes', _('Enabled'))?>
<?=mk_option(_var($dockercfg,'DOCKER_ALLOW_ACCESS'), '', _('Disabled'))?>
<?=mk_option(_var($dockercfg,'DOCKER_ALLOW_ACCESS'), 'yes', _('Enabled'))?>
</select>&nbsp;_(Make sure you understand what you are doing before enabling)_.
:docker_custom_network_access_help:
_(Preserve user defined networks)_:
: <select name="DOCKER_USER_NETWORKS">
<?=mk_option($dockercfg['DOCKER_USER_NETWORKS']??'', 'remove', _('No'))?>
<?=mk_option($dockercfg['DOCKER_USER_NETWORKS']??'', 'preserve', _('Yes'))?>
<?=mk_option(_var($dockercfg,'DOCKER_USER_NETWORKS'), 'remove', _('No'))?>
<?=mk_option(_var($dockercfg,'DOCKER_USER_NETWORKS'), 'preserve', _('Yes'))?>
</select>
:docker_user_defined_network_help:
@@ -285,13 +285,13 @@ $docker_auto = "DOCKER_AUTO_$net";
$docker_dhcp = "DOCKER_DHCP_$net";
?>
<input type="hidden" name="<?=$docker_auto?>" value="<?=$dockercfg[$docker_auto]??''?>">
<input type="hidden" name="<?=$docker_auto?>" value="<?=_var($dockercfg,$docker_auto)?>">
_(IPv4 custom network on interface)_ <?=$network?> (_(optional)_):
<?
$auto = ($dockercfg[$docker_auto]??'')!='no';
$auto = _var($dockercfg,$docker_auto)!='no';
$autoDisabled = $auto ? '':'disabled';
$dhcp = $dockercfg[$docker_dhcp] ?? false;
$dhcp = _var($dockercfg,$docker_dhcp);
$dhcpDisabled = ($auto && $dhcp) ? '':'disabled';
$net = base_min($route);
$max = base_max($route);
@@ -339,8 +339,8 @@ _(IPv4 custom network on interface)_ <?=$network?> (_(optional)_):
<?foreach ($unset as $network):?>
<?
$port = normalize($network);
[$subnet,$mask] = my_explode('/',$dockercfg["DOCKER_SUBNET_$port"]??'');
[$range,$size] = my_explode('/',$dockercfg["DOCKER_RANGE_$port"]??'');
[$subnet,$mask] = my_explode('/',_var($dockercfg,"DOCKER_SUBNET_$port"));
[$range,$size] = my_explode('/',_var($dockercfg,"DOCKER_RANGE_$port"));
$disabled = $subnet ? '':'disabled';
$dhcpDisabled = $range ? '':'disabled';
?>
@@ -352,7 +352,7 @@ _(IPv4 custom network on interface)_ <?=$network?> (_(optional)_):
<select id="DOCKER_CUSTOM_<?=$port?>_mask" name="DOCKER_MASK_<?=$port?>" class="mask"<?=$disabled?>>
<?for ($m=16; $m<=30; $m++) echo mk_option($mask?:24,$m,$m)?></select>
</span>
<span class="<?=$gw4class?>">**_(Gateway)_:** <input type="text" id="DOCKER_CUSTOM_<?=$port?>_gw" name="DOCKER_GATEWAY_<?=$port?>" class="ip4" value="<?=$dockercfg["DOCKER_GATEWAY_$port"]??''?>" title="_(IPv4 address A.B.C.D)_"<?=$disabled?>></span>
<span class="<?=$gw4class?>">**_(Gateway)_:** <input type="text" id="DOCKER_CUSTOM_<?=$port?>_gw" name="DOCKER_GATEWAY_<?=$port?>" class="ip4" value="<?=_var($dockercfg,"DOCKER_GATEWAY_$port")?>" title="_(IPv4 address A.B.C.D)_"<?=$disabled?>></span>
<input type="checkbox" id="DOCKER_CUSTOM_<?=$port?>_dhcp" onchange="customDHCP(this.id,4)"<?=$subnet?'checked':''?><?=$dhcpDisabled?>>
**_(DHCP pool)_:** <input type="text" id="DOCKER_CUSTOM_<?=$port?>_pool" name="DOCKER_RANGE_<?=$port?>" class="ip4" value="<?=$range?>" title="_(IPv4 address A.B.C.D)_"<?=$disabled?>>/
<select id="DOCKER_CUSTOM_<?=$port?>_size" name="DOCKER_SIZE_<?=$port?>" class="mask" onchange="changeHosts(this.id,this.value)"<?=$disabled?>>
@@ -377,9 +377,9 @@ $docker_dhcp6 = "DOCKER_DHCP6_$net";
_(IPv6 custom network on interface)_ <?=$network?> (_(optional)_):
<?
$auto6 = ($dockercfg[$docker_auto]??'')!='no';
$auto6 = _var($dockercfg,$docker_auto)!='no';
$auto6Disabled = $auto6 ? '':'disabled';
$dhcp6 = $dockercfg[$docker_dhcp6] ?? false;
$dhcp6 = _var($dockercfg,$docker_dhcp6);
$dhcp6Disabled = ($auto6 && $dhcp6) ? '':'disabled';
$net = base_net($route);
$mask = explode('/',$route)[1];
@@ -407,8 +407,8 @@ _(IPv6 custom network on interface)_ <?=$network?> (_(optional)_):
<?foreach ($unset as $network):?>
<?
$port = normalize($network);
[$subnet6,$mask6] = my_explode('/',$dockercfg["DOCKER_SUBNET6_$port"]??'');
[$range6,$size6] = my_explode('/',$dockercfg["DOCKER_RANGE6_$port"]??'');
[$subnet6,$mask6] = my_explode('/',_var($dockercfg,"DOCKER_SUBNET6_$port"));
[$range6,$size6] = my_explode('/',_var($dockercfg,"DOCKER_RANGE6_$port"));
$disabled = $subnet6 ? '':'disabled';
$dhcpDisabled = $range6 ? '':'disabled';
?>
@@ -419,7 +419,7 @@ _(IPv6 custom network on interface)_ <?=$network?> (_(optional)_):
<span class="ip6">**_(Subnet)_:**<input type="text" id="DOCKER_CUSTOM6_<?=$port?>_net" name="DOCKER_SUBNET6_<?=$port?>" class="ip6" value="<?=$subnet6?>" title="_(IPv6 address nnnn:xxxx::yyyy)_"<?=$disabled?>>/
<select id="DOCKER_CUSTOM6_<?=$port?>_mask" name="DOCKER_MASK6_<?=$port?>" class="mask"<?=$disabled?>>
<?for ($m=64; $m<=120; $m+=8) echo mk_option($mask6?:64,$m,$m)?></select></span>
<span class="gw6">**_(Gateway)_:**<input type="text" id="DOCKER_CUSTOM6_<?=$port?>_gw" name="DOCKER_GATEWAY6_<?=$port?>" class="gw6" value="<?=$dockercfg["DOCKER_GATEWAY6_$port"]??''?>" title="_(IPv6 address nnnn:xxxx::yyyy)_"<?=$disabled?>></span>
<span class="gw6">**_(Gateway)_:**<input type="text" id="DOCKER_CUSTOM6_<?=$port?>_gw" name="DOCKER_GATEWAY6_<?=$port?>" class="gw6" value="<?=_var($dockercfg,"DOCKER_GATEWAY6_$port")?>" title="_(IPv6 address nnnn:xxxx::yyyy)_"<?=$disabled?>></span>
<input type="checkbox" id="DOCKER_CUSTOM6_<?=$port?>_dhcp" onchange="customDHCP(this.id,6)"<?=$subnet6?'checked':''?><?=$dhcpDisabled?>>
**_(DHCP pool)_:**<input type="text" id="DOCKER_CUSTOM6_<?=$port?>_pool" name="DOCKER_RANGE6_<?=$port?>" class="ip6" value="<?=$range6?>" title="_(IPv6 address nnnn:xxxx::yyyy)_"<?=$disabled?>>/
<select id="DOCKER_CUSTOM6_<?=$port?>_size" name="DOCKER_SIZE6_<?=$port?>" class="mask"<?=$disabled?>>
@@ -439,39 +439,39 @@ _(Docker version)_:
:docker_version_help:
<?if ($dockercfg['DOCKER_IMAGE_TYPE']??''!='folder'):?>
<?if (_var($dockercfg,'DOCKER_IMAGE_TYPE')!='folder'):?>
_(Docker vDisk location)_:
: <?=$dockercfg['DOCKER_IMAGE_FILE']??''?>
: <?=_var($dockercfg,'DOCKER_IMAGE_FILE')?>
<?else:?>
_(Docker directory)_:
: <?=$dockercfg['DOCKER_IMAGE_FILE']??''?>
: <?=_var($dockercfg,'DOCKER_IMAGE_FILE')?>
<?endif;?>
:docker_vdisk_location_active_help:
_(Default appdata storage location)_:
: <?=$dockercfg['DOCKER_APP_CONFIG_PATH']??''?>
: <?=_var($dockercfg,'DOCKER_APP_CONFIG_PATH')?>
:docker_appdata_location_active_help:
<div markdown="1" class="advanced">
_(Docker LOG rotation)_:
: <?=$dockercfg['DOCKER_LOG_ROTATION']??''=='yes' ? _('Enabled') : _('Disabled')?>
: <?=_var($dockercfg,'DOCKER_LOG_ROTATION')=='yes' ? _('Enabled') : _('Disabled')?>
:docker_log_rotation_active_help:
_(Docker custom network type)_:
: <?=$dockercfg['DOCKER_NETWORK_TYPE']??''=='1' ? _('ipvlan') : _('macvlan')?>
: <?=_var($dockercfg,'DOCKER_NETWORK_TYPE')=='1' ? _('ipvlan') : _('macvlan')?>
:docker_custom_network_type_help:
_(Host access to custom networks)_:
: <?=$dockercfg['DOCKER_ALLOW_ACCESS']??''=='yes' ? _('Enabled') : _('Disabled')?>
: <?=_var($dockercfg,'DOCKER_ALLOW_ACCESS')=='yes' ? _('Enabled') : _('Disabled')?>
:docker_custom_network_access_help:
_(Preserve user defined networks)_:
: <?=$dockercfg['DOCKER_USER_NETWORKS']??''=='preserve' ? _('Yes') : _('No')?>
: <?=_var($dockercfg,'DOCKER_USER_NETWORKS')=='preserve' ? _('Yes') : _('No')?>
:docker_user_defined_network_help:
@@ -484,7 +484,7 @@ $docker_dhcp = "DOCKER_DHCP_$net";
_(IPv4 custom network on interface)_ <?=$network?>:
: <span class="<?=$gw4class?>">**_(Subnet)_:** <?=$route?></span>
<span class="<?=$gw4class?>">**_(Gateway)_:** <?=$gateway[$network]?></span>
**_(DHCP pool)_:** <?=$dockercfg[$docker_dhcp] ?? "_(not set)_"?><?if (isset($dockercfg[$docker_dhcp])):?>&nbsp;&nbsp;(<?=pow(2,32-explode('/',$dockercfg[$docker_dhcp])[1])?> _(hosts)_)<?endif;?>
**_(DHCP pool)_:** <?=_var($dockercfg,$docker_dhcp) ?: "_(not set)_"?><?if (isset($dockercfg[$docker_dhcp])):?>&nbsp;&nbsp;(<?=pow(2,32-my_explode('/',$dockercfg[$docker_dhcp])[1])?> _(hosts)_)<?endif;?>
<?endif;?>
<?endforeach;?>
@@ -494,19 +494,19 @@ $port = normalize($network);
[$eth,$vlan] = my_explode('.',$network);
$eth = str_replace(['bond','br'],'eth',$eth);
if (!$vlan) {
$protocol = $$eth['PROTOCOL:0'] ?? 'ipv4';
$protocol = _var($$eth,'PROTOCOL:0','ipv4');
} else {
foreach ($$eth as $key => $value) {
if (strpos($key,'VLANID')!==false && $value==$vlan) {$protocol = $$eth[str_replace('VLANID','PROTOCOL',$key)] ?? 'ipv4'; break;}
if (strpos($key,'VLANID')!==false && $value==$vlan) {$protocol = _var($$eth,str_replace('VLANID','PROTOCOL',$key),'ipv4'); break;}
}
}
[$subnet,$mask] = my_explode('/',$dockercfg["DOCKER_SUBNET_$port"]??'');
[$range,$size] = my_explode('/',$dockercfg["DOCKER_RANGE_$port"]??'');
[$subnet,$mask] = my_explode('/',_var($dockercfg,"DOCKER_SUBNET_$port"));
[$range,$size] = my_explode('/',_var($dockercfg,"DOCKER_RANGE_$port"));
?>
<?if ($protocol != 'ipv6' && $subnet):?>
_(IPv4 custom network on interface)_ <?=$network?>:
: <span class="<?=$gw4class?>">**_(Subnet)_:** <?=$subnet?>/<?=$mask?></span>
<span class="<?=$gw4class?>">**_(Gateway)_:** <?=$dockercfg["DOCKER_GATEWAY_$port"]??''?></span>
<span class="<?=$gw4class?>">**_(Gateway)_:** <?=_var($dockercfg,"DOCKER_GATEWAY_$port")?></span>
**_(DHCP pool)_:** <?=$range ? "$range/$size" : "_(not set)_"?><?if ($range):?>&nbsp;&nbsp;(<?=pow(2,32-($size?:25))?> _(hosts)_)<?endif;?>
<?endif;?>
@@ -520,7 +520,7 @@ if (isset($dockercfg[$docker_dhcp6]) || empty($dockercfg["DOCKER_AUTO_$net"])):?
_(IPv6 custom network on interface)_ <?=$network?>:
: <span class="gw6">**_(Subnet)_:** <?=$route?></span>
<span class="gw6">**_(Gateway)_:** <?=$gateway6[$network]?></span>
**_(DHCP pool)_:** <?=$dockercfg[$docker_dhcp6] ?? "_(not set)_"?>
**_(DHCP pool)_:** <?=_var($dockercfg,$docker_dhcp6) ?: "_(not set)_"?>
<?endif;?>
<?endforeach;?>
@@ -530,19 +530,19 @@ $port = normalize($network);
[$eth,$vlan] = my_explode('.',$network);
$eth = str_replace(['bond','br'],'eth',$eth);
if (!$vlan) {
$protocol = $$eth['PROTOCOL:0'] ?? 'ipv4';
$protocol = _var($$eth,'PROTOCOL:0','ipv4');
} else {
foreach ($$eth as $key => $value) {
if (strpos($key,'VLANID')!==false && $value==$vlan) {$protocol = $$eth[str_replace('VLANID','PROTOCOL',$key)] ?? 'ipv4'; break;}
if (strpos($key,'VLANID')!==false && $value==$vlan) {$protocol = _var($$eth,str_replace('VLANID','PROTOCOL',$key),'ipv4'); break;}
}
}
[$subnet6,$mask6] = my_explode('/',$dockercfg["DOCKER_SUBNET6_$port"]??'');
[$range6,$size6] = my_explode('/',$dockercfg["DOCKER_RANGE6_$port"]??'');
[$subnet6,$mask6] = my_explode('/',_var($dockercfg,"DOCKER_SUBNET6_$port"));
[$range6,$size6] = my_explode('/',_var($dockercfg,"DOCKER_RANGE6_$port"));
?>
<?if ($protocol != 'ipv4' && $subnet6):?>
_(IPv6 custom network on interface)_ <?=$network?>:
: <span class="gw6">**_(Subnet)_:** <?=$subnet6?>/<?=$mask6?></span>
<span class="gw6">**_(Gateway)_:** <?=$dockercfg["DOCKER_GATEWAY6_$port"]??''?></span>
<span class="gw6">**_(Gateway)_:** <?=_var($dockercfg,"DOCKER_GATEWAY6_$port")?></span>
**_(DHCP pool)_:** <?=$range6 ? "$range6/$size6" : "_(not set)_"?>
<?endif;?>
<?endforeach;?>

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

@@ -39,7 +39,7 @@ $cpus = DockerUtil::cpus();
function cpu_pinning() {
global $xml,$cpus;
$vcpu = explode(',',$xml['CPUset'] ?? '');
$vcpu = explode(',',_var($xml,'CPUset'));
$total = count($cpus);
$loop = floor(($total-1)/16)+1;
for ($c = 0; $c < $loop; $c++) {
@@ -73,7 +73,7 @@ function cpu_pinning() {
if (isset($_POST['contName'])) {
$postXML = postToXML($_POST, true);
$dry_run = isset($_POST['dryRun']) && $_POST['dryRun']=='true';
$existing = $_POST['existingContainer'] ?? false;
$existing = _var($_POST,'existingContainer',false);
$create_paths = $dry_run ? false : true;
// Get the command line
[$cmd, $Name, $Repository] = xmlToCommand($postXML, $create_paths);
@@ -228,9 +228,9 @@ if (isset($_GET['xmlTemplate'])) {
$arrConfig['Name'] = 'AppData Config Path';
}
}
$arrConfig['Name'] = strip_tags($arrConfig['Name']);
$arrConfig['Description'] = strip_tags($arrConfig['Description']);
$arrConfig['Requires'] = strip_tags($arrConfig['Requires']);
$arrConfig['Name'] = strip_tags(_var($arrConfig,'Name'));
$arrConfig['Description'] = strip_tags(_var($arrConfig,'Description'));
$arrConfig['Requires'] = strip_tags(_var($arrConfig,'Requires'));
}
}
if (!empty($dockercfg['DOCKER_APP_UNRAID_PATH']) && file_exists($dockercfg['DOCKER_APP_UNRAID_PATH'])) {
@@ -943,16 +943,6 @@ _(Privileged)_:
# ╚════╝ ╚══════╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚══════╝╚══════╝
?>
<div markdown="1" id="templatePopupConfig" style="display:none">
<html <?=$display['rtl']?>lang="<?=strtok($locale,'_')?:'en'?>">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="format-detection" content="telephone=no">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex, nofollow">
<meta name="referrer" content="same-origin">
</head>
<body>
_(Config Type)_:
: <select name="Type" onchange="toggleMode(this,false)">
<option value="Path">_(Path)_</option>
@@ -1006,8 +996,6 @@ _(Password Mask)_:
<option value="true">_(Yes)_</option>
</select>
</div>
</body>
</html>
</div>
<div markdown="1" id="templateDisplayConfig" style="display:none">
@@ -16,15 +16,15 @@ libxml_use_internal_errors(true); # Suppress any warnings from xml errors.
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
// add translations
if (isset($_SERVER['REQUEST_URI']) && $_SERVER['REQUEST_URI']!='docker' && substr($_SERVER['REQUEST_URI'],0,7) != '/Docker') {
$_SERVER['REQUEST_URI'] = 'docker';
require_once "$docroot/webGui/include/Translations.php";
}
require_once "$docroot/plugins/dynamix.docker.manager/include/Helpers.php";
require_once "$docroot/webGui/include/Wrappers.php";
// add translations
if (_var($_SERVER,'REQUEST_URI')!='docker' && substr(_var($_SERVER,'REQUEST_URI'),0,7)!='/Docker') {
$_SERVER['REQUEST_URI'] = 'docker';
require_once "$docroot/webGui/include/Translations.php";
}
$dockerManPaths = [
'autostart-file' => "/var/lib/docker/unraid-autostart",
'update-status' => "/var/lib/docker/unraid-update-status.json",
@@ -49,7 +49,7 @@ $driver = DockerUtil::driver();
// Docker configuration file - guaranteed to exist
$docker_cfgfile = '/boot/config/docker.cfg';
$defaults = @parse_ini_file("$docroot/plugins/dynamix.docker.manager/default.cfg") ?: [];
$dockercfg = array_replace_recursive($defaults, parse_ini_file($docker_cfgfile));
$dockercfg = array_replace_recursive($defaults, @parse_ini_file($docker_cfgfile) ?: []);
function var_split($item, $i=0) {
return array_pad(explode(' ',$item),$i+1,'')[$i];
@@ -251,18 +251,18 @@ class DockerTemplates {
}
$TemplateRepository = DockerUtil::ensureImageTag($doc->getElementsByTagName('Repository')->item(0)->nodeValue??'');
if ($TemplateRepository && $TemplateRepository==$Repository) {
$TemplateField = $doc->getElementsByTagName($field)->item(0)->nodeValue ?? '';
$TemplateField = $doc->getElementsByTagName($field)->item(0)->nodeValue??'';
return trim($TemplateField);
}
}
return null;
}
public function getUserTemplate($Container) {
foreach ($this->getTemplates('user') as $file) {
$doc = new DOMDocument('1.0', 'utf-8');
$doc->load($file['path']);
$Name = $doc->getElementsByTagName('Name')->item(0)->nodeValue;
$Name = $doc->getElementsByTagName('Name')->item(0)->nodeValue??'';
if ($Name==$Container) return $file['path'];
}
return false;
@@ -271,13 +271,13 @@ class DockerTemplates {
private function getControlURL(&$ct, $myIP, $WebUI) {
global $host;
$port = &$ct['Ports'][0];
$myIP = $myIP ?: $this->getTemplateValue($ct['Image'], 'MyIP') ?: ($ct['NetworkMode']=='host'||$port['NAT'] ? $host : ($port['IP'] ?: DockerUtil::myIP($ct['Name'])));
$myIP = $myIP ?: $this->getTemplateValue($ct['Image'],'MyIP') ?: (_var($ct,'NetworkMode')=='host'||_var($port,'NAT') ? $host : (_var($port,'IP') ?: DockerUtil::myIP($ct['Name'])));
// Get the WebUI address from the templates as a fallback
$WebUI = preg_replace("%\[IP\]%", $myIP, $WebUI ?? $this->getTemplateValue($ct['Image'], 'WebUI'));
if (preg_match("%\[PORT:(\d+)\]%", $WebUI, $matches)) {
$ConfigPort = $matches[1];
$ConfigPort = $matches[1] ?? '';
foreach ($ct['Ports'] as $port) {
if ($port['NAT'] && $port['PrivatePort']==$ConfigPort) {$ConfigPort = $port['PublicPort']; break;}
if (_var($port,'NAT') && _var($port,'PrivatePort')==$ConfigPort) {$ConfigPort = _var($port,'PublicPort'); break;}
}
$WebUI = preg_replace("%\[PORT:\d+\]%", $ConfigPort, $WebUI);
}
@@ -290,14 +290,14 @@ class DockerTemplates {
$DockerUpdate = new DockerUpdate();
//$DockerUpdate->verbose = $this->verbose;
$info = DockerUtil::loadJSON($dockerManPaths['webui-info']);
$autoStart = array_map('var_split', @file($dockerManPaths['autostart-file'], FILE_IGNORE_NEW_LINES) ?: []);
$autoStart = array_map('var_split', @file($dockerManPaths['autostart-file'],FILE_IGNORE_NEW_LINES) ?: []);
foreach ($DockerClient->getDockerContainers() as $ct) {
$name = $ct['Name'];
$image = $ct['Image'];
$tmp = &$info[$name] ?? [];
$tmp['running'] = $ct['Running'];
$tmp['paused'] = $ct['Paused'];
$tmp['autostart'] = in_array($name, $autoStart);
$tmp['autostart'] = in_array($name,$autoStart);
$tmp['cpuset'] = $ct['CPUset'];
$tmp['url'] = $tmp['url'] ?? '';
// read docker label for WebUI & Icon
@@ -314,10 +314,11 @@ class DockerTemplates {
// non-templated webui, user specified
$tmp['url'] = $webui;
} else {
$ip = ($ct['NetworkMode']=='host'||($port['NAT']??false) ? $host : ($port['IP']??''));
$ip = ($ct['NetworkMode']=='host'||_var($port,'NAT')) ? $host : _var($port,'IP');
$tmp['url'] = $ip ? (strpos($tmp['url'],$ip)!==false ? $tmp['url'] : $this->getControlURL($ct, $ip, $tmp['url'])) : $tmp['url'];
}
$tmp['shell'] = $tmp['shell'] ?? $this->getTemplateValue($image, 'Shell');
if ( ($tmp['shell'] ?? false) == false )
$tmp['shell'] = $this->getTemplateValue($image, 'Shell');
}
$tmp['registry'] = $tmp['registry'] ?? $this->getTemplateValue($image, 'Registry');
$tmp['Support'] = $tmp['Support'] ?? $this->getTemplateValue($image, 'Support');
@@ -347,7 +348,7 @@ class DockerTemplates {
$imageName = $contName ?: $name;
$iconRAM = sprintf('%s/%s-%s.png', $dockerManPaths['images-ram'], $contName, 'icon');
$icon = sprintf('%s/%s-%s.png', $dockerManPaths['images'], $contName, 'icon');
if (!is_dir(dirname($iconRAM))) mkdir(dirname($iconRAM), 0755, true);
if (!is_dir(dirname($icon))) mkdir(dirname($icon), 0755, true);
@@ -522,7 +523,7 @@ class DockerUpdate{
global $dockerManPaths;
$DockerClient = new DockerClient();
$updateStatus = DockerUtil::loadJSON($dockerManPaths['update-status']);
$images = $image ? [DockerUtil::ensureImageTag($image)] : array_map(function($ar){return $ar['Tags'][0];}, $DockerClient->getDockerImages());
$images = $image ? [DockerUtil::ensureImageTag($image)] : array_map(function($ar){return $ar['Tags'][0]??'';}, $DockerClient->getDockerImages());
foreach ($images as $img) {
$localVersion = null;
if (!empty($updateStatus[$img]) && array_key_exists('local', $updateStatus[$img])) {
@@ -562,7 +563,7 @@ class DockerUpdate{
public function updateUserTemplate($Container) {
// Don't update templates, but leave code in place for future reference
return;
$changed = false;
$DockerTemplates = new DockerTemplates();
$validElements = ['Support', 'Overview', 'Category', 'Project', 'Icon', 'ReadMe'];
@@ -709,7 +710,7 @@ class DockerClient {
}
public function getDockerJSON($url, $method='GET', &$code=null, $callback=null, $unchunk=false, $headers=null) {
$api = '/v1.37'; // used to force an API version. See https://docs.docker.com/develop/sdk/#api-version-matrix
$api = ''; // latest API version. See https://docs.docker.com/develop/sdk/#api-version-matrix
$fp = stream_socket_client('unix:///var/run/docker.sock', $errno, $errstr);
if ($fp === false) {
echo "Couldn't create socket: [$errno] $errstr";
@@ -747,7 +748,7 @@ class DockerClient {
public function doesImageExist($image) {
foreach ($this->getDockerImages() as $img) {
if (strpos($img['Tags'][0], $image)!==false) return true;
if (strpos($img['Tags'][0]??'', $image)!==false) return true;
}
return false;
}
@@ -781,7 +782,7 @@ class DockerClient {
public function stopContainer($id, $t=false) {
global $dockercfg;
$this->getDockerJSON("/containers/$id/stop?t=".($t?:$dockercfg['DOCKER_TIMEOUT']??10), 'POST', $code);
$this->getDockerJSON("/containers/$id/stop?t=".($t ?: _var($dockercfg,'DOCKER_TIMEOUT',10)), 'POST', $code);
$this->flushCache($this::$containersCache);
return $code;
}
@@ -867,7 +868,7 @@ class DockerClient {
}
[$ret['username'], $ret['password']] = array_pad(explode(':', base64_decode($dockerConfig['auths'][ $configKey ]['auth'])),2,'');
return $ret;
return $ret;
}
public function removeImage($id) {
@@ -928,7 +929,7 @@ class DockerClient {
$ports = (isset($ports) && is_array($ports)) ? $ports : [];
foreach ($ports as $port => $value) {
[$PrivatePort, $Type] = array_pad(explode('/', $port),2,'');
$c['Ports'][] = ['IP' => $ip, 'PrivatePort' => $PrivatePort, 'PublicPort' => $nat ? $value[0]['HostPort']:$PrivatePort, 'NAT' => $nat, 'Type' => $Type ];
$c['Ports'][] = ['IP' => $ip, 'PrivatePort' => $PrivatePort, 'PublicPort' => $nat ? $value[0]['HostPort'] : $PrivatePort, 'NAT' => $nat, 'Type' => $Type];
}
$this::$containersCache[] = $c;
}
@@ -1,7 +1,7 @@
<?PHP
/* Copyright 2005-2020, Lime Technology
* Copyright 2014-2020, Guilherme Jardim, Eric Schultz, Jon Panozzo.
* Copyright 2012-2020, Bergware International.
/* Copyright 2005-2023, Lime Technology
* Copyright 2014-2023, Guilherme Jardim, Eric Schultz, Jon Panozzo.
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
@@ -13,13 +13,14 @@
?>
<?
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/webGui/include/Helpers.php";
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
// add translations
$_SERVER['REQUEST_URI'] = 'docker';
require_once "$docroot/webGui/include/Translations.php";
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
require_once "$docroot/webGui/include/Helpers.php";
$DockerClient = new DockerClient();
$DockerTemplates = new DockerTemplates();
$containers = $DockerClient->getDockerContainers();
@@ -33,8 +34,9 @@ if (!$containers && !$images) {
}
if (file_exists($user_prefs)) {
$prefs = parse_ini_file($user_prefs); $sort = [];
foreach ($containers as $ct) $sort[] = array_search($ct['Name'],$prefs) ?? 999;
$prefs = @parse_ini_file($user_prefs) ?: [];
$sort = [];
foreach ($containers as $ct) $sort[] = array_search($ct['Name'],$prefs);
array_multisort($sort,SORT_NUMERIC,$containers);
unset($sort);
}
@@ -44,8 +46,8 @@ $allInfo = $DockerTemplates->getAllInfo();
$docker = [];
$null = '0.0.0.0';
$autostart = @file($autostart_file, FILE_IGNORE_NEW_LINES) ?: [];
$names = array_map('var_split', $autostart);
$autostart = @file($autostart_file,FILE_IGNORE_NEW_LINES) ?: [];
$names = array_map('var_split',$autostart);
function my_lang_time($text) {
[$number, $text] = my_explode(' ',$text,2);
@@ -75,7 +77,7 @@ foreach ($containers as $ct) {
$support = html_entity_decode($info['Support']??'');
$project = html_entity_decode($info['Project']??'');
$registry = html_entity_decode($info['registry']??'');
$donateLink = html_entity_decode($info['DonateLink']??'');
$donateLink = html_entity_decode($info['DonateLink']??'');
$readme = html_entity_decode($info['ReadMe']??'');
$menu = sprintf("onclick=\"addDockerContainerContext('%s','%s','%s',%s,%s,%s,%s,'%s','%s','%s','%s','%s','%s', '%s','%s')\"", addslashes($name), addslashes($ct['ImageId']), addslashes($template), $running, $paused, $updateStatus, $is_autostart, addslashes($webGui), $shell, $id, addslashes($support), addslashes($project),addslashes($registry),addslashes($donateLink),addslashes($readme));
$docker[] = "docker.push({name:'$name',id:'$id',state:$running,pause:$paused,update:$updateStatus});";
@@ -88,9 +90,9 @@ foreach ($containers as $ct) {
$wait = var_split($autostart[array_search($name,$names)]??'',1);
$ports = [];
foreach ($ct['Ports'] as $port) {
$intern = $running ? ($ct['NetworkMode']=='host' ? $host : $port['IP']) : $null;
$extern = $running ? ($port['NAT'] ? $host : $intern) : $null;
$ports[] = sprintf('%s:%s/%s<i class="fa fa-arrows-h" style="margin:0 6px"></i>%s:%s', $intern, $port['PrivatePort'], strtoupper($port['Type']), $extern, $port['PublicPort']);
$intern = $running ? ($ct['NetworkMode']=='host' ? $host : _var($port,'IP')) : $null;
$extern = $running ? (_var($port,'NAT') ? $host : $intern) : $null;
$ports[] = sprintf('%s:%s/%s<i class="fa fa-arrows-h" style="margin:0 6px"></i>%s:%s', $intern, _var($port,'PrivatePort'), strtoupper(_var($port,'Type')), $extern, _var($port,'PublicPort'));
}
$paths = [];
$ct['Volumes'] = is_array($ct['Volumes']) ? $ct['Volumes'] : [];
@@ -98,7 +100,7 @@ foreach ($containers as $ct) {
[$host_path,$container_path,$access_mode] = my_explode(':',$mount,3);
$paths[] = sprintf('%s<i class="fa fa-%s" style="margin:0 6px"></i>%s', htmlspecialchars($container_path), $access_mode=='ro'?'long-arrow-left':'arrows-h', htmlspecialchars($host_path));
}
echo "<tr class='sortable'><td class='ct-name' style='width:220px;padding:8px'>";
echo "<tr class='sortable'><td class='ct-name' style='width:220px;padding:8px'><i class='fa fa-arrows-v mover orange-text'></i>";
if ($template) {
$appname = "<a class='exec' onclick=\"editContainer('".addslashes(htmlspecialchars($name))."','".addslashes(htmlspecialchars($template))."')\">".htmlspecialchars($name)."</a>";
} else {
@@ -150,7 +152,7 @@ foreach ($images as $image) {
$menu = sprintf("onclick=\"addDockerImageContext('%s','%s')\"", $id, implode(',',$image['Tags']));
echo "<tr class='advanced'><td style='width:220px;padding:8px'>";
echo "<span class='outer apps'><span id='$id' $menu class='hand'><img src='/webGui/images/disk.png' class='img'></span><span class='inner'>("._('orphan image').")<br><i class='fa fa-square stopped grey-text'></i><span class='state'>"._('stopped')."</span></span></span>";
echo "</td><td colspan='5'>"._('Image ID').": $id<br>";
echo "</td><td colspan='6'>"._('Image ID').": $id<br>";
echo implode(', ',$image['Tags']);
echo "</td><td>"._('Created')." ".htmlspecialchars(_($image['Created'],0))."</td></tr>";
}
@@ -0,0 +1,73 @@
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2014-2023, Guilherme Jardim, Eric Schultz, Jon Panozzo.
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/webGui/include/Secure.php";
require_once "$docroot/webGui/include/Wrappers.php";
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
// add translations
$_SERVER['REQUEST_URI'] = 'docker';
require_once "$docroot/webGui/include/Translations.php";
$DockerClient = new DockerClient();
$action = unscript(_var($_REQUEST,'action'));
$container = unbundle(_var($_REQUEST,'container'));
$name = unscript(_var($_REQUEST,'name'));
$image = unscript(_var($_REQUEST,'image'));
$arrResponse = ['error' => _('Missing parameters')];
switch ($action) {
case 'start':
if ($container) $arrResponse = ['success' => $DockerClient->startContainer($container)];
break;
case 'pause':
if ($container) $arrResponse = ['success' => $DockerClient->pauseContainer($container)];
break;
case 'stop':
if ($container) $arrResponse = ['success' => $DockerClient->stopContainer($container)];
break;
case 'resume':
if ($container) $arrResponse = ['success' => $DockerClient->resumeContainer($container)];
break;
case 'restart':
if ($container) $arrResponse = ['success' => $DockerClient->restartContainer($container)];
break;
case 'remove_container':
if ($container) $arrResponse = ['success' => $DockerClient->removeContainer($name, $container, 1)];
break;
case 'remove_image':
if ($image) $arrResponse = ['success' => $DockerClient->removeImage($image)];
break;
case 'remove_all':
if ($container && $image) {
// first: try to remove container
$ret = $DockerClient->removeContainer($name, $container, 2);
if ($ret === true) {
// next: try to remove image
$arrResponse = ['success' => $DockerClient->removeImage($image)];
} else {
// error: container failed to remove
$arrResponse = ['success' => $ret];
}
}
break;
default:
$arrResponse = ['error' => _('Unknown action')." '$action'"];
break;
}
header('Content-Type: application/json');
die(json_encode($arrResponse));
@@ -111,14 +111,13 @@ function xmlToVar($xml) {
$c['Value'] = strlen(xml_decode($config)) ? xml_decode($config) : xml_decode($config['Default']);
foreach ($config->attributes() as $key => $value) {
$value = xml_decode($value);
$val = strtolower($value);
if ($key == 'Mode') {
switch (xml_decode($config['Type'])) {
case 'Path':
$value = ($val=='rw'||$val=='rw,slave'||$val=='rw,shared'||$val=='ro'||$val=='ro,slave'||$val=='ro,shared') ? $value : "rw";
$value = in_array(strtolower($value),['rw','rw,slave','rw,shared','ro','ro,slave','ro,shared']) ? $value : "rw";
break;
case 'Port':
$value = ($val=='tcp'||$val=='udp') ? $value : "tcp";
$value = in_array(strtolower($value),['tcp','udp']) ? $value : "tcp";
break;
}
}
@@ -495,6 +494,8 @@ function setXmlVal(&$xml, $value, $el, $attr=null, $pos=0) {
function getAllocations() {
global $DockerClient, $host;
$ports = [];
foreach ($DockerClient->getDockerContainers() as $ct) {
$list = $port = [];
$nat = $ip = false;
@@ -2,6 +2,7 @@ var eventURL = '/plugins/dynamix.docker.manager/include/Events.php';
function addDockerContainerContext(container, image, template, started, paused, update, autostart, webui, shell, id, Support, Project, Registry, donateLink, ReadMe) {
var opts = [];
context.settings({right:false,above:false});
if (started && !paused) {
if (webui !== '' && webui != '#') opts.push({text:_('WebUI'), icon:'fa-globe', href:webui, target:'_blank'});
opts.push({text:_('Console'), icon:'fa-terminal', action:function(e){e.preventDefault(); openTerminal('docker',container,shell);}});
@@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="Content-Security-Policy" content="block-all-mixed-content">
<meta name="format-detection" content="telephone=no">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="viewport" content="width=1300">
<meta name="robots" content="noindex, nofollow">
<meta name="referrer" content="same-origin">
<style>
@@ -1,7 +1,7 @@
#!/usr/bin/php -q
<?PHP
/* Copyright 2005-2022, Lime Technology
* Copyright 2012-2022, Bergware International.
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
@@ -19,7 +19,7 @@ extract(parse_plugin_cfg('dynamix',true));
// add translations
$_SERVER['REQUEST_URI'] = 'docker';
$login_locale = $display['locale'];
$login_locale = _var($display,'locale');
require_once "$docroot/webGui/include/Translations.php";
$unit = ['B','kB','MB','GB','TB','PB','EB','ZB','YB'];
@@ -69,12 +69,12 @@ foreach ($container as $ct) {
$sum = ['total' => 0, 'writable' => 0, 'log' => 0];
array_multisort(array_column($list,'total'),SORT_DESC,$list); // sort on container size
$i = 0;
write(align(_('Name'),50).align(_('Container')).align(_('Writable'))._('Log')."\n");
write(align(_('Name'),48).align(_('Container')).align(_('Writable'))._('Log')."\n");
foreach ($list as $ct) {
write(($i++==0 ? '<hr>':'').align($ct['name'],50).align(autoscale($ct['total'])).align(autoscale($ct['writable'])).autoscale($ct['log'])."\n");
write(($i++==0 ? '<hr>':'').align($ct['name'],48).align(autoscale($ct['total'])).align(autoscale($ct['writable'])).autoscale($ct['log'])."\n");
$sum['total'] += $ct['total'];
$sum['writable'] += $ct['writable'];
$sum['log'] += $ct['log'];
}
write("<hr>".align(_('Total size'),50).align(autoscale($sum['total'])).align(autoscale($sum['writable'])).autoscale($sum['log'])."\n",'_DONE_','');
write("<hr>".align(_('Total size'),48).align(autoscale($sum['total'])).align(autoscale($sum['writable'])).autoscale($sum['log'])."\n",'_DONE_','');
?>
@@ -2,6 +2,7 @@
<?PHP
/* Copyright 2015-2023, Lime Technology
* Copyright 2015-2016, Guilherme Jardim, Eric Schultz, Jon Panozzo.
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
@@ -14,26 +15,25 @@
<?
$cfgfile = "/boot/config/docker.cfg";
$cfg_defaults = [
"DOCKER_ENABLED" => "no",
"DOCKER_IMAGE_FILE" => "/mnt/user/system/docker/docker.img",
"DOCKER_IMAGE_SIZE" => "20",
"DOCKER_APP_CONFIG_PATH" => "/mnt/user/appdata/",
"DOCKER_APP_UNRAID_PATH" => "",
"DOCKER_READMORE" => "yes"
"DOCKER_ENABLED" => "no",
"DOCKER_IMAGE_FILE" => "/mnt/user/system/docker/docker.img",
"DOCKER_IMAGE_SIZE" => "20",
"DOCKER_APP_CONFIG_PATH" => "/mnt/user/appdata/",
"DOCKER_APP_UNRAID_PATH" => "",
"DOCKER_READMORE" => "yes"
];
$cfg_new = $cfg_defaults;
if (file_exists($cfgfile)) {
$cfg_old = parse_ini_file($cfgfile);
if (!empty($cfg_old)) {
$cfg_new = array_merge($cfg_defaults, $cfg_old);
if (empty(array_diff($cfg_new, $cfg_old)))
unset($cfg_new);
}
$cfg_old = parse_ini_file($cfgfile);
if (!empty($cfg_old)) {
$cfg_new = array_merge($cfg_defaults, $cfg_old);
if (empty(array_diff($cfg_new, $cfg_old))) unset($cfg_new);
}
}
if (isset($cfg_new)) {
$tmp = '';
foreach ($cfg_new as $key => $value) $tmp .= "$key=\"$value\"\n";
file_put_contents($cfgfile, $tmp);
$tmp = '';
foreach ($cfg_new as $key => $value) $tmp .= "$key=\"$value\"\n";
file_put_contents($cfgfile, $tmp);
}
?>
@@ -1,7 +1,8 @@
#!/usr/bin/php
#!/usr/bin/php -q
<?PHP
/* Copyright 2005-2022, Lime Technology
* Copyright 2014-2022, Guilherme Jardim, Eric Schultz, Jon Panozzo.
/* Copyright 2005-2023, Lime Technology
* Copyright 2014-2023, Guilherme Jardim, Eric Schultz, Jon Panozzo.
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
@@ -15,18 +16,17 @@
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/webGui/include/Wrappers.php";
$unraid = parse_plugin_cfg('dynamix', true);
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
extract(parse_plugin_cfg('dynamix', true));
// Multi-language support
$_SERVER['REQUEST_URI'] = "scripts";
$login_locale = $unraid['display']['locale'];
$login_locale = _var($display,'locale');
require_once "$docroot/webGui/include/Translations.php";
exec("pgrep docker", $pid);
if (count($pid) == 1) exit(0);
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
$DockerClient = new DockerClient();
$DockerTemplates = new DockerTemplates();
@@ -44,19 +44,16 @@ if (!isset($check)) {
$DockerTemplates->getAllInfo(true);
echo " Done.";
} else {
require_once "$docroot/webGui/include/Wrappers.php";
$notify = "$docroot/webGui/scripts/notify";
$unraid = parse_plugin_cfg("dynamix",true);
$var = parse_ini_file("/var/local/emhttp/var.ini");
$server = strtoupper($var['NAME']);
$output = $unraid['notify']['docker_notify'];
$info = $DockerTemplates->getAllInfo(true);
$var = @parse_ini_file("/var/local/emhttp/var.ini") ?: [];
$server = strtoupper(_var($var,'NAME','tower'));
$output = _var($notify,'docker_notify');
$info = $DockerTemplates->getAllInfo(true);
foreach ($DockerClient->getDockerContainers() as $ct) {
$name = $ct['Name'];
$name = $ct['Name'];
$image = $ct['Image'];
if ($info[$name]['updated'] == "false") {
$updateStatus = (is_file($dockerManPaths['update-status'])) ? json_decode(file_get_contents($dockerManPaths['update-status']), TRUE) : [];
$updateStatus = is_file($dockerManPaths['update-status']) ? json_decode(file_get_contents($dockerManPaths['update-status']),true) : [];
$new = str_replace('sha256:', '', $updateStatus[$image]['remote']);
$new = substr($new, 0, 4).'..'.substr($new, -4, 4);
if ( ! isset($nonotify) ) {
@@ -0,0 +1,30 @@
#!/usr/bin/php -q
<?PHP
function startsWith($haystack, $needle) {
if (!is_string($haystack) || !is_string($needle)) return false;
return $needle === "" || strripos($haystack, $needle, -strlen($haystack)) !== false;
}
$xmlFiles = glob("/boot/config/plugins/dockerMan/templates-user/*.xml");
foreach ($xmlFiles as $file) {
unset($changeFlag);
$xml = @simplexml_load_file($file);
if (!$xml) continue;
foreach ($xml->Config as $id => $config) {
if (startsWith((string)$config->attributes()->Description,"Container ".(string)$config->attributes()->Type)) {
$config->attributes()->Description = "";
$changeFlag = true;
}
}
if (isset($changeFlag)) {
copy($file,"$file.bak");
$dom = new DOMDocument('1.0');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($xml->asXML());
file_put_contents($file,$dom->saveXML());
}
}
?>
@@ -19,7 +19,7 @@ extract(parse_plugin_cfg('dynamix',true));
// add translations
$_SERVER['REQUEST_URI'] = '';
$login_locale = $display['locale'];
$login_locale = _var($display,'locale');
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
$var = parse_ini_file('/var/local/emhttp/var.ini');
@@ -28,21 +28,40 @@ unraid-promo {
<?
$myservers_flash_cfg_path='/boot/config/plugins/dynamix.my.servers/myservers.cfg';
$myservers = file_exists($myservers_flash_cfg_path) ? @parse_ini_file($myservers_flash_cfg_path,true) : [];
$ALLOWED_UPC_ENV_VALS = [
'production',
'staging',
'stagingLogs',
'development',
'local',
'preview',
];
$UPC_ENV_CK = in_array($_COOKIE['UPC_ENV']??'', $ALLOWED_UPC_ENV_VALS)
? $_COOKIE['UPC_ENV']
$ALLOWED_UPC_ENV_PREVIEW_CNAME = '.d1eohvtyc6gnee.amplifyapp.com/';
// defaults
$computedCookieValue = $_COOKIE['UPC_ENV'] ?? '';
$previewUrl = '';
$isPreview = strpos($computedCookieValue, 'preview::');
// extract preview src url
if ($isPreview !== false) {
list($computedCookieValue, $previewUrl) = explode('::', $computedCookieValue);
// prevent unauthoraized URLs for previews
$isPreviewAllowed = strpos($previewUrl, $ALLOWED_UPC_ENV_PREVIEW_CNAME);
if (!$isPreviewAllowed) {
$computedCookieValue = '';
$previewUrl = '';
}
}
// finalize cookie value
$UPC_ENV_CK = in_array($computedCookieValue, $ALLOWED_UPC_ENV_VALS)
? $computedCookieValue
: null;
// Determine what source we should use for web components
if (!file_exists('/usr/local/sbin/unraid-api')) { // When NOT using the plugin we should load the UPC from the file system unless $UPC_ENV_CK exists.
$UPC_ENV = $UPC_ENV_CK ?? 'local';
} else { // When PLG exists load from local when not signed in but when signed in load UPC from production.
$UPC_ENV = $UPC_ENV_CK ?? ((empty($myservers['remote']['username']) || empty($var['regFILE'])) ? 'local' : 'production');
} else {
$UPC_ENV = $UPC_ENV_CK ?? 'production';
}
$upcLocalSrc = autov('/plugins/dynamix.my.servers/webComps/unraid.min.js', true);
switch ($UPC_ENV) {
@@ -58,6 +77,9 @@ switch ($UPC_ENV) {
case 'development':
$upcSrc = 'https://launchpad.unraid.test:6969/webComps/unraid.js?t=' . time();
break;
case 'preview':
$upcSrc = $previewUrl . 'webComps/unraid.min.js';
break;
default: // load from webGUI filesystem.
$upcSrc = $upcLocalSrc;
break;
@@ -68,18 +90,20 @@ echo '<script id="unraid-wc" defer src="' . $upcSrc . '"></script>';
<script type="text/javascript">
const upcEnvCookie = "<?=$UPC_ENV_CK??''?>";
if (upcEnvCookie) console.debug('[UPC_ENV] ✨', upcEnvCookie);
// If the UPC isn't defined after 2secs inject UPC via
// If the UPC isn't defined after 3secs inject UPC via
setTimeout(() => {
if (!window.customElements.get('unraid-user-profile')) {
console.log('[UPC] Fallback to filesystem src 😖');
const el = document.createElement('script');
el.type = 'text/javascript';
el.src = '<?=$upcLocalSrc?>';
document.head.appendChild(el);
return upcEnv('local', false, true); // set session cookie to prevent delayed loads of UPC
}
return false;
}, 2000);
// UPC exists do nothing
if (window.customElements.get('unraid-user-profile')) return;
console.log('[UPC] Fallback to filesystem src 😖');
const el = document.createElement('script');
el.type = 'text/javascript';
el.src = '<?=$upcLocalSrc?>';
document.head.appendChild(el);
return upcEnv('local', false, true); // set session cookie to prevent delayed loads of UPC
}, 3000);
function upcEnv(str, reload = true, session = false) { // overwrite upc src
const ckName = 'UPC_ENV';
const ckDate = new Date();
@@ -0,0 +1,648 @@
<!-- myservers2 -->
<?
// add 'ipaddr' function for 6.9 backwards compatibility
if (!function_exists('ipaddr')) {
function ipaddr($ethX='eth0', $prot=4) {
global $$ethX;
switch ($$ethX['PROTOCOL:0']) {
case 'ipv4':
return $$ethX['IPADDR:0'];
case 'ipv6':
return $$ethX['IPADDR6:0'];
case 'ipv4+ipv6':
switch ($prot) {
case 4: return $$ethX['IPADDR:0'];
case 6: return $$ethX['IPADDR6:0'];
default:return [$$ethX['IPADDR:0'],$$ethX['IPADDR6:0']];}
default:
return $$ethX['IPADDR:0'];
}
}
}
$upc_translations = [
($_SESSION['locale']) ? $_SESSION['locale'] : 'en_US' => [
'getStarted' => _('Get Started'),
'signIn' => _('Sign In'),
'signUp' => _('Sign Up'),
'signInUp' => _('Sign In / Up'),
'signInUnraidNetAccount' => _('Sign In with Unraid.net Account'),
'signOut' => _('Sign Out'),
'error' => _('Error'),
'fixError' => _('Fix Error'),
'closeLaunchpad' => _('Close Launchpad and continue to webGUI'),
'installPlugin' => _('Install Plugin'),
'noThanks' => _('No thanks'),
'closePromo' => _('Close Connect details and continue to webGUI'),
'promoHeading' => _('Enhance your Unraid experience with these<br> Connect (BETA) features'),
'learnMore' => _('Learn more'),
'checkoutTheMyServersDocs' => _('Checkout the Connect docs'),
'popUp' => _('Pop-up'),
'close' => _('Close'),
'backToPopUp' => sprintf(_('Back to %s'), _('Pop-up')),
'closePopUp' => sprintf(_('Close %s'), _('Pop-up')),
'contactSupport' => _('Contact Support'),
'lanIp' => sprintf(_('Click to copy LAN IP %s'), '{0}'),
'lanIpCopied' => _('LAN IP Copied'),
'continueToUnraid' => _('Continue to Unraid'),
'description' => _('Description'),
'year' => _('year'),
'years' => _('years'),
'month' => _('month'),
'months' => _('months'),
'day' => _('day'),
'days' => _('days'),
'hour' => _('hour'),
'hours' => _('hours'),
'minute' => _('minute'),
'minutes' => _('minutes'),
'second' => _('second'),
'seconds' => _('seconds'),
'ago' => _('ago'),
'basicPlusPro' => [
'heading' => _('Thank you for choosing Unraid OS and Connect').'!',
'message' => [
'registered' => _('Register for Connect by signing in to Unraid.net'),
'upgradeEligible' => _('To support more storage devices as your server grows click Upgrade Key'),
],
],
'actions' => [
'purchase' => _('Purchase Key'),
'upgrade' => _('Upgrade Key'),
'recover' => _('Recover Key'),
'replace' => _('Replace Key'),
'replaceIneligible' => _('Replace Key Ineligible'),
'extend' => _('Extend Trial'),
'startTrial' => _('Start Trial'),
'signOutUnraidNet' => _('Sign Out of Unraid.net'),
'redeemActivationCode' => _('Redeem Activation Code'),
],
'upc' => [
'avatarAlt' => '{0} '._('Avatar'),
'confirmClosure' => _('Confirm closure then continue to webGUI'),
'closeDropdown' => _('Close dropdown'),
'openDropdown' => _('Open dropdown'),
'pleaseConfirmClosureYouHaveOpenPopUp' => _('Please confirm closure').'. '._('You have an open pop-up').'.',
'trialHasExpiredSeeOptions' => _('Trial has expired see options below'),
'errorCertRequiresSignIn' => _('Sign In before your Unraid.net SSL certificate expires'),
'removeMyServersPlugin' => _('Remove Connect plugin'),
'continueUsingMyServers' => _('Continue using Connect'),
'confirmMyServersPluginRemoval' => _('Confirm Connect plugin removal'),
'removingMyServersPlugin' => _('Removing Connect plugin…'),
'enhanceYourExperienceWithMyServers' => _('Enhance your experience with Connect'),
'lanIpCopied' => _('LAN IP Copied'),
'installingMyServers' => _('Installing Connect (beta)'),
"updatePlugin" => _('Update Plugin'),
"updatingMyServers" => _('Updating Connect (beta)'),
'thankYouForInstallingMyServers' => _('Thank you installing Connect') . '!',
'connectYourUnraidnetAccountToGetStarted' => _('Connect your Unraid.net account to get started'),
'noRemoteApikeyRegisteredWithPlg' => [
'heading' => _('Connect Error'),
'msg' => _('Unraid.net re-authentication required'),
],
'errorTooManyDisks' => [
'heading' => 'Too many devices',
'msg' => [
'base' => 'You must upgrade your key to support more devices.',
'basic' => 'Your Basic key supports 6 devices.',
'plus' => 'Your Plus key supports 12 devices.',
],
],
'extraLinks' => [
'newTab' => sprintf(_('Opens %s in new tab'), '{0}'),
'myServers' => _('Go to Connect'),
'forums' => _('Unraid Forums'),
'settings' => [
'text' => _('Settings'),
'title' => _('Settings > Management Access • Unraid.net'),
],
],
'meta' => [
'trial' => [
'active' => [
'date' => sprintf(_('Trial key expires at %s'), '{date}'),
'timeDiff' => sprintf(_('Trial expires in %s'), '{timeDiff}'),
],
'expired' => [
'date' => sprintf(_('Trial key expired at %s'), '{date}'),
'timeDiff' => sprintf(_('Trial expired %s'), '{timeDiff}'),
],
],
'uptime' => [
'date' => sprintf(_('Server up since %s'), '{date}'),
'readable' => sprintf(_('Uptime %s'), '{timeDiff}'),
],
],
'myServers' => [
'heading' => _('Connect'),
'beta' => _('beta'),
'restarting' => _('Restarting…'),
'errors' => [
'unraidApi' => [
'heading' => _('Unraid API Error'),
'message' => _('Failed to connect to Unraid API'),
],
'myServers' => [
'heading' => _('Connect Error'),
'message' => _('Please wait a moment and reload the page'),
],
],
'closeDetails' => _('Close Details'),
'loading' => _('Loading Connect data'),
'displayingLastKnown' => _('Displaying last known server data'),
'mothership' => [
'connected' => _('Connected'),
'notConnected' => _('Disconnected'),
],
'accessLabels' => [
'current' => _('Current server'),
'local' => _('Local access'),
'offline' => _('Server Offline'),
'remote' => _('Remote access'),
'unavailable' => _('Access unavailable'),
],
'api' => [
'start' => _('Restart unraid-api'),
'startTitle' => _('Executes `unraid-api start`; no terminal needed'),
'stop' => _('Stop unraid-api'),
],
],
'opensNewHttpsWindow' => [
'base' => sprintf(_('Opens new HTTPS window to %s'), '{0}'),
'signIn' => sprintf(_('Opens new HTTPS window to %s'), _('Sign In')),
'signOut' => sprintf(_('Opens new HTTPS window to %s'), _('Sign Out')),
'purchase' => sprintf(_('Opens new HTTPS window to %s'), _('Purchase Key')),
'upgrade' => sprintf(_('Opens new HTTPS window to %s'), _('Upgrade Key')),
],
'signInActions' => [
'resolve' => _('Sign In to resolve'),
'purchaseKey' => _('Sign In to Purchase Key'),
'purchaseKeyOrExtendTrial' => '@:upc.signInActions.purchaseKey or @:actions.extend',
],
],
'stateData' => [
'ENOKEYFILE' => [
'humanReadable' => _('No Keyfile'),
'heading' => _("Let's unleash your hardware").'!',
'message' => '<p>'._('Your server will not be usable until you purchase a Registration key or install a free 30-day Trial key').'. '._('A Trial key provides all the functionality of a Pro Registration key').'.</p><p>'._('Registration keys are bound to your USB Flash boot device serial number (GUID)').'. '._('Please use a high quality name brand device at least 1GB in size (min 4GB recommended)').'.</p><p>'._('Note: USB memory card readers are generally not supported because most do not present unique serial numbers').'.</p>',
],
'TRIAL' => [
'humanReadable' => _('Trial'),
'heading' => _('Thank you for choosing Unraid OS').'!',
'message' => _('Your Trial key includes all the functionality and device support of a Pro key').'. '._('After your Trial has reached expiration your server still functions normally until the next time you Stop the array or reboot your server').'. '._('At that point you may either purchase a license key or request a Trial extension').'.',
'_extraMsg' => sprintf(_('You have %s remaining on your Trial key'), '{parsedExpireTime}'),
],
'EEXPIRED' => [
'humanReadable' => _('Trial Expired'),
'heading' => _('Your Trial has expired'),
'message' => [
'base' => _('To continue using Unraid OS you may purchase a license key').'. ',
'extensionNotEligible' => _('You have used all your Trial extensions').'. @:stateData.EEXPIRED.message.base',
'extensionEligible' => '@:stateData.EEXPIRED.message.base '._('Alternately, you may request a Trial extension').'.',
],
],
'BASIC' => [
'humanReadable' => _('Basic'),
],
'PLUS' => [
'humanReadable' => _('Plus'),
],
'PRO' => [
'humanReadable' => _('Pro'),
],
'EGUID' => [
'humanReadable' => _('GUID Error'),
'error' => [
'heading' => _('Registration key / GUID mismatch'),
'message' => [
'default' => _('The license key file does not correspond to the USB Flash boot device').'. '._('Please copy the correct key file to the */config* directory on your USB Flash boot device or choose Purchase Key').'.',
'replacementIneligible' => _('Your Unraid registration key is ineligible for replacement as it has been replaced within the last 12 months').'.',
'replacementEligible' => _('The license key file does not correspond to the USB Flash boot device').'. '._('Please copy the correct key file to the */config* directory on your USB Flash boot device or choose Purchase Key or Replace Key').'.',
'blacklisted' => _('Your Unraid registration key is ineligible for replacement as it is blacklisted') . '.',
],
],
],
'ENOKEYFILE2' => [
'humanReadable' => _('Missing key file'),
'error' => [
'heading' => '@:stateData.ENOKEYFILE2.humanReadable',
'message' => _('It appears that your license key file is corrupted or missing').'. '._('The key file should be located in the */config* directory on your USB Flash boot device').'. '._('If you do not have a backup copy of your license key file you may install the Connect (beta) plugin to attempt to recover your key').'. '._('If this was an expired Trial installation, you may purchase a license key').'.',
],
],
'ETRIAL' => [
'humanReadable' => _('Invalid installation'),
'error' => [
'heading' => '@:stateData.ETRIAL.humanReadable',
'message' => _('It is not possible to use a Trial key with an existing Unraid OS installation').'. '._('You may purchase a license key corresponding to this USB Flash device to continue using this installation').'.',
],
],
'ENOKEYFILE1' => [
'humanReadable' => _('No Keyfile'),
'error' => [
'heading' => _('No USB flash configuration data'),
'message' => _('There is a problem with your USB Flash device'),
],
],
'ENOFLASH' => [
'humanReadable' => _('No Flash'),
'error' => [
'heading' => _('Cannot access your USB Flash boot device'),
'message' => _('There is a physical problem accessing your USB Flash boot device'),
],
],
'EGUID1' => [
'humanReadable' => _('Multiple License Keys Present'),
'error' => [
'heading' => '@:stateData.EGUID1.humanReadable',
'message' => _('There are multiple license key files present on your USB flash device and none of them correspond to the USB Flash boot device').'. '._('Please remove all key files except the one you want to replace from the */config* directory on your USB Flash boot device').'. '._('Alternately you may purchase a license key for this USB flash device').'. '._('If you want to replace one of your license keys with a new key bound to this USB Flash device please first remove all other key files first').'.',
],
],
'EBLACKLISTED' => [
'humanReadable' => _('BLACKLISTED'),
'error' => [
'heading' => _('Blacklisted USB Flash GUID'),
'message' => _('This USB Flash boot device has been blacklisted').'. '._('This can occur as a result of transferring your license key to a replacement USB Flash device, and you are currently booted from your old USB Flash device').'. '._('A USB Flash device may also be blacklisted if we discover the serial number is not unique this is common with USB card readers').'.',
],
],
'EBLACKLISTED1' => [
'humanReadable' =>'@:stateData.EBLACKLISTED.humanReadable',
'error' => [
'heading' => _('USB Flash device error'),
'message' => _('This USB Flash device has an invalid GUID').'. '._('Please try a different USB Flash device').'.',
],
],
'EBLACKLISTED2' => [
'humanReadable' => '@:stateData.EBLACKLISTED.humanReadable',
'error' => [
'heading' => _('USB Flash has no serial number'),
'message' => '@:stateData.EBLACKLISTED.error.message',
],
],
'ENOCONN' => [
'humanReadable' => _('Trial Requires Internet Connection'),
'error' => [
'heading' => _('Cannot validate Unraid Trial key'),
'message' => _('Your Trial key requires an internet connection').'. '._('Please check Settings > Network').'.',
],
],
'STALE' => [
'humanReadable' => _('Stale'),
'error' => [
'heading' => _('Stale Server'),
'message' => _('Please refresh the page to ensure you load your latest configuration'),
],
],
],
'regWizPopUp' => [
'regWiz' => _('Registration Wizard'),
'toHome' => _('To Registration Wizard Home'),
'continueTrial' => _('Continue Trial'),
'serverInfoToggle' => _('Toggle server info visibility'),
'youCanSafelyCloseThisWindow' => _('You can safely close this window'),
'automaticallyClosingIn' => sprintf(_('Auto closing in %s'), '{0}'),
'byeBye' => _('bye bye 👋'),
'browserWillSelfDestructIn' => sprintf(_('Browser will self destruct in %s'), '{0}'),
'closingPopUpMayLeadToErrors' => _('Closing this pop-up window while actions are being preformed may lead to unintended errors'),
'goBack' => _('Go Back'),
'shutDown' => _('Shut Down'),
'haveAccountSignIn' => _('Already have an account').'? '._('Sign In'),
'noAccountSignUp' => _('Do not have an account').'? '._('Sign Up'),
'willConnectYourServerToMyServers' => _('This will register your server with Connect <sup>BETA</sup>'),
'serverInfo' => [
'flash' => _('Flash'),
'product' => _('Product'),
'GUID' => _('GUID'),
'name' => _('Name'),
'ip' => _('IP'),
],
'forms' => [
'displayName' => _('Display Name'),
'emailAddress' => _('Email Address'),
'displayNameOrEmailAddress' => _('Display Name or Email Address'),
'displayNameRootMessage' => _('Use your Unraid.net credentials, not your local server credentials'),
'honeyPotCopy' => _('If you fill this field out then your email will not be sent'),
'fieldRequired' => _('This field is required'),
'submit' => _('Submit'),
'submitting' => _('Submitting'),
'notValid' => _('Form not valid'),
'cancel' => _('Cancel'),
'confirm' => _('Confirm'),
'createMyAccount' => _('Create My Account'),
'subject' => _('Subject'),
'password' => _('Password'),
'togglePasswordVisibility' => _('Toggle Password Visibility'),
'message' => _('Message'),
'confirmPassword' => _('Confirm Password'),
'passwordMustMatch' => _('Password confirmation must match'),
'passwordMinimum' => _('8 or more characters'),
'comments' => _('comments'),
'newsletterCopy' => _('Sign me up for the monthly Unraid newsletter').': '._('a digest of recent blog posts, community videos, popular forum threads, product announcements, and more'),
'terms' => [
'iAgree' => _('I agree to the'),
'text' => _('Terms of Use'),
],
],
'routes' => [
'extendTrial' => [
'heading' => [
'loading' => _('Extending Trial'),
'error' => _('Trial Extension Failed'),
],
'message' => _('Not ready to purchase?').'<br>'._('Receive an additional 15 days for your trial').'.',
],
'forgotPassword' => [
'heading' => _('Forgot Password'),
'subheading' => _("After resetting your password come back to the Registration Wizard pop-up window to Sign In and complete your server's registration"),
'resetPasswordNow' => _('Reset Password Now'),
'backToSignIn' => _('Back to Sign In'),
],
'signIn' => [
'heading' => [
'signIn' => _('Unraid.net Sign In'),
'recover' => _('Unraid.net Sign In to Recover Key'),
'replace' => _('Unraid.net Sign In to Replace Key'),
],
'form' => [
'replacementConditions' => [
'name' => _('Acknowledge Replacement Conditions'),
'label' => _('I acknowledge that replacing a license key results in permanently blacklisting the previous USB Flash GUID'),
],
'label' => [
'password' => [
'replace' => _('Unraid.net account password'),
],
],
],
],
'signUp' => [
'heading' => _('Create Unraid.net Account'),
],
'signOut' => [
'heading' => _('Unraid.net Sign Out'),
'warnings' => [
'remoteAccessDisabled' => _('Remote access will be disabled'),
'remoteAccessInaccessible' => sprintf(_('You will no longer have access to this server using <abbr title="%s" class="italic">this url</abbr>'), '{0}'),
'disablingFlashBackup' => _('Automated flash backups will be disabled until you sign in again'),
'downloadFlashBackup' => _('Download latest backup from Connect Dashboard before signing out'),
],
],
'success' => [
'heading' => [
'username' => sprintf(_('Hi %s'), '{0}'),
'default' => _('Success'),
],
'subheading' => [
'extention' => _('Your trial will expire in 15 days'),
'newTrial' => _('Your trial will expire in 30 days'),
],
'signIn' => [
'tileTitle' => [
'actionFail' => sprintf(_('%s was not signed in to your Unraid.net account'), '{0}'),
'actionSuccess' => sprintf(_('%s is signed in to your Unraid.net account'), '{0}'),
'loading' => sprintf(_('Signing in %s to Unraid.net account'), '{0}'),
],
],
'signOut' => [
'tileTitle' => [
'actionFail' => sprintf(_('%s was not signed out of your Unraid.net account'), '{0}'),
'actionSuccess' => sprintf(_('%s was signed out of your Unraid.net account'), '{0}'),
'loading' => sprintf(_('Signing out %s from Unraid.net account'), '{0}'),
],
],
'keys' => [
'trial' => _('Trial'),
'basic' => _('Basic'),
'plus' => _('Plus'),
'pro' => _('Pro'),
],
'extended' => sprintf(_('%s Key Extended'), '{0}'),
'recovered' => sprintf(_('%s Key Recovered'), '{0}'),
'replaced' => sprintf(_('%s Key Replaced'), '{0}'),
'created' => sprintf(_('%s Key Created'), '{0}'),
'install' => [
'loading' => sprintf(_('Installing %s Key'), '{0}'),
'error' => sprintf(_('%s Key Install Error'), '{0}'),
'success' => sprintf(_('Installed %s Key'), '{0}'),
'manualInstructions' => _('To manually install the key paste the key file url into the Key file URL field on the webGUI Tools > Registration page and then click Install Key') . '.',
'copyFail' => _('Unable to copy'),
'copySuccess' => _('Copied key url') . '!',
'copyButton' => _('Copy Key URL'),
'copyBeforeClose' => _('Please copy the Key URL before closing this window'),
],
'timeout' => sprintf(_('Communication with %s has timed out'), '{0}'),
'loading1' => _('Please keep this window open'),
'loading2' => _('Still working our magic'),
'countdown' => [
'success' => [
'prefix' => sprintf(_('Auto closing in %s'), '{0}'),
'text' => _('You can safely close this window'),
],
'error' => [
'prefix' => sprintf(_('Auto redirecting in %s'), '{0}'),
'text' => _('Back to Registration Home'),
'complete' => _('Back in a flash ⚡️'),
],
],
],
'troubleshoot' => [
'heading' => [
'default' => _('Troubleshoot'),
'success' => _('Thank you for contacting Unraid'),
],
'subheading' => [
'default' => _("Forgot what Unraid.net account you used").'? '._("Have a USB flash device that already has an account associated with it").'? '._("Just give us the details about what happened and we'll do our best to get you up and running again").'.',
'success' => _('We have received your e-mail and will respond in the order it was received').'. '._('While we strive to respond to all requests as quickly as possible please allow for up to 3 business days for a response').'.',
],
'relevantServerData' => _('Your USB Flash GUID and other relevant server data will also be sent'),
],
'verifyEmail' => [
'heading' => _('Verify Email'),
'subheading' => sprintf(_('We have sent a verifcation email to %s'), '{0}'),
'form' => [
'verificationCode' => _('verification code'),
'verifyCode' => _('Paste or Enter code'),
],
'noCode' => _("Didn't get code?"),
],
'verifyEmailResend' => [
'heading' => _('Resend Email Verification Code'),
'goBack' => _("Have the code now? Go Back"),
'resend' => _("Resend Code"),
],
'whatIsMyServers' => [
'heading' => _('What is Unraid.net?'),
'subheading' => _('Expand your servers capabilities'),
'copy' => _('With an Unraid.net account you can start using Connect (beta) which gives you access to the following features:'),
'features' => [
"dynamicRemoteAccess" => [
'heading' => _('Dynamic Remote Access'),
'copy' => _('Toggle on/off server accessibility with dynamic remote access').'. '._('Automatically turn on UPnP and open a random WAN port on your router at the click of a button and close off access in seconds').'.',
],
"manageWithinConnect" => [
'heading' => _('Manage Your Server Within Connect'),
'copy' => _('Servers equipped with a myunraid.net certificate can be managed directly from within the Connect web UI').'. '._('Manage multiple servers from your phone, tablet, laptop, or PC in the same browser window').'.',
],
"deepLinking" => [
'heading' => _('Deep Linking'),
'copy' => _('The Connect dashboard links to relevant sections of the webgui, allowing quick access to those settings and server sections').'.',
],
"onlineFlashBackup" => [
'heading' => _('Online Flash Backup'),
'copy' => _('Never ever be left without a backup of your config').'. '._('If you need to change flash drives, generate a backup from Connect and be up and running in minutes').'.',
],
"realTimeMonitoring" => [
'heading' => _('Real-time Monitoring'),
'copy' => _("Get an overview of your server's state, storage space, apps and VMs status, and more").'.',
],
"customizableDashboardTitles" => [
'heading' => _('Customizable Dashboard Tiles'),
'copy' => _("Set custom server tiles how you like and automatically display your server's banner image on your Connect Dashboard").'.',
],
"licenseManagement" => [
'heading' => _('License Management'),
'copy' => _('Manage your license keys at any time via the My Keys section').'.',
],
'plusMore' => [
'heading' => _('Plus more on the way'),
'copy' => _('All you need is an active internet connection, an Unraid.net account, and the Connect plugin').'. '._('Get started by installing the plugin') . '.',
],
],
],
'replaceKey' => [
'subheading' => [
'registered' => 'A record of your replacement will be sent to your Unraid.net account email address',
'notRegistered' => 'A record of your replacement will be sent to this email',
],
],
'notFound' => [
'subheading' => _('Page Not Found'),
],
'notAllowed' => [
'subheading' => _('Page Not Allowed'),
],
],
],
'wanIpCheck' => [
'checking' => _('Checking Wan IPs'),
'match' => sprintf(_('Remark: your WAN IPv4 is **%s**'), '{0}'),
'mismatch' => sprintf(_("Remark: Unraid's WAN IPv4 **%1s** does not match your client's WAN IPv4 **%2s**"), '{0}', '{1}').'. '._('This may indicate a complex network that will not work with this Remote Access solution').'. '._('Ignore this message if you are currently connected via Remote Access or VPN').'.',
'resolveError' => _('DNS issue, unable to resolve wanip4.unraid.net'),
],
'upcTrigger' => [
'upgrade' => _('To support more storage devices as your server grows click the *Open Dropdown* button').'.',
'default' => _('Key management is done via the dropdown in the top right of the webGUI on every page').'.',
'open' => _('Open Dropdown'),
],
'yargYePirate' => _('Oh no! Are you pirating Unraid OS?<br>Are you ready to buy a real license?'),
'keyFileNotValid' => _('Key file not valid'),
'installFailed' => [
'heading' => _('Connect plugin install failed'),
'message' => _('The Connect plugin install is incomplete').'. '._('Please uninstall and reinstall the Connect plugin').'. '._('Be sure to let the install complete before you close the window').'.',
],
"downloadUnraidApiLogs" => _('Download unraid-api Logs'),
"download" => _('Download'),
"pleaseWait" => _('Please wait…')
],
];
// note: $myservers variable defined in myservers1.php, by parsing myservers.cfg
$configErrorEnum = [ // used to map $var['configValid'] value to mimic unraid-api's `configError` ENUM
"error" => 'UNKNOWN_ERROR',
"invalid" => 'INVALID',
"nokeyserver" => 'NO_KEY_SERVER',
"withdrawn" => 'WITHDRAWN',
];
$nginx = parse_ini_file('/var/local/emhttp/nginx.ini');
$plgInstalled = '';
if (!file_exists('/var/lib/pkgtools/packages/dynamix.unraid.net') && !file_exists('/var/lib/pkgtools/packages/dynamix.unraid.net.staging')) {
$plgInstalled = ''; // base OS only, plugin not installed • show ad for plugin
} else {
// plugin is installed but if the unraid-api file doesn't fully install it's a failed install
if (file_exists('/var/lib/pkgtools/packages/dynamix.unraid.net')) $plgInstalled = 'dynamix.unraid.net.plg';
if (file_exists('/var/lib/pkgtools/packages/dynamix.unraid.net.staging')) $plgInstalled = 'dynamix.unraid.net.staging.plg';
// plugin install failed • append failure detected so we can show warning about failed install via UPC
if (!file_exists('/usr/local/sbin/unraid-api')) $plgInstalled = $plgInstalled . '_installFailed';
}
// read flashbackup ini file
$flashbackup_ini = '/var/local/emhttp/flashbackup.ini';
$flashbackup_status = (file_exists($flashbackup_ini)) ? @parse_ini_file($flashbackup_ini) : [];
$serverstate = [ // feeds server vars to Vuex store in a slightly different array than state.php
"avatar" => (!empty($myservers['remote']['avatar']) && $plgInstalled) ? $myservers['remote']['avatar'] : '',
"config" => [
'valid' => $var['configValid'] === 'yes',
'error' => $var['configValid'] !== 'yes'
? (array_key_exists($var['configValid'], $configErrorEnum) ? $configErrorEnum[$var['configValid']] : 'UNKNOWN_ERROR')
: null,
],
"deviceCount" => $var['deviceCount'],
"email" => $myservers['remote']['email'] ?? '',
"extraOrigins" => explode(',', $myservers['api']['extraOrigins']??''),
"flashproduct" => $var['flashProduct'],
"flashvendor" => $var['flashVendor'],
"flashBackupActivated" => empty($flashbackup_status['activated']) ? '' : 'true',
"guid" => $var['flashGUID'],
"hasRemoteApikey" => !empty($myservers['remote']['apikey']),
"internalip" => ipaddr(),
"internalport" => $_SERVER['SERVER_PORT'],
"keyfile" => empty($var['regFILE'])? "" : str_replace(['+','/','='], ['-','_',''], trim(base64_encode(@file_get_contents($var['regFILE'])))),
"osVersion" => $var['version'],
"plgVersion" => $plgversion = file_exists('/var/log/plugins/dynamix.unraid.net.plg')
? trim(@exec('/usr/local/sbin/plugin version /var/log/plugins/dynamix.unraid.net.plg 2>/dev/null'))
: ( file_exists('/var/log/plugins/dynamix.unraid.net.staging.plg')
? trim(@exec('/usr/local/sbin/plugin version /var/log/plugins/dynamix.unraid.net.staging.plg 2>/dev/null'))
: 'base-'.$var['version'] ),
"plgInstalled" => $plgInstalled,
"protocol" => $_SERVER['REQUEST_SCHEME'],
"reggen" => (int)$var['regGen'],
"regGuid" => $var['regGUID'],
"registered" => (!empty($myservers['remote']['username']) && $plgInstalled),
"servername" => $var['NAME'],
"site" => $_SERVER['REQUEST_SCHEME']."://".$_SERVER['HTTP_HOST'],
"state" => strtoupper(empty($var['regCheck']) ? $var['regTy'] : $var['regCheck']),
"ts" => time(),
"username" => (!empty($myservers['remote']['username']) && $plgInstalled) ? $myservers['remote']['username'] : '',
"wanFQDN" => $nginx['NGINX_WANFQDN'] ?? '',
];
/** @TODO - prop refactor needed. The issue is because the prop names share the same name as the vuex store variables
* if we remove the props and deployed a UPC that doesn't rely on props anymore uses that don't have an updated version
* of this file will have a non-working UPC.
* apikey
* apiVersion
* csrf
* expiretime
* hideMyServers
* plgPath
* regWizTime
* sendCrashInfo
* serverdesc
* servermodel
* serverupdate
* uptime
*/
?>
<unraid-user-profile
apikey="<?=$myservers['upc']['apikey'] ?? ''?>"
api-version="<?=$myservers['api']['version'] ?? ''?>"
banner="<?=$display['banner'] ?? ''?>"
bgcolor="<?=($backgnd) ? '#'.$backgnd : ''?>"
csrf="<?=$var['csrf_token']?>"
displaydesc="<?=($display['headerdescription']??''!='no') ? 'true' : ''?>"
expiretime="<?=1000*($var['regTy']=='Trial'||strstr($var['regTy'],'expired')?$var['regTm2']:0)?>"
hide-my-servers="<?=$plgInstalled ? '' : 'yes' ?>"
locale="<?=($_SESSION['locale']) ? $_SESSION['locale'] : 'en_US'?>"
locale-messages="<?=rawurlencode(json_encode($upc_translations, JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE))?>"
metacolor="<?=($display['headermetacolor']??'') ? '#'.$display['headermetacolor'] : ''?>"
plg-path="dynamix.my.servers"
reg-wiz-time="<?=$myservers['remote']['regWizTime'] ?? ''?>"
serverdesc="<?=$var['COMMENT']?>"
servermodel="<?=$var['SYS_MODEL']?>"
serverstate="<?=rawurlencode(json_encode($serverstate, JSON_UNESCAPED_SLASHES))?>"
show-banner-gradient="<?=$display['showBannerGradient'] ?? 'yes'?>"
textcolor="<?=($header) ? '#'.$header : ''?>"
theme="<?=$display['theme']?>"
uptime="<?=1000*(time() - round(strtok(exec("cat /proc/uptime"),' ')))?>"
></unraid-user-profile>
<!-- /myservers2 -->
@@ -358,6 +358,56 @@ SOFTWARE.
backo2
MIT
base64-js
MIT
The MIT License (MIT)
Copyright (c) 2014 Jameson Little
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
buffer
MIT
The MIT License (MIT)
Copyright (c) Feross Aboukhadijeh, and other contributors.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
clipboard
MIT
MIT License
@@ -383,6 +433,29 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
core-js
MIT
Copyright (c) 2014-2023 Denis Pushkarev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
css-loader
MIT
Copyright JS Foundation and other contributors
@@ -1236,6 +1309,21 @@ Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
ieee754
BSD-3-Clause
Copyright 2008 Fair Oaks Labs, Inc.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
is-secret
MIT
The MIT License (MIT)
@@ -1261,6 +1349,79 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
isarray
MIT
lodash
MIT
Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
Based on Underscore.js, copyright Jeremy Ashkenas,
DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>
This software consists of voluntary contributions made by many
individuals. For exact contribution history, see the revision history
available at https://github.com/lodash/lodash
The following license applies to all parts of this software except as
documented below:
====
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
====
Copyright and related rights for sample code are waived via CC0. Sample
code is defined as all source code displayed within the prose of the
documentation.
CC0: http://creativecommons.org/publicdomain/zero/1.0/
====
Files located in the node_modules and vendor directories are externally
maintained libraries used by this software which have their own
licenses; we recommend you read them, as their terms may differ from the
terms above.
lru-cache
ISC
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
node-libs-browser
MIT
(The MIT License)
@@ -1334,6 +1495,24 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
post-robot
Apache 2.0
/*───────────────────────────────────────────────────────────────────────────*\
│ Copyright (C) 2016 PayPal │
│ │
│ │
│ Licensed under the Apache License, Version 2.0 (the "License"); you may │
│ not use this file except in compliance with the License. You may obtain │
│ a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 │
│ │
│ Unless required by applicable law or agreed to in writing, software │
│ distributed under the License is distributed on an "AS IS" BASIS, │
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
│ See the License for the specific language governing permissions and │
│ limitations under the License. │
\*───────────────────────────────────────────────────────────────────────────*/
semver
ISC
The ISC License
@@ -1719,6 +1898,25 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
yallist
ISC
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
zen-observable
MIT
Copyright (c) 2018 zenparsing (Kevin Smith)
File diff suppressed because one or more lines are too long
@@ -127,10 +127,17 @@ function loadlist(id,check) {
var cmd = id.split(':');
var tr = 'tr#'+cmd[0].replace(/[\. _]/g,'');
switch (cmd[1]) {
case 'update' :
case 'return' : updateInfo(data[0]); break;
case 'remove' : list.find(tr).remove(); break;
case 'install': if (!list.find(tr).length) list.append(data[0]); loadlist(null,1); break;
case 'update':
case 'return':
updateInfo(data[0]);
if (data[1] > 1) $('#updateall').show(); else $('#updateall').hide();
break;
case 'remove':
list.find(tr).remove();
break;
case 'install':
if (!list.find(tr).length) list.append(data[0]); loadlist(null,1);
break;
}
} else {
updateInfo(data[0]);
@@ -1,6 +1,6 @@
<?PHP
/* Copyright 2005-2020, Lime Technology
* Copyright 2012-2020, Bergware International.
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
@@ -12,12 +12,15 @@
?>
<?
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/webGui/include/Wrappers.php";
require_once "$docroot/webGui/include/Secure.php";
// add translations
$_SERVER['REQUEST_URI'] = 'plugins';
require_once "$docroot/webGui/include/Translations.php";
require_once "$docroot/webGui/include/Secure.php";
exec("mv -f /boot/previous/* /boot");
$version = unscript($_GET['version']??'');
$version = unscript(_var($_GET,'version'));
file_put_contents("$docroot/plugins/unRAIDServer/README.md","**"._('DOWNGRADE TO VERSION')." $version**");
?>
@@ -0,0 +1,36 @@
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/webGui/include/Wrappers.php";
extract(parse_plugin_cfg('dynamix',true));
// add translations
$_SERVER['REQUEST_URI'] = 'plugins';
require_once "$docroot/webGui/include/Translations.php";
$valid = ['/var/tmp/','/tmp/plugins/'];
$good = false;
?>
<body style="margin:14px 10px">
<?
if ($file = realpath(unscript(_var($_GET,'file')))) {
foreach ($valid as $check) if (strncmp($file,$check,strlen($check))===0) $good = true;
if ($good && pathinfo($file,PATHINFO_EXTENSION)=='txt') echo Markdown(file_get_contents($file));
} else {
echo Markdown("*"._('No release notes available')."!*");
}
?>
<br><div style="text-align:center"><input type="button" value="<?=_('Done')?>" onclick="top.Shadowbox.close()"></div>
</body>
@@ -12,19 +12,20 @@
?>
<?
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
// add translations
$_SERVER['REQUEST_URI'] = 'plugins';
require_once "$docroot/webGui/include/Translations.php";
require_once "$docroot/webGui/include/Helpers.php";
require_once "$docroot/plugins/dynamix.plugin.manager/include/PluginHelpers.php";
$system = unscript($_GET['system']??'');
$branch = unscript($_GET['branch']??'');
$audit = unscript($_GET['audit']??'');
$check = unscript($_GET['check']??'');
$cmd = unscript($_GET['cmd']??'');
$init = unscript($_GET['init']??'');
// add translations
$_SERVER['REQUEST_URI'] = 'plugins';
require_once "$docroot/webGui/include/Translations.php";
$system = unscript(_var($_GET,'system'));
$branch = unscript(_var($_GET,'branch'));
$audit = unscript(_var($_GET,'audit'));
$check = unscript(_var($_GET,'check'));
$cmd = unscript(_var($_GET,'cmd'));
$init = unscript(_var($_GET,'init'));
$empty = true;
$install = false;
$updates = 0;
@@ -32,6 +33,7 @@ $alerts = '/tmp/plugins/my_alerts.txt';
$builtin = ['unRAIDServer'];
$plugins = "/var/log/plugins/*.plg";
$ncsi = null; // network connection status indicator
$Unraid = parse_ini_file("/etc/unraid-version");
if ($cmd=='alert') {
// signal alert message yer or no
@@ -41,7 +43,7 @@ if ($cmd=='alert') {
if ($cmd=='pending') {
// prepare pending status for multi operations
foreach (explode('*',$_GET['plugin']) as $plugin) file_put_contents("/tmp/plugins/pluginPending/$plugin",'multi');
foreach (explode('*',_var($_GET,'plugin')) as $plugin) file_put_contents("/tmp/plugins/pluginPending/$plugin",'multi');
die();
}
@@ -162,7 +164,12 @@ foreach (glob($plugins,GLOB_NOSORT) as $plugin_link) {
$latest = plugin('version',$filename);
if ($os ? version_compare($latest,$version,'>') : strcmp($latest,$version) > 0) {
$version .= "<br><span class='red-text'>$latest</span>";
$status = make_link("update",basename($plugin_file));
$error = null;
if ( ! $os && (version_compare(plugin("min",$filename,$error) ?: "1.0",$Unraid['version'],">") || version_compare(plugin("max",$filename,$error) ?: "999.9.9",$Unraid['version'],"<") ) ) {
$status = "<span class='warning'><i class='fa fa-exclamation-triangle' aria-hidden='true'></i> "._("Update Incompatible")."</span>";
} else {
$status = make_link("update",basename($plugin_file));
}
$changes_file = $filename;
if (!$os) $updates++;
} else {
@@ -195,5 +202,5 @@ foreach (glob($plugins,GLOB_NOSORT) as $plugin_link) {
}
}
if ($empty) echo "<tr><td colspan='6' style='text-align:center;padding-top:12px'><i class='fa fa-check-square-o icon'></i> "._('No plugins installed')."</td><tr>";
if (!$init && !$os) echo "\0".$updates;
if (!$init && !($os??false)) echo "\0".$updates;
?>
@@ -1,7 +1,7 @@
#!/usr/bin/php -q
<?PHP
/* Copyright 2005-2022, Lime Technology
* Copyright 2012-2022, Bergware International.
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
@@ -15,12 +15,11 @@
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/webGui/include/Wrappers.php";
// Get the webGui configuration preferences
extract(parse_plugin_cfg('dynamix',true));
// add translations
$_SERVER['REQUEST_URI'] = 'plugins';
$login_locale = $display['locale'];
$login_locale = _var($display,'locale');
require_once "$docroot/webGui/include/Translations.php";
$nchan = $argv[$argc-1] == 'nchan'; // console or nchan output
@@ -46,7 +45,7 @@ function write(...$messages){
}
write(_("Checking connectivity")." ...\n");
if (exec("wget --spider --no-check-certificate -nv -T10 -t1 https://www.msftncsi.com/ncsi.txt 2>&1|grep -o 'OK'")) {
if (exec("wget --spider --no-check-certificate -nv -T10 -t1 https://www.msftncsi.com/ncsi.txt 2>&1|grep -om1 'OK'")) {
$check = popen('plugin checkall','r');
while (!feof($check)) write(fgets($check));
pclose($check);
@@ -1,7 +1,7 @@
#!/usr/bin/php -q
<?PHP
/* Copyright 2005-2022, Lime Technology
* Copyright 2012-2022, Bergware International.
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
@@ -16,18 +16,23 @@ $docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/webGui/include/Wrappers.php";
require_once "$docroot/plugins/dynamix.plugin.manager/include/PluginHelpers.php";
$var = parse_ini_file('/var/local/emhttp/var.ini');
$unraid = parse_plugin_cfg('dynamix', true);
extract(parse_plugin_cfg('dynamix', true));
// Multi-language support
$_SERVER['REQUEST_URI'] = "scripts";
$login_locale = $unraid['display']['locale'];
$login_locale = _var($display,'locale');
require_once "$docroot/webGui/include/Translations.php";
$var = @parse_ini_file('/var/local/emhttp/var.ini') ?: [];
function apos($text) {
// So that "&apos;" doesn't show up in email
return str_replace("&apos;","'",$text);
}
$server = strtoupper($var['NAME']);
$output = $unraid['notify']['language_notify'];
$notify = "$docroot/webGui/scripts/notify";
$output = _var($notify,'language_notify');
$script = "$docroot/webGui/scripts/notify";
$tmp = '/tmp/plugins';
$plugins = '/var/log/plugins';
@@ -37,13 +42,13 @@ foreach (glob("/$tmp/lang-*.xml", GLOB_NOSORT) as $file) {
$lang = language('LanguageLocal', $file);
$new = language('Version', $file);
$old = language('Version', "$plugins/$name");
// silently suppress bad download of XML file
if (strcmp($new, $old) > 0) {
// So that "&apos;" doesn't show up in email
$event = str_replace("&apos;","'",_("Language")." - $lang [$new]");
$subject = str_replace("&apos;","'",sprintf(_("Notice [%s] - Version update %s"),$server,$new));
$description = str_replace("&apos;","'",sprintf(_("A new version of %s is available"),$lang));
exec("$notify -e ".escapeshellarg($event)." -s ".escapeshellarg($subject)." -d ".escapeshellarg($description)." -i ".escapeshellarg("normal $output")." -l '/Apps' -x");
$event = apos(_("Language")." - $lang [$new]");
$subject = apos(sprintf(_("Notice [%s] - Version update %s"),$server,$new));
$description = apos(sprintf(_("A new version of %s is available"),$lang));
exec("$script -e ".escapeshellarg($event)." -s ".escapeshellarg($subject)." -d ".escapeshellarg($description)." -i ".escapeshellarg("normal $output")." -l '/Apps' -x");
}
}
exit(0);
@@ -1,7 +1,7 @@
#!/usr/bin/php -q
<?PHP
/* Copyright 2005-2022, Lime Technology
* Copyright 2012-2022, Bergware International.
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
@@ -12,8 +12,8 @@
*/
?>
<?
$method = $argv[1];
$plugins = explode('*',$argv[2]);
$method = $argv[1]??'';
$plugins = explode('*',$argv[2]??'');
$nchan = $argv[$argc-1] == 'nchan'; // console or nchan output
$call = ['plg' => 'plugin', 'xml' => 'language', '' => 'language'];
@@ -38,7 +38,7 @@ function write(...$messages){
}
foreach ($plugins as $plugin) {
if (!$plugin || (!$cmd = $call[pathinfo($plugin)['extension']])) continue;
if (!$plugin || (!$cmd = $call[pathinfo($plugin,PATHINFO_EXTENSION)])) continue;
$line = '';
$pluginArg = $method == "update" ? basename($plugin) : $plugin;
$run = popen("$cmd $method $pluginArg",'r');
@@ -238,9 +238,9 @@ function download($url, $name, &$error, $write=true) {
if ($file = popen("wget --compression=auto --no-cache --progress=dot --retry-connrefused --prefer-family=IPv4 --timeout=10 --tries=$tries --waitretry=$tries -O $name $url 2>&1", 'r')) {
if ($write) write("plugin: downloading: $plg ...\r");
$level = -1;
while (($line = fgets($file)) !== false) {
if (preg_match('/\d+%/', $line, $matches)) {
$percentage = substr($matches[0],0,-1);
while (($line = fgets($file)) !== false) {
if (preg_match('/ \d+% /', $line, $matches)) {
$percentage = substr(trim($matches[0]),0,-1);
if ($percentage > $level) {
if ($write) write("plugin: downloading: $plg ... $percentage%\r");
$level = $percentage;
@@ -477,7 +477,7 @@ function plugin($method, $plugin_file, &$error) {
unlink($name);
}
if ($retval != 0) {
$error = "run failed: $command";
$error = "run failed: '$command' returned $retval";
return false;
}
}
@@ -1,7 +1,7 @@
#!/usr/bin/php -q
<?PHP
/* Copyright 2005-2022, Lime Technology
* Copyright 2012-2022, Bergware International.
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
@@ -17,23 +17,25 @@ $docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/webGui/include/Wrappers.php";
require_once "$docroot/plugins/dynamix.plugin.manager/include/PluginHelpers.php";
$var = parse_ini_file('/var/local/emhttp/var.ini');
$unraid = parse_plugin_cfg('dynamix', true);
extract(parse_plugin_cfg('dynamix',true));
// Multi-language support
$_SERVER['REQUEST_URI'] = "scripts";
$login_locale = $unraid['display']['locale'];
$login_locale = _var($display,'locale');
require_once "$docroot/webGui/include/Translations.php";
$var = @parse_ini_file('/var/local/emhttp/var.ini') ?: [];
function apos($text) {
// So that "&apos;" doesn't show up in email
return str_replace("&apos;","'",$text);
}
$current = parse_ini_file('/etc/unraid-version');
$server = strtoupper($var['NAME']);
$output = $unraid['notify']['plugin'];
$server = strtoupper(_var($var,'NAME','tower'));
$output = _var($notify,'plugin');
$builtin = ['unRAIDServer','unRAIDServer-'];
$notify = "$docroot/webGui/scripts/notify";
$script = "$docroot/webGui/scripts/notify";
$tmp = '/tmp/plugins';
$plugins = '/var/log/plugins';
@@ -50,7 +52,7 @@ foreach (glob("/$tmp/*.plg", GLOB_NOSORT) as $file) {
$event = apos(_("Plugin")." - $name [$new]");
$subject = apos(sprintf(_("Notice [%s] - Version update %s"),$server,$new));
$description = apos(sprintf(_("A new version of %s is available"),$name));
exec("$notify -e ".escapeshellarg($event)." -s ".escapeshellarg($subject)." -d ".escapeshellarg($description)." -i ".escapeshellarg("normal $output")." -l '/Plugins' -x");
exec("$script -e ".escapeshellarg($event)." -s ".escapeshellarg($subject)." -d ".escapeshellarg($description)." -i ".escapeshellarg("normal $output")." -l '/Plugins' -x");
}
}
exit(0);
@@ -1,7 +1,7 @@
#!/usr/bin/php -q
<?PHP
/* Copyright 2005-2022, Lime Technology
* Copyright 2012-2022, Bergware International.
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
@@ -19,10 +19,10 @@ extract(parse_plugin_cfg('dynamix',true));
// add translations
$_SERVER['REQUEST_URI'] = 'plugins';
$login_locale = $display['locale'];
$login_locale = _var($display,'locale');
require_once "$docroot/webGui/include/Translations.php";
$file = realpath($argv[1] ?? '');
$file = realpath($argv[1]??'');
$valid = ['/var/tmp/','/tmp/plugins/'];
$good = false;
@@ -1,7 +1,7 @@
#!/usr/bin/php -q
<?PHP
/* Copyright 2005-2020, Lime Technology
* Copyright 2012-2020, Bergware International.
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
@@ -12,32 +12,34 @@
*/
?>
<?
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/webGui/include/Wrappers.php";
require_once "$docroot/plugins/dynamix.plugin.manager/include/PluginHelpers.php";
// Multi-language support
if (!function_exists('_')) {
function _($text) {return $text;}
}
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/webGui/include/Wrappers.php";
require_once "$docroot/plugins/dynamix.plugin.manager/include/PluginHelpers.php";
extract(parse_plugin_cfg('dynamix', true));
$var = parse_ini_file('/var/local/emhttp/var.ini');
$current = parse_ini_file('/etc/unraid-version');
$unraid = parse_plugin_cfg('dynamix', true);
$notify = "$docroot/webGui/scripts/notify";
$server = strtoupper($var['NAME']);
$output = $unraid['notify']['plugin'];
$var = @parse_ini_file('/var/local/emhttp/var.ini') ?: [];
$script = "$docroot/webGui/scripts/notify";
$server = strtoupper(_var($var,'NAME','server'));
$output = _var($notify,'plugin');
$builtin = ['unRAIDServer'];
foreach ($builtin as $name) {
$plg = "$name.plg";
$plg = "$name.plg";
plugin('check',$plg);
$file = "/tmp/plugins/$plg";
$old = plugin('version', "/var/log/plugins/$plg");
$new = plugin('version', $file);
$old = plugin('version', "/var/log/plugins/$plg");
$new = plugin('version', $file);
// silently suppress bad download of PLG file
if (version_compare($new,$old,'>')) {
exec("$notify -e ".escapeshellarg("System - $name [$new]")." -s ".escapeshellarg("Notice [$server] - Version update $new")." -d ".escapeshellarg("A new version of $name is available")." -i ".escapeshellarg("normal $output")." -l '/Tools/Update' -x");
exec("$script -e ".escapeshellarg("System - $name [$new]")." -s ".escapeshellarg("Notice [$server] - Version update $new")." -d ".escapeshellarg("A new version of $name is available")." -i ".escapeshellarg("normal $output")." -l '/Tools/Update' -x");
}
}
exit(0);
@@ -5,9 +5,9 @@ Cond="is_file('/var/run/libvirt/libvirtd.pid')"
Markdown="false"
---
<?PHP
/* Copyright 2005-2021, Lime Technology
* Copyright 2015-2021, Derek Macias, Eric Schultz, Jon Panozzo.
* Copyright 2012-2021, Bergware International.
/* Copyright 2005-2023, Lime Technology
* Copyright 2015-2023, Derek Macias, Eric Schultz, Jon Panozzo.
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
@@ -19,6 +19,7 @@ Markdown="false"
?>
<?
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
$cpus = cpu_list();
$hover = in_array($theme,['white','azure']) ? 'rgba(0,0,0,0.1)' : 'rgba(255,255,255,0.1)';
$bgcolor = in_array($theme,['white','azure']) ? '#f2f2f2' : '#1c1c1c';
@@ -56,10 +57,10 @@ function vsize($size,$expand=true) {
}
}
$uuid = unscript($_GET['uuid']??'');
$subaction = $_GET['subaction'] ?? false;
$uuid = unscript(_var($_GET,'uuid'));
$subaction = _var($_GET,'subaction');
if (isset($_GET['refresh'])) {
$vm = unscript($_GET['name']);
$vm = unscript(_var($_GET,'name'));
if ($lv->domain_is_active($vm)) {
echo "<meta http-equiv='refresh' content='5; url=/VMs?name=$vm&amp;refresh=true'>";
$msg = "Waiting for $vm to shutdown...";
@@ -78,7 +79,7 @@ if ($subaction) {
$msg = "Error: disk capacity has to be greater than {$_GET['oldcap']}";
}
} elseif ($subaction == 'disk-remove') {
$msg = $lv->domain_disk_remove($vm, $_GET['dev'])
$msg = $lv->domain_disk_remove($vm,_var($_GET,'dev'))
? "$vm disk has been removed"
: "Error: ".$lv->get_last_error();
} elseif ($subaction == 'snap-create') {
@@ -86,15 +87,15 @@ if ($subaction) {
? "Snapshot for $vm has been created"
: "Error: ".$lv->get_last_error();
} elseif ($subaction == 'snap-delete') {
$msg = $lv->domain_snapshot_delete($vm, $_GET['snap'])
$msg = $lv->domain_snapshot_delete($vm,_var($_GET,'snap'))
? "Snapshot for $vm has been deleted"
: "Error: ".$lv->get_last_error();
} elseif ($subaction == 'snap-revert') {
$msg = $lv->domain_snapshot_revert($vm, $_GET['snap'])
$msg = $lv->domain_snapshot_revert($vm,_var($_GET,'snap'))
? "$vm has been reverted"
: "Error: ".$lv->get_last_error();
} elseif ($subaction == 'snap-desc') {
$msg = $lv->snapshot_set_metadata($vm, $_GET['snap'], $_POST['snapdesc'])
$msg = $lv->snapshot_set_metadata($vm,_var($_GET,'snap'),_var($_POST,'snapdesc'))
? "Snapshot description for $vm has been saved"
: "Error: ".$lv->get_last_error();
}
@@ -119,22 +120,21 @@ div.four label{float:left;display:table-cell;width:25%}
div.four label:nth-child(4n+4){float:none;clear:both}
div.four label.cpu1{width:32%}
div.four label.cpu2{width:26%}
table.domdisk thead th:nth-child(1){width:76%;}
table.domdisk thead th:nth-child(2){width:8%;}
table.domdisk thead th:nth-child(3){width:8%;}
table.domdisk thead th:nth-child(3){width:8%;}
div.template,div#dialogWindow,input#upload{display:none}
table.domdisk thead tr th:nth-child(1){width:56%!important}
table.domdisk thead tr th:nth-child(n+2){width:8%!important}
table.domdisk thead tr th:nth-child(1){padding-left:72px}
table.domdisk tbody tr td:nth-child(1){padding-left:72px}
table.domdisk tbody tr:nth-child(even){background-color:transparent!important}
table.domdisk tbody tr:nth-child(4n-1){background-color:transparent!important}
.fileTree{background:<?=$bgcolor?>;width:500px;max-height:320px;overflow-y:scroll;overflow-x:hidden;position:absolute;z-index:100;display:none}
div.template,div#dialogWindow,input#upload{display:none}
i.mover{margin-right:8px;display:none}
#resetsort{margin-left:12px;display:inline-block;width:32px}
.fileTree{background:<?=$bgcolor?>;width:500px;max-height:320px;overflow-y:scroll;overflow-x:hidden;position:absolute;z-index:100;display:none}
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset button[disabled]{cursor:default;color:#808080;background:-webkit-gradient(linear,left top,right top,from(#404040),to(#808080)) 0 0 no-repeat,-webkit-gradient(linear,left top,right top,from(#404040),to(#808080)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#404040),to(#404040)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#808080),to(#808080)) 100% 100% no-repeat;background:linear-gradient(90deg,#404040 0,#808080) 0 0 no-repeat,linear-gradient(90deg,#404040 0,#808080) 0 100% no-repeat,linear-gradient(0deg,#404040 0,#404040) 0 100% no-repeat,linear-gradient(0deg,#808080 0,#808080) 100% 100% no-repeat;background-size:100% 2px,100% 2px,2px 100%,2px 100%}
.dropdown-menu{z-index:10001;
.dropdown-menu{z-index:10001}
</style>
<table id="kvm_table" class="tablesorter four shift">
<thead><tr><th class="th1"><a href="#" style="cursor:hand;margin-left:12px;display:inline-block;width:32px" onclick="resetSorting()" title="Reset sorting"><i class="fa fa-th-list"></i></a>_(Name)_</th><th class="th2">_(Description)_</th><th>_(CPUs)_</th><th>_(Memory)_</th><th>_(vDisks)_</th><th>_(Graphics)_</th><th class="th3">_(Autostart)_</th></tr></thead>
<thead><tr><th class="th1"><a id="resetsort" class="nohand" onclick="resetSorting()" title="Reset sorting"><i class="fa fa-th-list"></i></a>_(Name)_</th><th class="th2">_(Description)_</th><th>_(CPUs)_</th><th>_(Memory)_</th><th>_(vDisks)_</th><th>_(Graphics)_</th><th class="th3">_(Autostart)_</th></tr></thead>
<tbody id="kvm_list"><tr><td colspan='8'></td></tr></tbody>
</table>
<input type="button" onclick="addVM()" id="btnAddVM" value="_(Add VM)_" style="display:none">
@@ -146,31 +146,28 @@ div.template,div#dialogWindow,input#upload{display:none}
<script src="<?autov('/plugins/dynamix.vm.manager/javascript/vmmanager.js')?>"></script>
<script src="<?autov("/webGui/javascript/jquery.filetree.js")?>"></script>
<script>
<?if ($display['resize']):?>
function resize(bind) {
var width = [];
var h = $('#kvm_list').height();
var s = Math.max(window.innerHeight-340,330);
if (h>s || bind) {
$('#kvm_list').height(s);
$('#kvm_table tbody tr:first-child td').each(function(){width.push($(this).width());});
$('#kvm_table thead tr th').each(function(i){$(this).width(width[i]);});
if (!bind) $('#kvm_table thead,#kvm_table tbody').addClass('fixed');
}
<?if (_var($display,'resize')):?>
function resize() {
$('#kvm_list').height(Math.max(window.innerHeight-340,330));
$('#kvm_table thead,#kvm_table tbody').removeClass('fixed');
$('#kvm_table thead tr th').each(function(){$(this).width($(this).width());});
$('#kvm_table tbody tr td').each(function(){$(this).width($(this).width());});
$('#kvm_table thead,#kvm_table tbody').not('.child').addClass('fixed');
}
<?endif;?>
function resetSorting() {
if ($.cookie('lockbutton')==null) return;
$('input[type=button]').prop('disabled',true);
$.post('/plugins/dynamix.vm.manager/include/UserPrefs.php',{reset:true},function(){loadlist();});
}
function changemedia(uuid,dev,bus,file) {
if (file === "--select") getisoimage(uuid,dev,bus,file) ;
if (file === "--select") getisoimage(uuid,dev,bus,file);
if (file === "--eject") ajaxVMDispatch({action:"change-media", uuid:uuid , cdrom:"" , dev:dev , bus:bus , file:file}, "loadlist");
}
function getisoimage(uuid,dev,bus,file){
var root = <?= '"'.$domain_cfg["MEDIADIR"].'"';?> ;
var match= ".iso" ;
var root = <?= '"'.$domain_cfg["MEDIADIR"].'"';?>;
var match= ".iso";
var box = $("#dialogWindow");
box.html($("#templateISO").html());
@@ -196,7 +193,7 @@ function getisoimage(uuid,dev,bus,file){
if (!target ) {errorTarget(); return;}
} else target = '';
box.find('#target').prop('disabled',true);
ajaxVMDispatch({action:"change-media", uuid:uuid , cdrom:"" , dev:dev , bus:bus , file:target}, "loadlist"); ;
ajaxVMDispatch({action:"change-media", uuid:uuid , cdrom:"" , dev:dev , bus:bus , file:target}, "loadlist");
box.dialog('close');
},
"_(Cancel)_": function(){
@@ -213,31 +210,45 @@ function dialogStyle() {
$('.ui-button-text').css({'padding':'0px 5px'});
}
var sortableHelper = function(e,ui){
var child = ui.next();
if (child.is(':visible')) child.addClass('unhide').hide();
ui.children().each(function(){$(this).width($(this).width());});
return ui;
};
function LockButton() {
if ($.cookie('lockbutton') == null) {
if ($.cookie('lockbutton')==null) {
$.cookie('lockbutton','lockbutton');
$('#resetsort').removeClass('nohand').addClass('hand');
$('i.mover').show();
$('#kvm_list .sortable').css({'cursor':'move'});
<?if ($themes1):?>
$('div.nav-item.LockButton').find('a').prop('title',"_(Lock sortable items)_");
$('div.nav-item.LockButton').find('b').removeClass('icon-u-lock red-text').addClass('icon-u-lock-open green-text');
$('div.nav-item.LockButton a').prop('title',"_(Lock sortable items)_");
$('div.nav-item.LockButton b').removeClass('icon-u-lock green-text').addClass('icon-u-lock-open red-text');
<?endif;?>
$('div.nav-item.LockButton').find('span').text("_(Lock sortable items)_");
$('#kvm_list').sortable({helper:sortableHelper,items:'tr.sortable',cursor:'move',axis:'y',containment:'parent',delay:100,opacity:0.5,zIndex:9999,forcePlaceholderSize:true,
$('div.nav-item.LockButton span').text("_(Lock sortable items)_");
$('#kvm_list').sortable({helper:sortableHelper,items:'.sortable',cursor:'grab',axis:'y',containment:'parent',delay:100,opacity:0.5,zIndex:9999,forcePlaceholderSize:true,
update:function(e,ui){
var row = $('#kvm_list').find('tr:first');
var names = ''; var index = '';
$('#kvm_list .sortable').each(function(){
var parent = $(this).attr('parent-id');
var child = $('tr[child-id="'+parent+'"]');
child.detach().insertAfter($(this));
if (child.hasClass('unhide')) child.removeClass('unhide').show();
});
var row = $('#kvm_list tr:first');
var names = '', index = '';
row.parent().children().find('td.vm-name').each(function(){names+=$(this).find('a').text()+';';index+=$(this).parent().parent().children().index($(this).parent())+';';});
$.post('/plugins/dynamix.vm.manager/include/UserPrefs.php',{names:names,index:index});
}});
} else {
$.removeCookie('lockbutton');
$('#resetsort').removeClass('hand').addClass('nohand');
$('i.mover').hide();
$('#kvm_list .sortable').css({'cursor':'default'});
<?if ($themes1):?>
$('div.nav-item.LockButton').find('a').prop('title',"_(Unlock sortable items)_");
$('div.nav-item.LockButton').find('b').removeClass('icon-u-lock-open green-text').addClass('icon-u-lock red-text');
$('div.nav-item.LockButton a').prop('title',"_(Unlock sortable items)_");
$('div.nav-item.LockButton b').removeClass('icon-u-lock-open red-text').addClass('icon-u-lock green-text');
<?endif;?>
$('div.nav-item.LockButton').find('span').text("_(Unlock sortable items)_");
$('div.nav-item.LockButton span').text("_(Unlock sortable items)_");
$('#kvm_list').sortable('destroy');
}
}
@@ -248,9 +259,9 @@ function loadlist() {
var data = d.split(/\0/);
$('#kvm_list').html(data[0]);
$('head').append('<script>'+data[1]+'<\/script>');
<?if ($display['resize']):?>
<?if (_var($display,'resize')):?>
resize();
$(window).bind('resize',function(){resize(true);});
$(window).bind('resize',function(){resize();});
<?endif;?>
<?foreach ($vms as $vm) {
$res = $lv->get_domain_by_name($vm);
@@ -272,7 +283,6 @@ function loadlist() {
$(this).prop('checked', data.autostart);
},'json');
});
context.init({preventDoubleContext:false,left:true,above:false});
$('div.spinner.fixed').hide('slow');
$('input[type=button]').prop('disabled',false).show('slow');
$('.text').click(showInput);
@@ -292,20 +302,7 @@ $(function() {
<div id="dialogWindow"></div>
<div markdown="1" id="templateISO" class="template">
<html <?=$display['rtl']?>lang="<?=strtok($locale,'_')?:'en'?>">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="format-detection" content="telephone=no">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex, nofollow">
<meta name="referrer" content="same-origin">
<link type="text/css" rel="stylesheet" href="<?autov("/webGui/styles/default-fonts.css")?>">
</head>
<body>
_(ISO Image)_:
: <input type="text" id="target" autocomplete="off" spellcheck="false" value="" data-pickcloseonfile="true" data-pickfolders="true" data-pickfilter="" data-pickmatch="" data-pickroot="" data-picktop="">
</body>
</html>
</div>
@@ -24,7 +24,7 @@ require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
$hardware = !empty(shell_exec("/etc/rc.d/rc.libvirt test"));
if (!$hardware) {
echo "<p class='notice'>"._('Your hardware does not have Intel VT-x or AMD-V capability').". "._('This is required to create VMs in KVM').". "._('Please disable the VM function').". ";
echo "<a href='https://wiki.unraid.net/Manual/VM_Management#Determining_HVM.2FIOMMU_Hardware_Support' target='_blank'> "._('Click here to see the Unraid Wiki for more information')."</a></p>";
echo "<a href='https://docs.unraid.net/unraid-os/manual/vm-management#determining-hvmiommu-hardware-support' target='_blank'> "._('Click here to see the Unraid Wiki for more information')."</a></p>";
}
function scan($area, $text) {
@@ -185,6 +185,15 @@ _(VM shutdown time-out)_:
:vms_shutdown_timeout_help:
_(Console Options)_:
: <select id="vmsconsole" name="CONSOLE">
<?=mk_option($domain_cfg['CONSOLE'], 'web', _('Web interface'))?>
<?=mk_option($domain_cfg['CONSOLE'], 'remote', _('Virt-Manager Remote Viewer'))?>
<?=mk_option($domain_cfg['CONSOLE'], 'both', _('Both Web & Virt-Manager Remote Viewer'))?>
</select>
:vms_console_help:
_(PCIe ACS override)_:
: <select id="pcie_acs_override"<?=$safemode?' disabled':''?>>
<?= mk_option($pcie_acs_override, '', _('Disabled'))?>
@@ -1,6 +1,7 @@
Menu="Tasks:70"
Type="xmenu"
Code="e918"
Lock="true"
Cond="exec(\"grep -o '^SERVICE=.enable' /boot/config/domain.cfg 2>/dev/null\")"
---
<?PHP
@@ -1,7 +1,7 @@
<?PHP
/* Copyright 2005-2021, Lime Technology
* Copyright 2015-2021, Derek Macias, Eric Schultz, Jon Panozzo.
* Copyright 2012-2021, Bergware International.
/* Copyright 2005-2023, Lime Technology
* Copyright 2015-2023, Derek Macias, Eric Schultz, Jon Panozzo.
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
@@ -13,14 +13,13 @@
?>
<?
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
// add translations
$_SERVER['REQUEST_URI'] = 'vms';
require_once "$docroot/webGui/include/Translations.php";
require_once "$docroot/webGui/include/Helpers.php";
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
$user_prefs = '/boot/config/plugins/dynamix.vm.manager/userprefs.cfg';
$vms = $lv->get_domains();
if (empty($vms)) {
@@ -28,36 +27,37 @@ if (empty($vms)) {
return;
}
if (file_exists($user_prefs)) {
$prefs = parse_ini_file($user_prefs); $sort = [];
foreach ($vms as $vm) $sort[] = array_search($vm,$prefs) ?? 999;
$prefs = @parse_ini_file($user_prefs) ?: [];
$sort = [];
foreach ($vms as $vm) $sort[] = array_search($vm,$prefs);
array_multisort($sort,SORT_NUMERIC,$vms);
unset($sort);
} else {
natcasesort($vms);
}
$i = 0;
$kvm = ['var kvm=[];'];
$show = explode(',',unscript($_GET['show']??''));
$path = $domain_cfg['MEDIADIR'] ;
$show = explode(',',unscript(_var($_GET,'show')));
$path = _var($domain_cfg,'MEDIADIR');
foreach ($vms as $vm) {
$res = $lv->get_domain_by_name($vm);
$desc = $lv->domain_get_description($res);
$uuid = $lv->domain_get_uuid($res);
$dom = $lv->domain_get_info($res);
$id = $lv->domain_get_id($res) ?: '-';
$is_autostart = $lv->domain_get_autostart($res);
$autostart = $lv->domain_get_autostart($res) ? 'checked' : '';
$state = $lv->domain_state_translate($dom['state']);
$icon = $lv->domain_get_icon_url($res);
$image = substr($icon,-4)=='.png' ? "<img src='$icon' class='img'>" : (substr($icon,0,5)=='icon-' ? "<i class='$icon img'></i>" : "<i class='fa fa-$icon img'></i>");
$arrConfig = domain_to_config($uuid);
if ($state == 'running') {
$mem = $dom['memory'] / 1024;
$mem = $dom['memory']/1024;
} else {
$mem = $lv->domain_get_memory($res) / 1024;
$mem = $lv->domain_get_memory($res)/1024;
}
$mem = round($mem).'M';
$vcpu = $dom['nrVirtCpu'];
$auto = $is_autostart ? 'checked':'';
$template = $lv->_get_single_xpath_result($res, '//domain/metadata/*[local-name()=\'vmtemplate\']/@name');
if (empty($template)) $template = 'Custom';
$log = (is_file("/var/log/libvirt/qemu/$vm.log") ? "libvirt/qemu/$vm.log" : '');
@@ -74,13 +74,13 @@ foreach ($vms as $vm) {
$graphics = '';
if ($vmrcport > 0) {
$wsport = $lv->domain_get_ws_port($res);
$vmrcprotocol = $lv->domain_get_vmrc_protocol($res) ;
$vmrcurl = autov('/plugins/dynamix.vm.manager/'.$vmrcprotocol.'.html',true).'&autoconnect=true&host=' . $_SERVER['HTTP_HOST'] ;
if ($vmrcprotocol == "spice") $vmrcurl .= '&vmname='. urlencode($vm) .'&port=/wsproxy/' . $vmrcport . '/' ; else $vmrcurl .= '&port=&path=/wsproxy/' . $wsport . '/';
$vmrcprotocol = $lv->domain_get_vmrc_protocol($res);
$vmrcurl = autov('/plugins/dynamix.vm.manager/'.$vmrcprotocol.'.html',true).'&autoconnect=true&host='._var($_SERVER,'HTTP_HOST');
if ($vmrcprotocol == "spice") $vmrcurl .= '&vmname='. urlencode($vm) .'&port=/wsproxy/'.$vmrcport.'/'; else $vmrcurl .= '&port=&path=/wsproxy/'.$wsport.'/';
$graphics = strtoupper($vmrcprotocol).":".$vmrcport;
} elseif ($vmrcport == -1 || $autoport) {
$vmrcprotocol = $lv->domain_get_vmrc_protocol($res) ;
if ($autoport == "yes") $auto = "auto" ; else $auto="manual" ;
$vmrcprotocol = $lv->domain_get_vmrc_protocol($res);
if ($autoport == "yes") $auto = "auto"; else $auto="manual";
$graphics = strtoupper($vmrcprotocol).':'._($auto);
} elseif (!empty($arrConfig['gpu'])) {
$arrValidGPUDevices = getValidGPUDevices();
@@ -89,10 +89,10 @@ foreach ($vms as $vm) {
if ($arrGPU['id'] == $arrDev['id']) {
if (count(array_filter($arrValidGPUDevices, function($v) use ($arrDev) { return $v['name'] == $arrDev['name']; })) > 1) {
$graphics .= $arrDev['name'].' ('.$arrDev['id'].')'."\n";
$vmrcprotocol = "VGA" ;
$vmrcprotocol = "VGA";
} else {
$graphics .= $arrDev['name']."\n";
$vmrcprotocol = "VGA" ;
$vmrcprotocol = "VGA";
}
}
}
@@ -100,7 +100,8 @@ foreach ($vms as $vm) {
$graphics = str_replace("\n", "<br>", trim($graphics));
}
unset($dom);
$menu = sprintf("onclick=\"addVMContext('%s','%s','%s','%s','%s','%s','%s')\"", addslashes($vm),addslashes($uuid),addslashes($template),$state,addslashes($vmrcurl),strtoupper($vmrcprotocol),addslashes($log));
if (!isset($domain_cfg["CONSOLE"])) $vmrcconsole = "web" ; else $vmrcconsole = $domain_cfg["CONSOLE"] ;
$menu = sprintf("onclick=\"addVMContext('%s','%s','%s','%s','%s','%s','%s','%s')\"", addslashes($vm),addslashes($uuid),addslashes($template),$state,addslashes($vmrcurl),strtoupper($vmrcprotocol),addslashes($log), $vmrcconsole);
$kvm[] = "kvm.push({id:'$uuid',state:'$state'});";
switch ($state) {
case 'running':
@@ -122,21 +123,21 @@ foreach ($vms as $vm) {
}
/* VM information */
echo "<tr parent-id='$i' class='sortable'><td class='vm-name' style='width:220px;padding:8px'>";
echo "<tr parent-id='$i' class='sortable'><td class='vm-name' style='width:220px;padding:8px'><i class='fa fa-arrows-v mover orange-text'></i>";
echo "<span class='outer'><span id='vm-$uuid' $menu class='hand'>$image</span><span class='inner'><a href='#' onclick='return toggle_id(\"name-$i\")' title='click for more VM info'>$vm</a><br><i class='fa fa-$shape $status $color'></i><span class='state'>"._($status)."</span></span></span></td>";
echo "<td>$desc</td>";
echo "<td><a class='vcpu-$uuid' style='cursor:pointer'>$vcpu</a></td>";
echo "<td>$mem</td>";
echo "<td title='$diskdesc'>$disks</td>";
echo "<td>$graphics</td>";
echo "<td><input class='autostart' type='checkbox' name='auto_{$vm}' title=\""._('Toggle VM autostart')."\" uuid='$uuid' $auto></td></tr>";
echo "<td><input class='autostart' type='checkbox' name='auto_{$vm}' title=\""._('Toggle VM autostart')."\" uuid='$uuid' $autostart></td></tr>";
/* Disk device information */
echo "<tr child-id='$i' id='name-$i".(in_array('name-'.$i++,$show) ? "'>" : "' style='display:none'>");
echo "<td colspan='8' style='margin:0;padding:0'>";
echo "<table class='tablesorter domdisk' id='domdisk_table'>";
echo "<thead><tr><th><i class='fa fa-hdd-o'></i> <b>"._('Disk devices')."</b></th><th>"._('Serial')."</b></th><th>"._('Bus')."</th><th>"._('Capacity')."</th><th>"._('Allocation')."</th><th>Boot Order</th</tr></thead>";
echo "<tbody id='domdisk_list'>";
echo "<table class='tablesorter domdisk'>";
echo "<thead class='child'><tr><th><i class='fa fa-hdd-o'></i> <b>"._('Disk devices')."</b></th><th>"._('Serial')."</b></th><th>"._('Bus')."</th><th>"._('Capacity')."</th><th>"._('Allocation')."</th><th>Boot Order</th</tr></thead>";
echo "<tbody class='child'>";
/* Display VM disks */
foreach ($lv->get_disk_stats($res) as $arrDisk) {
@@ -145,9 +146,9 @@ foreach ($vms as $vm) {
$disk = $arrDisk['file'] ?? $arrDisk['partition'];
$dev = $arrDisk['device'];
$bus = $arrValidDiskBuses[$arrDisk['bus']] ?? 'VirtIO';
$boot= $arrDisk["boot order"] ;
$serial = $arrDisk["serial"] ;
if ($boot < 1) $boot="Not set" ;
$boot= $arrDisk["boot order"];
$serial = $arrDisk["serial"];
if ($boot < 1) $boot="Not set";
echo "<tr><td>$disk</td><td>$serial</td><td>$bus</td>";
if ($state == 'shutoff') {
echo "<td title='Click to increase Disk Size'>";
@@ -173,39 +174,40 @@ foreach ($vms as $vm) {
$disk = $arrCD['file'] ?? $arrCD['partition'];
$dev = $arrCD['device'];
$bus = $arrValidDiskBuses[$arrCD['bus']] ?? 'VirtIO';
$boot= $arrCD["boot order"] ;
if ($boot < 1) $boot="Not set" ;
$boot= $arrCD["boot order"];
if ($boot < 1) $boot="Not set";
if ($disk != "" ) {
$title = _("Eject CD Drive").".";
$changemedia = "changemedia(\"{$uuid}\",\"{$dev}\",\"{$bus}\", \"--eject\")" ;
echo "<tr><td>$disk <a title='$title' href='#' onclick='$changemedia'> <i class='fa fa-eject' aria-hidden=true></i></a></td><td></td><td>$bus</td><td>$capacity</td><td>$allocation</td><td>$boot</td></tr>";
$changemedia = "changemedia(\"{$uuid}\",\"{$dev}\",\"{$bus}\", \"--eject\")";
echo "<tr><td>$disk <a title='$title' href='#' onclick='$changemedia'> <i class='fa fa-eject' aria-hidden=true></i></a></td><td></td><td>$bus</td><td>$capacity</td><td>$allocation</td><td>$boot</td></tr>";
} else {
$title = _("Insert CD").".";
$changemedia = "changemedia(\"{$uuid}\",\"{$dev}\",\"{$bus}\",\"--select\")" ;
$disk = _("No CD image inserted in to drive") ;
echo "<tr><td>$disk<a title='$title' href='#' onclick='$changemedia'> <i class='fa fa-bullseye' aria-hidden=true></i></a> </td><td></td><td>$bus</td><td>$capacity</td><td>$allocation</td><td>$boot</td></tr>";
$changemedia = "changemedia(\"{$uuid}\",\"{$dev}\",\"{$bus}\",\"--select\")";
$disk = _("No CD image inserted in to drive");
echo "<tr><td>$disk<a title='$title' href='#' onclick='$changemedia'> <i class='fa fa-bullseye' aria-hidden=true></i></a> </td><td></td><td>$bus</td><td>$capacity</td><td>$allocation</td><td>$boot</td></tr>";
}
}
echo "</tbody>";
/* Display VM IP Addresses "execute":"guest-network-get-interfaces" --pretty */
echo "<thead><tr><th><i class='fa fa-sitemap'></i> <b>"._('Interfaces')."</b></th><th></th><th></th><th>"._('Type')."</th><th>"._('IP Address')."</th><th>"._('Prefix')."</th></tr></thead>";
$gastate = getgastate($res) ;
echo "<thead class='child'><tr><th><i class='fa fa-sitemap'></i> <b>"._('Interfaces')."</b></th><th></th><th></th><th>"._('Type')."</th><th>"._('IP Address')."</th><th>"._('Prefix')."</th></tr></thead>";
echo "<tbody class='child'>";
$gastate = getgastate($res);
if ($gastate == "connected") {
$ip = $lv->domain_interface_addresses($res, 1) ;
$ip = $lv->domain_interface_addresses($res, 1);
if ($ip != false) {
$duplicates = []; // hide duplicate interface names
foreach ($ip as $arrIP) {
$ipname = $arrIP["name"] ;
$ipname = $arrIP["name"];
if (preg_match('/^(lo|Loopback)/',$ipname)) continue; // omit loopback interface
$iphdwadr = $arrIP["hwaddr"] == "" ? _("N/A") : $arrIP["hwaddr"] ;
$iplist = $arrIP["addrs"] ;
$iphdwadr = $arrIP["hwaddr"] == "" ? _("N/A") : $arrIP["hwaddr"];
$iplist = $arrIP["addrs"];
foreach ($iplist as $arraddr) {
$ipaddrval = $arraddr["addr"] ;
$ipaddrval = $arraddr["addr"];
if (preg_match('/^f[c-f]/',$ipaddrval)) continue; // omit ipv6 private addresses
$iptype = $arraddr["type"] ? "ipv6" : "ipv4" ;
$ipprefix = $arraddr["prefix"] ;
$iptype = $arraddr["type"] ? "ipv6" : "ipv4";
$ipprefix = $arraddr["prefix"];
$ipnamemac = "$ipname ($iphdwadr)";
if (!in_array($ipnamemac,$duplicates)) $duplicates[] = $ipnamemac; else $ipnamemac = "";
echo "<tr><td>$ipnamemac</td><td></td><td></td><td>$iptype</td><td>$ipaddrval</td><td>$ipprefix</td></tr>";
@@ -215,8 +217,8 @@ foreach ($vms as $vm) {
}
else {
if ($gastate == "disconnected") echo "<tr><td>"._('Guest agent not installed')."</td><td></td><td></td><td></td></tr>";
else echo "<tr><td>"._('Guest not running')."</td><td></td><td></td><td></td><td></td></tr>" ;
}
else echo "<tr><td>"._('Guest not running')."</td><td></td><td></td><td></td><td></td></tr>";
}
echo "</tbody></table>";
echo "</td></tr>";
}
@@ -1,7 +1,7 @@
<?PHP
/* Copyright 2005-2020, Lime Technology
* Copyright 2015-2020, Derek Macias, Eric Schultz, Jon Panozzo.
* Copyright 2012-2020, Bergware International.
/* Copyright 2005-2023, Lime Technology
* Copyright 2015-2023, Derek Macias, Eric Schultz, Jon Panozzo.
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
@@ -13,10 +13,10 @@
?>
<?
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
// add translations
$_SERVER['REQUEST_URI'] = 'vms';
require_once "$docroot/webGui/include/Translations.php";
require_once "$docroot/webGui/include/Helpers.php";
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
@@ -62,8 +62,8 @@ function embed(&$syslinux, $key, $value) {
}
$arrSizePrefix = [0 => '', 1 => 'K', 2 => 'M', 3 => 'G', 4 => 'T', 5 => 'P'];
$action = unscript($_REQUEST['action']??'');
$uuid = unscript($_REQUEST['uuid']??'');
$action = unscript(_var($_REQUEST,'action'));
$uuid = unscript(_var($_REQUEST,'uuid'));
$arrResponse = [];
if ($uuid) {
@@ -90,21 +90,60 @@ case 'domain-start':
: ['error' => $lv->get_last_error()];
break;
case 'domain-start-console':
requireLibvirt();
$arrResponse = $lv->domain_start($domName)
? ['success' => true, 'state' => $lv->domain_get_state($domName)]
: ['error' => $lv->get_last_error()];
$dom = $lv->get_domain_by_name($domName) ;
$vmrcport = $lv->domain_get_vnc_port($dom);
$wsport = $lv->domain_get_ws_port($dom);
$protocol = $lv->domain_get_vmrc_protocol($dom);
if ($vmrcport > 0) {
$vmrcurl = autov('/plugins/dynamix.vm.manager/'.$protocol.'.html',true).'&autoconnect=true&host=' . $_SERVER['HTTP_HOST'] ;
if ($protocol == "spice") $vmrcurl .= '&vmname='. urlencode($domName) .'&port=/wsproxy/'.$vmrcport.'/'; else $vmrcurl .= '&port=&path=/wsproxy/' . $wsport . '/';
}
$arrResponse['vmrcurl'] = $vmrcurl ;
break;
case 'domain-start-console':
requireLibvirt();
$arrResponse = $lv->domain_start($domName)
? ['success' => true, 'state' => $lv->domain_get_state($domName)]
: ['error' => $lv->get_last_error()];
$dom = $lv->get_domain_by_name($domName);
$vmrcport = $lv->domain_get_vnc_port($dom);
$wsport = $lv->domain_get_ws_port($dom);
$protocol = $lv->domain_get_vmrc_protocol($dom);
if ($vmrcport > 0) {
$vmrcurl = autov('/plugins/dynamix.vm.manager/'.$protocol.'.html',true).'&autoconnect=true&host='._var($_SERVER,'HTTP_HOST');
if ($protocol == "spice") $vmrcurl .= '&vmname='. urlencode($domName) .'&port=/wsproxy/'.$vmrcport.'/'; else $vmrcurl .= '&port=&path=/wsproxy/'.$wsport.'/';
}
$arrResponse['vmrcurl'] = $vmrcurl;
break;
case 'domain-start-consoleRV':
requireLibvirt();
$arrResponse = $lv->domain_start($domName)
? ['success' => true, 'state' => $lv->domain_get_state($domName)]
: ['error' => $lv->get_last_error()];
$dom = $lv->get_domain_by_name($domName);
$vmrcport = $lv->domain_get_vnc_port($dom);
$wsport = $lv->domain_get_ws_port($dom);
$protocol = $lv->domain_get_vmrc_protocol($dom);
if ($protocol == "spice") $port= $vmrcport ; else $port=$vmrcport ;
$vvarray = array() ;
$vvarray[] = "[virt-viewer]\n";
$vvarray[] = "type=$protocol\n";
$vvarray[] = "host="._var($_SERVER,'HTTP_HOST')."\n" ;
$vvarray[] = "port=$port\n" ;
if (!is_dir("/mnt/user/system/remoteviewer")) mkdir("/mnt/user/system/remoteviewer") ;
$vvfile = "/mnt/user/system/remoteviewer/rv"._var($_SERVER,'HTTP_HOST').".$port.vv" ;
file_put_contents($vvfile,$vvarray) ;
$arrResponse['vvfile'] = $vvfile;
break;
case 'domain-consoleRV':
requireLibvirt();
$dom = $lv->get_domain_by_name($domName);
$vmrcport = $lv->domain_get_vnc_port($dom);
$wsport = $lv->domain_get_ws_port($dom);
$protocol = $lv->domain_get_vmrc_protocol($dom);
if ($protocol == "spice") $port= $vmrcport ; else $port=$vmrcport ;
$vvarray = array() ;
$vvarray[] = "[virt-viewer]\n";
$vvarray[] = "type=$protocol\n";
$vvarray[] = "host="._var($_SERVER,'HTTP_HOST')."\n" ;
$vvarray[] = "port=$port\n" ;
if (!is_dir("/mnt/user/system/remoteviewer")) mkdir("/mnt/user/system/remoteviewer") ;
$vvfile = "/mnt/user/system/remoteviewer/rv"._var($_SERVER,'HTTP_HOST').".$port.vv" ;
file_put_contents($vvfile,$vvarray) ;
$arrResponse['vvfile'] = $vvfile;
break;
case 'domain-pause':
requireLibvirt();
@@ -216,13 +255,13 @@ case 'cdrom-change':
case 'change-media':
requireLibvirt();
$dev= $_REQUEST['dev'] ;
$file= $_REQUEST['file'] ;
$cmdstr = "virsh change-media '$domName' $dev $file" ;
$dev= $_REQUEST['dev'];
$file= $_REQUEST['file'];
$cmdstr = "virsh change-media '$domName' $dev $file";
$rtn=shell_exec($cmdstr)
? ['success' => true]
: ['error' => "Change Media Failed"];
break;
break;
case 'memory-change':
requireLibvirt();
@@ -616,6 +655,7 @@ case 'virtio-win-iso-remove':
$arrResponse = ['success' => true];
}
break;
default:
$arrResponse = ['error' => _('Unknown action')." '$action'"];
break;
@@ -704,7 +704,6 @@
<binary path='/usr/libexec/virtiofsd' xattr='on'>
<sandbox mode='chroot'/>
<cache mode='always'/>
<lock posix='on' flock='on'/>
</binary>
</filesystem>" ;
} else {
@@ -1527,7 +1526,7 @@
function get_node_device_information($dev) {
$dev = $this->get_node_device_res($dev);
$tmp = libvirt_nodedev_get_information($dev);
if ($dev) $tmp = libvirt_nodedev_get_information($dev); else $tmp = $dev ;
return ($tmp) ? $tmp : $this->_set_last_error();
}
@@ -678,7 +678,7 @@ private static $encoding = 'UTF-8';
$lv = new Libvirt('qemu:///system', null, null, false);
$arrHostInfo = $lv->host_get_node_info();
$maxcpu = (int)$arrHostInfo['cpus'];
$maxmem = number_format(($arrHostInfo['memory'] / 1048576), 1, '.', ' ');
$maxmem = number_format(($arrHostInfo['memory'] / 1048576), 1, '.', '');
}
function mk_dropdown_options($arrOptions, $strSelected) {
@@ -37,6 +37,15 @@ function getCloneName(name){
});
dialogStyle();
}
function downloadFile(source) {
var a = document.createElement('a');
a.setAttribute('href',source);
a.setAttribute('download',source.split('/').pop());
a.style.display = 'none';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
function ajaxVMDispatch(params, spin){
if (spin) $('#vm-'+params['uuid']).parent().find('i').removeClass('fa-play fa-square fa-pause').addClass('fa-refresh fa-spin');
@@ -71,19 +80,46 @@ function ajaxVMDispatchconsole(params, spin){
}
},'json');
}
function addVMContext(name, uuid, template, state, vmrcurl,vmrcprotocol , log){
function ajaxVMDispatchconsoleRV(params, spin){
if (spin) $('#vm-'+params['uuid']).parent().find('i').removeClass('fa-play fa-square fa-pause').addClass('fa-refresh fa-spin');
$.post("/plugins/dynamix.vm.manager/include/VMajax.php", params, function(data) {
if (data.error) {
swal({
title:_("Execution error"), html:true,
text:data.error, type:"error",
confirmButtonText:_('Ok')
},function(){
if (spin) setTimeout(spin+'()',500); else location=window.location.href;
});
} else {
if (spin) setTimeout(spin+'()',500); else location=window.location.href;
downloadFile(data.vvfile) ;
}
},'json');
}
function addVMContext(name, uuid, template, state, vmrcurl, vmrcprotocol, log, console="web"){
var opts = [];
var path = location.pathname;
var x = path.indexOf("?");
if (x!=-1) path = path.substring(0,x);
if (vmrcurl !== "" && state == "running") {
var vmrctext=_("VM Console") + "(" + vmrcprotocol + ")" ;
opts.push({text:vmrctext, icon:"fa-desktop", action:function(e) {
e.preventDefault();
window.open(vmrcurl, '_blank', 'scrollbars=yes,resizable=yes');
}});
if (vmrcurl !== "" && state == "running") {
if (console == "web" || console == "both") {
var vmrctext=_("VM Console") + "(" + vmrcprotocol + ")" ;
opts.push({text:vmrctext, icon:"fa-desktop", action:function(e) {
e.preventDefault();
window.open(vmrcurl, '_blank', 'scrollbars=yes,resizable=yes');
}});
}
if (console == "remote" || console == "both") {
opts.push({text:_("VM remote-viewer")+ "(" + vmrcprotocol + ")" , icon:"fa-desktop", action:function(e) {
e.preventDefault();
ajaxVMDispatchconsoleRV({action:"domain-consoleRV", uuid:uuid, vmrcurl:vmrcurl}, "loadlist") ;
}});
}
opts.push({divider:true});
}
context.settings({right:false,above:false});
if (state == "running") {
opts.push({text:_("Stop"), icon:"fa-stop", action:function(e) {
e.preventDefault();
@@ -128,11 +164,18 @@ function addVMContext(name, uuid, template, state, vmrcurl,vmrcprotocol , log){
e.preventDefault();
ajaxVMDispatch({action:"domain-start", uuid:uuid}, "loadlist");
}});
if (vmrcprotocol == "VNC" || vmrcprotocol == "SPICE") {
opts.push({text:_("Start with console")+ "(" + vmrcprotocol + ")" , icon:"fa-play", action:function(e) {
e.preventDefault();
ajaxVMDispatchconsole({action:"domain-start-console", uuid:uuid, vmrcurl:vmrcurl}, "loadlist") ;
}});
if (vmrcprotocol == "VNC" || vmrcprotocol == "SPICE") {
if (console == "web" || console == "both") {
opts.push({text:_("Start with console")+ "(" + vmrcprotocol + ")" , icon:"fa-play", action:function(e) {
e.preventDefault();
ajaxVMDispatchconsole({action:"domain-start-console", uuid:uuid, vmrcurl:vmrcurl}, "loadlist") ;
}});}
if (console == "remote" || console == "both") {
opts.push({text:_("Start with remote-viewer")+ "(" + vmrcprotocol + ")" , icon:"fa-play", action:function(e) {
e.preventDefault();
ajaxVMDispatchconsoleRV({action:"domain-start-consoleRV", uuid:uuid, vmrcurl:vmrcurl}, "loadlist") ;
}});
}
}}
opts.push({divider:true});
if (log !== "") {

Some files were not shown because too many files have changed in this diff Show More