mirror of
https://github.com/remittor/zapret-openwrt.git
synced 2026-01-27 12:50:35 +03:00
luci: updater: Add new func tools.execAndRead
This commit is contained in:
@@ -6,6 +6,7 @@ return baseclass.extend({
|
|||||||
appName : 'zapret2',
|
appName : 'zapret2',
|
||||||
AppName : 'Zapret2',
|
AppName : 'Zapret2',
|
||||||
execPath : '/etc/init.d/zapret2',
|
execPath : '/etc/init.d/zapret2',
|
||||||
|
appDir : '/opt/zapret2',
|
||||||
syncCfgPath : '/opt/zapret2/sync_config.sh',
|
syncCfgPath : '/opt/zapret2/sync_config.sh',
|
||||||
defCfgPath : '/opt/zapret2/def-cfg.sh',
|
defCfgPath : '/opt/zapret2/def-cfg.sh',
|
||||||
defaultCfgPath : '/opt/zapret2/restore-def-cfg.sh',
|
defaultCfgPath : '/opt/zapret2/restore-def-cfg.sh',
|
||||||
|
|||||||
@@ -562,4 +562,93 @@ return baseclass.extend({
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
execAndRead: async function({ cmd = [ ], log = '', logArea = null, callback = null, cbarg = null, hiderow = [ ], rpc_timeout = 5, rpc_root = false } = {})
|
||||||
|
{
|
||||||
|
function appendLog(msg, end = '\n')
|
||||||
|
{
|
||||||
|
logArea.value += msg + end;
|
||||||
|
logArea.scrollTop = logArea.scrollHeight;
|
||||||
|
}
|
||||||
|
function fixLogEnd()
|
||||||
|
{
|
||||||
|
if (logArea.value && logArea.value.slice(-1) != '\n') {
|
||||||
|
appendLog('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let hide_rows = Array.isArray(hiderow) ? hiderow : [ hiderow ];
|
||||||
|
let rpc_opt = { "timeout": rpc_timeout*1000 };
|
||||||
|
if (rpc_root) {
|
||||||
|
rpc_opt.uid = 0; // run under root
|
||||||
|
}
|
||||||
|
const logFile = log; // file for reading: '/tmp/zapret_pkg_install.log'
|
||||||
|
const rcFile = logFile + '.rc';
|
||||||
|
try {
|
||||||
|
await fs.exec('/bin/busybox', [ 'rm', '-f', logFile + '*' ], null, rpc_opt);
|
||||||
|
appendLog('Output file cleared!');
|
||||||
|
} catch (e) {
|
||||||
|
return callback(cbarg, 500, 'ERROR: Failed to clear output file');
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
let opt_list = [ logFile ];
|
||||||
|
opt_list.push(...cmd);
|
||||||
|
let res = await fs.exec(this.appDir+'/script-exec.sh', opt_list, null, rpc_opt);
|
||||||
|
if (res.code != 0) {
|
||||||
|
return callback(cbarg, 525, 'ERROR: cannot run "' + cmd[0] + '" script! (error = ' + res.code + ')');
|
||||||
|
}
|
||||||
|
appendLog('Process started...');
|
||||||
|
} catch (e) {
|
||||||
|
return callback(cbarg, 520, 'ERROR: Failed on execute process: ' + e.message);
|
||||||
|
}
|
||||||
|
let lastLen = 0;
|
||||||
|
let retCode = -1;
|
||||||
|
let timerBusy = false;
|
||||||
|
let timer = setInterval(async () => {
|
||||||
|
if (timerBusy)
|
||||||
|
return; // skip iteration
|
||||||
|
timerBusy = true;
|
||||||
|
try {
|
||||||
|
let res = await fs.exec('/bin/cat', [ logFile ], null, rpc_opt);
|
||||||
|
if (res.stdout && res.stdout.length > lastLen) {
|
||||||
|
let log = res.stdout.slice(lastLen);
|
||||||
|
hide_rows.forEach(re => {
|
||||||
|
log = log.replace(re, '');
|
||||||
|
});
|
||||||
|
appendLog(log, '');
|
||||||
|
lastLen = res.stdout.length;
|
||||||
|
}
|
||||||
|
if (retCode < 0) {
|
||||||
|
let rc = await fs.exec('/bin/cat', [ rcFile ], null, rpc_opt);
|
||||||
|
if (rc.code != 0) {
|
||||||
|
clearInterval(timer);
|
||||||
|
fixLogEnd();
|
||||||
|
return callback(cbarg, 545, 'ERROR: cannot read file "' + rcFile + '"');
|
||||||
|
}
|
||||||
|
if (rc.stdout) {
|
||||||
|
retCode = parseInt(rc.stdout.trim(), 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (retCode >= 0) {
|
||||||
|
clearInterval(timer);
|
||||||
|
fixLogEnd();
|
||||||
|
if (retCode == 0 && res.stdout) {
|
||||||
|
return callback(cbarg, 0, res.stdout);
|
||||||
|
}
|
||||||
|
return callback(cbarg, retCode, 'ERROR: Process failed with error ' + retCode);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (e.message?.includes('RPC call to file/exec failed with error -32000: Object not found')) {
|
||||||
|
console.warn('WARN: execAndRead: ' + e.message);
|
||||||
|
return; // goto next timer iteration
|
||||||
|
}
|
||||||
|
clearInterval(timer);
|
||||||
|
fixLogEnd();
|
||||||
|
let errtxt = 'ERROR: execAndRead: ' + e.message;
|
||||||
|
errtxt += 'ERROR: execAndRead: ' + e.stack?.trim().split('\n')[0];
|
||||||
|
return callback(cbarg, 540, errtxt);
|
||||||
|
} finally {
|
||||||
|
timerBusy = false;
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
},
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -19,17 +19,20 @@ const fn_update_pkg_sh = '/opt/'+tools.appName+'/update-pkg.sh';
|
|||||||
return baseclass.extend({
|
return baseclass.extend({
|
||||||
releasesUrlPrefix : 'https://raw.githubusercontent.com/remittor/zapret-openwrt/gh-pages/releases/',
|
releasesUrlPrefix : 'https://raw.githubusercontent.com/remittor/zapret-openwrt/gh-pages/releases/',
|
||||||
|
|
||||||
appendLog: function(msg, end = '\n') {
|
appendLog: function(msg, end = '\n')
|
||||||
|
{
|
||||||
this.logArea.value += msg + end;
|
this.logArea.value += msg + end;
|
||||||
this.logArea.scrollTop = this.logArea.scrollHeight;
|
this.logArea.scrollTop = this.logArea.scrollHeight;
|
||||||
},
|
},
|
||||||
|
|
||||||
setBtnMode: function(enable) {
|
setBtnMode: function(enable)
|
||||||
|
{
|
||||||
this.btn_cancel.disabled = enable ? false : true;
|
this.btn_cancel.disabled = enable ? false : true;
|
||||||
this.btn_action.disabled = (enable == 2) ? false : true;
|
this.btn_action.disabled = (enable == 2) ? false : true;
|
||||||
},
|
},
|
||||||
|
|
||||||
setStage: function(stage, btn_flag = true) {
|
setStage: function(stage, btn_flag = true)
|
||||||
|
{
|
||||||
if (stage == 0) {
|
if (stage == 0) {
|
||||||
this.btn_action.textContent = _('Check for updates');
|
this.btn_action.textContent = _('Check for updates');
|
||||||
this.btn_action.classList.remove('hidden');
|
this.btn_action.classList.remove('hidden');
|
||||||
@@ -46,146 +49,94 @@ return baseclass.extend({
|
|||||||
this.stage = stage;
|
this.stage = stage;
|
||||||
},
|
},
|
||||||
|
|
||||||
checkUpdates: function() {
|
checkUpdates: async function()
|
||||||
|
{
|
||||||
|
this._action = 'checkUpdates';
|
||||||
this.setStage(0);
|
this.setStage(0);
|
||||||
this.setBtnMode(0);
|
this.setBtnMode(0);
|
||||||
this.pkg_url = null;
|
this.pkg_url = null;
|
||||||
this.appendLog(_('Checking for updates...'));
|
this.appendLog(_('Checking for updates...'));
|
||||||
let opt_list = [ '-c' ]; // check for updates
|
let cmd = [ fn_update_pkg_sh, '-c' ]; // check for updates
|
||||||
if (document.getElementById('cfg_exclude_prereleases').checked == false) {
|
if (document.getElementById('cfg_exclude_prereleases').checked == false) {
|
||||||
opt_list.push('-p'); // include prereleases ZIP-files
|
cmd.push('-p'); // include prereleases ZIP-files
|
||||||
}
|
}
|
||||||
let forced_reinstall = document.getElementById('cfg_forced_reinstall').checked;
|
this.forced_reinstall = document.getElementById('cfg_forced_reinstall').checked;
|
||||||
let rpc_opt = { timeout: 20*1000 }
|
let log = '/tmp/'+tools.appName+'_pkg_check.log';
|
||||||
//rpc_opt.uid = 0; // run under root
|
let callback = this.execAndReadCallback;
|
||||||
let res = fs.exec(fn_update_pkg_sh, opt_list, null, rpc_opt).then(res => {
|
let wnd = this;
|
||||||
let log = res.stdout.trim();
|
return tools.execAndRead({ cmd: cmd, log: log, logArea: this.logArea, callback: callback, cbarg: wnd });
|
||||||
this.appendLog(log);
|
|
||||||
let code = log.match(/^RESULT:\s*\(([^)]+)\)\s+.+$/m);
|
|
||||||
let pkg_url = log.match(/^ZAP_PKG_URL\s*=\s*(.+)$/m);
|
|
||||||
if (res.code == 0 && code && pkg_url) {
|
|
||||||
this.pkg_url = pkg_url[1];
|
|
||||||
code = code[1];
|
|
||||||
if (code == 'E' && !forced_reinstall) {
|
|
||||||
this.setStage(999);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
this.setStage(1);
|
|
||||||
this.setBtnMode(2); // enable all buttons
|
|
||||||
} else {
|
|
||||||
if (res.code != 0) {
|
|
||||||
this.appendLog('ERROR: Check for updates failed with error ' + res.code);
|
|
||||||
}
|
|
||||||
this.setStage(999);
|
|
||||||
}
|
|
||||||
return res.code;
|
|
||||||
}).catch(e => {
|
|
||||||
this.appendLog('ERROR: ' + _('Updates checking failed'));
|
|
||||||
this.appendLog('ERROR: ' + e);
|
|
||||||
this.setStage(999);
|
|
||||||
return 1;
|
|
||||||
}).finally(() => {
|
|
||||||
this.appendLog('=========================================================');
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
installUpdates: async function() {
|
installUpdates: async function()
|
||||||
|
{
|
||||||
|
this._action = 'installUpdates';
|
||||||
this.setStage(1);
|
this.setStage(1);
|
||||||
this.setBtnMode(0);
|
this.setBtnMode(0);
|
||||||
if (!this.pkg_url || this.pkg_url.length < 10) {
|
if (!this.pkg_url || this.pkg_url.length < 10) {
|
||||||
this.appendLog('ERROR: pkg_url = null');
|
this.appendLog('ERROR: pkg_url = null');
|
||||||
this.setStage(999);
|
this.setStage(999);
|
||||||
return 1;
|
return;
|
||||||
}
|
}
|
||||||
this.appendLog(_('Install updates...'));
|
this.appendLog(_('Install updates...'));
|
||||||
let opt_list = [ '-u', this.pkg_url ]; // update packages
|
let cmd = [ fn_update_pkg_sh, '-u', this.pkg_url ]; // update packages
|
||||||
if (document.getElementById('cfg_forced_reinstall').checked == true) {
|
if (document.getElementById('cfg_forced_reinstall').checked == true) {
|
||||||
opt_list.push('-f'); // forced reinstall if same version
|
cmd.push('-f'); // forced reinstall if same version
|
||||||
}
|
}
|
||||||
let rpc_opt = { timeout: 5*1000 }
|
//this._test = 1; cmd.push('-t'); cmd.push('45'); // only for testing
|
||||||
//rpc_opt.uid = 0; // run under root
|
let log = '/tmp/'+tools.appName+'_pkg_install.log';
|
||||||
const logFile = '/tmp/'+tools.appName+'_pkg_install.log';
|
let hiderow = /^ \* resolve_conffiles.*(?:\r?\n|$)/gm;
|
||||||
const rcFile = logFile + '.rc';
|
let callback = this.execAndReadCallback;
|
||||||
try {
|
let wnd = this;
|
||||||
await fs.exec('/bin/busybox', [ 'rm', '-f', logFile + '*' ], null, rpc_opt);
|
return tools.execAndRead({ cmd: cmd, log: log, logArea: this.logArea, hiderow: hiderow, callback: callback, cbarg: wnd });
|
||||||
this.appendLog('Install log cleared.');
|
|
||||||
} catch (e) {
|
|
||||||
this.appendLog('ERROR: Failed to clear log file');
|
|
||||||
this.setStage(999);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
let opt = [ logFile, fn_update_pkg_sh ];
|
|
||||||
//opt.push('-t'); opt.push('0'); // only for testing
|
|
||||||
opt.push(...opt_list);
|
|
||||||
let res = await fs.exec('/opt/'+tools.appName+'/script-exec.sh', opt, null, rpc_opt);
|
|
||||||
if (res.code == 0) {
|
|
||||||
this.appendLog('Process started...');
|
|
||||||
} else {
|
|
||||||
this.appendLog('ERROR: cannot run ' + fn_update_pkg_sh + ' script! (error = ' + res.code + ')');
|
|
||||||
throw new Error('cannot run script');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.appendLog('ERROR: Failed to start process: ' + e.message);
|
|
||||||
this.setStage(999);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
let lastLen = 0;
|
|
||||||
let retCode = -1;
|
|
||||||
let timerBusy = false;
|
|
||||||
let timer = setInterval(async () => {
|
|
||||||
if (timerBusy)
|
|
||||||
return; // skip iteration
|
|
||||||
timerBusy = true;
|
|
||||||
try {
|
|
||||||
let res = await fs.exec('/bin/cat', [ logFile ], null, rpc_opt);
|
|
||||||
if (res.stdout && res.stdout.length > lastLen) {
|
|
||||||
let log = res.stdout.slice(lastLen);
|
|
||||||
log = log.replace(/^ \* resolve_conffiles.*(?:\r?\n|$)/gm, '');
|
|
||||||
this.appendLog(log, '');
|
|
||||||
lastLen = res.stdout.length;
|
|
||||||
}
|
|
||||||
if (retCode < 0) {
|
|
||||||
let rc = await fs.exec('/bin/cat', [ rcFile ], null, rpc_opt);
|
|
||||||
if (rc.code != 0) {
|
|
||||||
throw new Error('cannot read file "' + rcFile + '"');
|
|
||||||
}
|
|
||||||
if (rc.stdout) {
|
|
||||||
retCode = parseInt(rc.stdout.trim(), 10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (retCode >= 0) {
|
|
||||||
clearInterval(timer);
|
|
||||||
this.appendLog('\n' + 'Process finished.');
|
|
||||||
if (res.stdout) {
|
|
||||||
let code = res.stdout.match(/^RESULT:\s*\(([^)]+)\)\s+.+$/m);
|
|
||||||
if (retCode == 0 && code && code[1] == '+') {
|
|
||||||
this.stage = 999;
|
|
||||||
this.btn_action.textContent = _('OK');
|
|
||||||
this.btn_action.disabled = false;
|
|
||||||
this.btn_cancel.disabled = true;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.appendLog('ERROR: Install updates failed with error ' + retCode);
|
|
||||||
this.setStage(999);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
if (e.message?.includes('RPC call to file/exec failed with error -32000: Object not found')) {
|
|
||||||
console.warn('WARN: installUpdates: ' + e.message);
|
|
||||||
return; // goto next timer iteration
|
|
||||||
}
|
|
||||||
clearInterval(timer);
|
|
||||||
this.appendLog('ERROR: installUpdates: ' + e.message);
|
|
||||||
this.appendLog('ERROR: installUpdates: ' + e.stack?.trim().split('\n')[0]);
|
|
||||||
this.setStage(999);
|
|
||||||
} finally {
|
|
||||||
timerBusy = false;
|
|
||||||
}
|
|
||||||
}, 500);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
openUpdateDialog: function(pkg_arch) {
|
execAndReadCallback: function(wnd, rc, txt = '')
|
||||||
|
{
|
||||||
|
//console.log('execAndReadCallback = ' + rc + '; _action = ' + wnd._action);
|
||||||
|
if (rc == 0 && txt) {
|
||||||
|
let code = txt.match(/^RESULT:\s*\(([^)]+)\)\s+.+$/m);
|
||||||
|
if (wnd._action == 'checkUpdates') {
|
||||||
|
let pkg_url = txt.match(/^ZAP_PKG_URL\s*=\s*(.+)$/m);
|
||||||
|
if (code && pkg_url) {
|
||||||
|
wnd.appendLog('=========================================================');
|
||||||
|
wnd.pkg_url = pkg_url[1];
|
||||||
|
code = code[1];
|
||||||
|
if (code == 'E' && !wnd.forced_reinstall) {
|
||||||
|
wnd.setStage(999); // install not needed
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wnd.setStage(1);
|
||||||
|
wnd.setBtnMode(2); // enable all buttons
|
||||||
|
return; // install allowed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (wnd._action == 'installUpdates') {
|
||||||
|
if (wnd._test || (code && code[1] == '+')) {
|
||||||
|
wnd.stage = 999;
|
||||||
|
wnd.btn_action.textContent = _('OK');
|
||||||
|
wnd.btn_action.disabled = false;
|
||||||
|
wnd.btn_cancel.disabled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rc >= 500) {
|
||||||
|
if (txt) {
|
||||||
|
wnd.appendLog(txt.startsWith('ERROR') ? txt : 'ERROR: ' + txt);
|
||||||
|
} else {
|
||||||
|
wnd.appendLog('ERROR: ' + wnd._action + ': Terminated with error code = ' + rc);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
wnd.appendLog('ERROR: Process finished with retcode = ' + rc);
|
||||||
|
}
|
||||||
|
wnd.setStage(999);
|
||||||
|
if (wnd._action == 'checkUpdates') {
|
||||||
|
wnd.appendLog('=========================================================');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
openUpdateDialog: function(pkg_arch)
|
||||||
|
{
|
||||||
this.stage = 0;
|
this.stage = 0;
|
||||||
this.pkg_arch = pkg_arch;
|
this.pkg_arch = pkg_arch;
|
||||||
this.pkg_url = null;
|
this.pkg_url = null;
|
||||||
@@ -201,8 +152,11 @@ return baseclass.extend({
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
this.logArea = E('textarea', {
|
this.logArea = E('textarea', {
|
||||||
|
'id': 'widget.modal_content',
|
||||||
'readonly': true,
|
'readonly': true,
|
||||||
'style': 'width:100%; height:400px; font-family: monospace;'
|
'style': 'width:100% !important; font-family: monospace;',
|
||||||
|
'rows': 20,
|
||||||
|
'wrap': 'off',
|
||||||
});
|
});
|
||||||
|
|
||||||
this.btn_cancel = E('button', {
|
this.btn_cancel = E('button', {
|
||||||
|
|||||||
Reference in New Issue
Block a user