'use strict';
'require uci';
'require rpc';
'require form';
var callBlockDevices, callMountPoints, callBlockDetect, callUmount,
callFileRead, callFileStat, callFileExec;
callBlockDevices = rpc.declare({
object: 'luci',
method: 'getBlockDevices',
expect: { '': {} }
});
callMountPoints = rpc.declare({
object: 'luci',
method: 'getMountPoints',
expect: { result: [] }
});
callBlockDetect = rpc.declare({
object: 'luci',
method: 'setBlockDetect',
expect: { result: false }
});
callUmount = rpc.declare({
object: 'luci',
method: 'setUmount',
params: [ 'path' ],
expect: { result: false }
});
callFileRead = rpc.declare({
object: 'file',
method: 'read',
params: [ 'path' ],
expect: { data: '' },
filter: function(s) {
return (s || '').split(/\n/).filter(function(ln) {
return ln.match(/\S/) && !ln.match(/^nodev\t/);
}).map(function(ln) {
return ln.trim();
});
}
});
callFileStat = rpc.declare({
object: 'file',
method: 'stat',
params: [ 'path' ],
expect: { '': {} },
filter: function(st) {
return (L.isObject(st) && st.path != null);
}
});
callFileExec = rpc.declare({
object: 'file',
method: 'exec',
params: [ 'command', 'params' ],
expect: { code: 255 }
});
function device_textvalue(devices, section_id) {
var v = (uci.get('fstab', section_id, 'uuid') || '').toLowerCase(),
e = Object.keys(devices).filter(function(dev) { return (devices[dev].uuid || '-').toLowerCase() == v })[0];
if (v) {
this.section.devices[section_id] = devices[e];
if (e && devices[e].size)
return E('span', 'UUID: %h (%s, %1024.2mB)'.format(v, devices[e].dev, devices[e].size));
else if (e)
return E('span', 'UUID: %h (%s)'.format(v, devices[e].dev));
else
return E('span', 'UUID: %h (%s)'.format(v, _('not present')));
}
v = uci.get('fstab', section_id, 'label');
e = Object.keys(devices).filter(function(dev) { return devices[dev].label == v })[0];
if (v) {
this.section.devices[section_id] = this.section.devices[section_id] || devices[e];
if (e && devices[e].size)
return E('span', 'Label: %h (%s, %1024.2mB)'.format(v, devices[e].dev, devices[e].size));
else if (e)
return E('span', 'Label: %h (%s)'.format(v, devices[e].dev));
else
return E('span', 'Label: %h (%s)'.format(v, _('not present')));
}
v = uci.get('fstab', section_id, 'device');
e = Object.keys(devices).filter(function(dev) { return devices[dev].dev == v })[0];
if (v) {
this.section.devices[section_id] = this.section.devices[section_id] || devices[e];
if (e && devices[e].size)
return E('span', '%h (%1024.2mB)'.format(v, devices[e].size));
else if (e)
return E('span', '%h'.format(v));
else
return E('span', '%h (%s)'.format(v, _('not present')));
}
}
return L.view.extend({
handleDetect: function(m, ev) {
return callBlockDetect()
.then(L.bind(uci.unload, uci, 'fstab'))
.then(L.bind(m.render, m));
},
handleMountAll: function(m, ev) {
return callFileExec('/sbin/block', ['mount'])
.then(function(rc) {
if (rc != 0)
L.ui.addNotification(null, E('p', _('The block mount command failed with code %d').format(rc)));
})
.then(L.bind(uci.unload, uci, 'fstab'))
.then(L.bind(m.render, m));
},
handleUmount: function(m, path, ev) {
return callUmount(path)
.then(L.bind(uci.unload, uci, 'fstab'))
.then(L.bind(m.render, m));
},
load: function() {
return Promise.all([
callBlockDevices(),
callFileRead('/proc/filesystems'),
callFileRead('/etc/filesystems'),
callFileStat('/usr/sbin/e2fsck'),
callFileStat('/usr/sbin/fsck.f2fs'),
callFileStat('/usr/sbin/dosfsck'),
callFileStat('/usr/bin/btrfsck'),
uci.load('fstab')
]);
},
render: function(results) {
var devices = results[0],
procfs = results[1],
etcfs = results[2],
triggers = {},
trigger, m, s, o;
var filesystems = procfs.concat(etcfs.filter(function(fs) {
return procfs.indexOf(fs) < 0;
})).sort();
var fsck = {
ext2: results[3],
ext3: results[3],
ext4: results[3],
f2fs: results[4],
vfat: results[5],
btrfs: results[6]
};
if (!uci.sections('fstab', 'global').length)
uci.add('fstab', 'global');
m = new form.Map('fstab', _('Mount Points'));
s = m.section(form.TypedSection, 'global', _('Global Settings'));
s.addremove = false;
s.anonymous = true;
o = s.option(form.Button, '_detect', _('Generate Config'), _('Find all currently attached filesystems and swap and replace configuration with defaults based on what was detected'));
o.onclick = this.handleDetect.bind(this, m);
o.inputstyle = 'reload';
o = s.option(form.Button, '_mountall', _('Mount attached devices'), _('Attempt to enable configured mount points for attached devices'));
o.onclick = this.handleMountAll.bind(this, m);
o.inputstyle = 'reload';
o = s.option(form.Flag, 'anon_swap', _('Anonymous Swap'), _('Mount swap not specifically configured'));
o.default = o.disabled;
o.rmempty = false;
o = s.option(form.Flag, 'anon_mount', _('Anonymous Mount'), _('Mount filesystems not specifically configured'));
o.default = o.disabled;
o.rmempty = false;
o = s.option(form.Flag, 'auto_swap', _('Automount Swap'), _('Automatically mount swap on hotplug'));
o.default = o.enabled;
o.rmempty = false;
o = s.option(form.Flag, 'auto_mount', _('Automount Filesystem'), _('Automatically mount filesystems on hotplug'));
o.default = o.enabled;
o.rmempty = false;
o = s.option(form.Flag, 'check_fs', _('Check filesystems before mount'), _('Automatically check filesystem for errors before mounting'));
o.default = o.disabled;
o.rmempty = false;
// Mount status table
o = s.option(form.DummyValue, '_mtab');
o.load = function(section_id) {
return callMountPoints().then(L.bind(function(mounts) {
this.mounts = mounts;
}, this));
};
o.render = L.bind(function(view, section_id) {
var table = E('div', { 'class': 'table' }, [
E('div', { 'class': 'tr table-titles' }, [
E('div', { 'class': 'th' }, _('Filesystem')),
E('div', { 'class': 'th' }, _('Mount Point')),
E('div', { 'class': 'th center' }, _('Available')),
E('div', { 'class': 'th center' }, _('Used')),
E('div', { 'class': 'th' }, _('Unmount'))
])
]);
var rows = [];
for (var i = 0; i < this.mounts.length; i++) {
var used = this.mounts[i].size - this.mounts[i].free,
umount = true;
if (/^\/(overlay|rom|tmp(?:\/.+)?|dev(?:\/.+)?|)$/.test(this.mounts[i].mount))
umount = false;
rows.push([
this.mounts[i].device,
this.mounts[i].mount,
'%1024.2mB / %1024.2mB'.format(this.mounts[i].avail, this.mounts[i].size),
'%.2f%% (%1024.2mB)'.format(100 / this.mounts[i].size * used, used),
umount ? E('button', {
'class': 'btn cbi-button-remove',
'click': L.ui.createHandlerFn(view, 'handleUmount', m, this.mounts[i].mount)
}, [ _('Unmount') ]) : '-'
]);
}
cbi_update_table(table, rows, E('em', _('Unable to obtain mount information')));
return E([], [ E('h3', _('Mounted file systems')), table ]);
}, o, this);
// Mounts
s = m.section(form.GridSection, 'mount', _('Mount Points'), _('Mount Points define at which point a memory device will be attached to the filesystem'));
s.modaltitle = _('Mount Points - Mount Entry');
s.anonymous = true;
s.addremove = true;
s.sortable = true;
s.devices = {};
s.renderHeaderRows = function(/* ... */) {
var trEls = form.GridSection.prototype.renderHeaderRows.apply(this, arguments);
return trEls.childNodes[0];
}
s.tab('general', _('General Settings'));
s.tab('advanced', _('Advanced Settings'));
o = s.taboption('general', form.Flag, 'enabled', _('Enabled'));
o.rmempty = false;
o.editable = true;
o = s.taboption('general', form.DummyValue, '_device', _('Device'));
o.rawhtml = true;
o.modalonly = false;
o.write = function() {};
o.remove = function() {};
o.textvalue = device_textvalue.bind(o, devices);
o = s.taboption('general', form.Value, 'uuid', _('UUID'), _('If specified, mount the device by its UUID instead of a fixed device node'));
o.modalonly = true;
o.value('', _('-- match by uuid --'));
var devs = Object.keys(devices).sort();
for (var i = 0; i < devs.length; i++) {
var dev = devices[devs[i]];
if (dev.uuid && dev.size)
o.value(dev.uuid, '%s (%s, %1024.2mB)'.format(dev.uuid, dev.dev, dev.size));
else if (dev.uuid)
o.value(dev.uuid, '%s (%s)'.format(dev.uuid, dev.dev));
}
o = s.taboption('general', form.Value, 'label', _('Label'), _('If specified, mount the device by the partition label instead of a fixed device node'));
o.modalonly = true;
o.depends('uuid', '');
o.value('', _('-- match by label --'));
for (var i = 0; i < devs.length; i++) {
var dev = devices[devs[i]];
if (dev.label && dev.size)
o.value(dev.label, '%s (%s, %1024.2mB)'.format(dev.label, dev.dev, dev.size));
else if (dev.label)
o.value(dev.label, '%s (%s)'.format(dev.label, dev.dev));
}
o = s.taboption('general', form.Value, 'device', _('Device'), _('The device file of the memory or partition (e.g. /dev/sda1
)'));
o.modalonly = true;
o.depends({ uuid: '', label: '' });
for (var i = 0; i < devs.length; i++) {
var dev = devices[devs[i]];
if (dev.size)
o.value(dev.dev, '%s (%1024.2mB)'.format(dev.dev, dev.size));
else
o.value(dev.dev);
}
o = s.taboption('general', form.Value, 'target', _('Mount point'), _('Specifies the directory the device is attached to'));
o.value('/', _('Use as root filesystem (/)'));
o.value('/overlay', _('Use as external overlay (/overlay)'));
o.rmempty = false;
o = s.taboption('general', form.DummyValue, '__notice', _('Root preparation'));
o.depends('target', '/');
o.modalonly = true;
o.rawhtml = true;
o.default = '' +
'
%s
'.format(_('Make sure to clone the root filesystem using something like the commands below:')) + '' + 'mkdir -p /tmp/introot\n' + 'mkdir -p /tmp/extroot\n' + 'mount --bind / /tmp/introot\n' + 'mount /dev/sda1 /tmp/extroot\n' + 'tar -C /tmp/introot -cvf - . | tar -C /tmp/extroot -xf -\n' + 'umount /tmp/introot\n' + 'umount /tmp/extroot\n' + '' ; o = s.taboption('advanced', form.ListValue, 'fstype', _('Filesystem')); o.textvalue = function(section_id) { var dev = this.section.devices[section_id], text = this.cfgvalue(section_id) || 'auto'; if (dev && dev.type && dev.type != text) text += ' (%s)'.format(dev.type); return text; }; o.value('', 'auto'); for (var i = 0; i < filesystems.length; i++) o.value(filesystems[i]); o = s.taboption('advanced', form.Value, 'options', _('Mount options'), _('See "mount" manpage for details')); o.textvalue = function(section_id) { return this.cfgvalue(section_id) || 'defaults' }; o.placeholder = 'defaults'; s.taboption('advanced', form.Flag, 'enabled_fsck', _('Run filesystem check'), _('Run a filesystem check before mounting the device')); // Swaps s = m.section(form.GridSection, 'swap', _('SWAP'), _('If your physical memory is insufficient unused data can be temporarily swapped to a swap-device resulting in a higher amount of usable RAM. Be aware that swapping data is a very slow process as the swap-device cannot be accessed with the high datarates of the RAM.')); s.modaltitle = _('Mount Points - Swap Entry'); s.anonymous = true; s.addremove = true; s.sortable = true; s.devices = {}; s.renderHeaderRows = function(/* ... */) { var trEls = form.GridSection.prototype.renderHeaderRows.apply(this, arguments); trEls.childNodes[0].childNodes[1].style.width = '90%'; return trEls.childNodes[0]; } o = s.option(form.Flag, 'enabled', _('Enabled')); o.rmempty = false; o.editable = true; o = s.option(form.DummyValue, '_device', _('Device')); o.modalonly = false; o.textvalue = device_textvalue.bind(o, devices); o = s.option(form.Value, 'uuid', _('UUID'), _('If specified, mount the device by its UUID instead of a fixed device node')); o.modalonly = true; o.value('', _('-- match by uuid --')); var devs = Object.keys(devices).sort(); for (var i = 0; i < devs.length; i++) { var dev = devices[devs[i]]; if (dev.dev.match(/^\/dev\/(mtdblock|ubi|ubiblock)\d/)) continue; if (dev.uuid && dev.size) o.value(dev.uuid, '%s (%s, %1024.2mB)'.format(dev.uuid, dev.dev, dev.size)); else if (dev.uuid) o.value(dev.uuid, '%s (%s)'.format(dev.uuid, dev.dev)); } o = s.option(form.Value, 'label', _('Label'), _('If specified, mount the device by the partition label instead of a fixed device node')); o.modalonly = true; o.depends('uuid', ''); o.value('', _('-- match by label --')); for (var i = 0; i < devs.length; i++) { var dev = devices[devs[i]]; if (dev.dev.match(/^\/dev\/(mtdblock|ubi|ubiblock)\d/)) continue; if (dev.label && dev.size) o.value(dev.label, '%s (%s, %1024.2mB)'.format(dev.label, dev.dev, dev.size)); else if (dev.label) o.value(dev.label, '%s (%s)'.format(dev.label, dev.dev)); } o = s.option(form.Value, 'device', _('Device'), _('The device file of the memory or partition (e.g.
/dev/sda1
)'));
o.modalonly = true;
o.depends({ uuid: '', label: '' });
for (var i = 0; i < devs.length; i++) {
var dev = devices[devs[i]];
if (dev.dev.match(/^\/dev\/(mtdblock|ubi|ubiblock)\d/))
continue;
if (dev.size)
o.value(dev.dev, '%s (%1024.2mB)'.format(dev.dev, dev.size));
else
o.value(dev.dev);
}
return m.render();
}
});