mirror of
https://codeberg.org/TicklishHoneyBee/CLIte.git
synced 2026-03-11 09:04:37 +00:00
3612 lines
87 KiB
JavaScript
3612 lines
87 KiB
JavaScript
var clite = {
|
|
state:{
|
|
version:'0.8.0',
|
|
isinit:false,
|
|
runlevel:1,
|
|
bios:null,
|
|
cookiesAccepted:null
|
|
},
|
|
includes:{
|
|
// a list of program/command files to load
|
|
prog:['commands.js','shell.js','vi.js','user.js','build.js'],
|
|
// a list of library files to load
|
|
libs:['libclite.js','libcurses.js','libstd.js','libstdio.js','libterm.js','libtime.js','libauth.js']
|
|
},
|
|
time:{
|
|
sec:function() {
|
|
return parseInt(Date.now()/1000.0,10);
|
|
},
|
|
msec:function() {
|
|
return Date.now();
|
|
},
|
|
nsec:function() {
|
|
return parseInt(Date.now()*1000,10);
|
|
}
|
|
},
|
|
core:{
|
|
execSafe:function(f) {
|
|
try{
|
|
f();
|
|
} catch(e) {
|
|
clite.log.write(e.message);
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
execSafeAsync:function(f,delay) {
|
|
if (typeof delay !== 'number')
|
|
delay = 10;
|
|
try{
|
|
setTimeout(f,delay);
|
|
} catch(e) {
|
|
clite.log.write(e.message);
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
download:function(name,data) {
|
|
var link = document.createElement("a");
|
|
link.target = '_blank';
|
|
link.download = name;
|
|
var blob = new Blob([data], {type: "text/plain"});
|
|
link.href = URL.createObjectURL(blob);
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
URL.revokeObjectURL(link.href);
|
|
document.body.removeChild(link);
|
|
},
|
|
reboot:function() {
|
|
clite.log.write('The system is rebooting');
|
|
setTimeout(function() {
|
|
clite.console.clear();
|
|
clite.log.write('Bringing down the system');
|
|
clite.log.write('Resetting system core');
|
|
var b = clite.state.bios;
|
|
delete clite;
|
|
clite = null;
|
|
b.io.write(4,true);
|
|
},10);
|
|
},
|
|
shutdown:function() {
|
|
clite.log.write('The system is shutting down');
|
|
setTimeout(function() {
|
|
clite.console.clear();
|
|
clite.log.write('Bringing down the system');
|
|
delete clite;
|
|
clite = null;
|
|
b.io.write(4,false);
|
|
},10);
|
|
},
|
|
hostname:function() {
|
|
if (window.location.protocol != 'file:')
|
|
return window.location.hostname;
|
|
return 'localhost';
|
|
}
|
|
},
|
|
init:function(bios) {
|
|
if (clite.state.isinit)
|
|
return;
|
|
clite.state.isinit = true;
|
|
|
|
clite.state.bios = bios;
|
|
clite.state.bios.io.write(0,'CLIte '+clite.state.version);
|
|
|
|
clite.core.execSafeAsync(function() {
|
|
clite.console.init();
|
|
var vfsapi = null;
|
|
|
|
function defaultDevices() {
|
|
// writing to /dev/local downloads data {name:'filename to save',data:'file content'}
|
|
{
|
|
vfsapi.mkFile(0,'/dev/local');
|
|
let n = vfsapi.getNode(0,'/dev/local');
|
|
if (!n) {
|
|
clite.log.write('local device failure');
|
|
return false;
|
|
}
|
|
n.data.content = {
|
|
read:function(cb) {
|
|
clite.state.bios.core.load.localfile('',cb);
|
|
return true;
|
|
},
|
|
write:function(obj) {
|
|
if (typeof obj === 'string') {
|
|
var n = 'data.txt';
|
|
var d = obj;
|
|
var ind = obj.indexOf(String.fromCharCode(2));
|
|
if (ind > -1) {
|
|
n = obj.substring(0,ind);
|
|
d = obj.substring(ind+1);
|
|
}
|
|
clite.core.download(n,d);
|
|
}else if (typeof obj.name === 'string' && typeof obj.data === 'string') {
|
|
clite.core.download(obj.name,obj.data);
|
|
}else{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
n.mode = clite.lib.modestr('crw-rw-rw-');
|
|
n.data.isdev = true;
|
|
}
|
|
// /dev/null - writing to it goes nowhere, reading from it is always null
|
|
{
|
|
vfsapi.mkFile(0,'/dev/null');
|
|
let n = vfsapi.getNode(0,'/dev/null');
|
|
if (!n) {
|
|
clite.log.write('null device failure');
|
|
return false;
|
|
}
|
|
n.data.content = {
|
|
read:function() {
|
|
return null;
|
|
},
|
|
write:function(v) {
|
|
return true;
|
|
}
|
|
};
|
|
n.mode = clite.lib.modestr('crw-rw-rw-');
|
|
n.data.isdev = true;
|
|
}
|
|
// /dev/random - writing to it goes nowhere, reading from it returns a stringified random number (integer)
|
|
{
|
|
vfsapi.mkFile(0,'/dev/random');
|
|
let n = vfsapi.getNode(0,'/dev/random');
|
|
if (!n) {
|
|
clite.log.write('random device failure');
|
|
return false;
|
|
}
|
|
n.data.content = {
|
|
read:function() {
|
|
return Math.floor(Math.random() * (Number.MAX_SAFE_INTEGER - Number.MIN_SAFE_INTEGER + 1) + Number.MIN_SAFE_INTEGER).toString();
|
|
},
|
|
write:null
|
|
};
|
|
n.mode = clite.lib.modestr('crw-rw-rw-');
|
|
n.data.isdev = true;
|
|
}
|
|
// /dev/initctl - writing to it sets the runlevel, reading from it returns the current runlevel
|
|
{
|
|
vfsapi.mkFile(0,'/dev/initctl');
|
|
let n = vfsapi.getNode(0,'/dev/initctl');
|
|
if (!n) {
|
|
clite.log.write('initctl device failure');
|
|
return false;
|
|
}
|
|
n.data.content = {
|
|
data:1,
|
|
read:function() {
|
|
return n.data.content.data.toString();
|
|
},
|
|
write:function(rl) {
|
|
var rli = parseInt(rl);
|
|
switch (rli) {
|
|
case 0:
|
|
clite.state.runlevel = rli;
|
|
n.data.content.data = 0;
|
|
clite.core.shutdown();
|
|
break;
|
|
case 1: // essentially the booting runlevel
|
|
clite.state.runlevel = rli;
|
|
n.data.content.data = 1;
|
|
break;
|
|
case 3: // really just a status change, doesn't "do" anything
|
|
clite.state.runlevel = rli;
|
|
clite.init = null;
|
|
n.data.content.data = 3;
|
|
break;
|
|
case 6:
|
|
clite.state.runlevel = rli;
|
|
n.data.content.data = 6;
|
|
clite.core.reboot();
|
|
break;
|
|
default:;
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
n.mode = clite.lib.modestr('crw-rw-rw-');
|
|
n.data.isdev = true;
|
|
}
|
|
// /dev/console - an interface for the system console - required by posix/sus, currently no op
|
|
{
|
|
vfsapi.mkFile(0,'/dev/console');
|
|
let n = vfsapi.getNode(0,'/dev/console');
|
|
if (!n) {
|
|
clite.log.write('console device failure');
|
|
return false;
|
|
}
|
|
n.data.content = {
|
|
read:clite.console.read,
|
|
write:clite.console.write
|
|
};
|
|
n.mode = clite.lib.modestr('crw-rw-rw-');
|
|
n.data.isdev = true;
|
|
}
|
|
// /dev/tty - virtual device that always acts like the controlling terminal
|
|
{
|
|
vfsapi.mkFile(0,'/dev/tty');
|
|
let n = vfsapi.getNode(0,'/dev/tty');
|
|
if (!n) {
|
|
clite.log.write('tty failure');
|
|
return false;
|
|
}
|
|
n.data.content = {
|
|
read:clite.tty.read,
|
|
write:clite.tty.write
|
|
};
|
|
n.mode = clite.lib.modestr('crw-rw-rw-');
|
|
n.data.isdev = true;
|
|
n.data.istty = true;
|
|
}
|
|
// /dev/time - reading from it returns the current unix epoch time UTC
|
|
{
|
|
vfsapi.mkFile(0,'/dev/time');
|
|
let n = vfsapi.getNode(0,'/dev/time');
|
|
if (!n) {
|
|
clite.log.write('time device failure');
|
|
return false;
|
|
}
|
|
n.data.content = {
|
|
read:function() {
|
|
return clite.time.msec().toString();
|
|
},
|
|
write:null
|
|
};
|
|
n.mode = clite.lib.modestr('crw-rw-rw-');
|
|
n.data.isdev = true;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function defaultConfig() {
|
|
// /etc/env contains the default environment variables
|
|
{
|
|
vfsapi.mkFile(0,'/etc/env');
|
|
let n = vfsapi.getNode(0,'/etc/env');
|
|
if (!n) {
|
|
clite.log.write('environment failure');
|
|
return false;
|
|
}
|
|
n.data.content = `
|
|
PATH=/bin
|
|
EDITOR=/bin/vi
|
|
`;
|
|
n.mode = clite.lib.modestr('-rw-r--r--');
|
|
}
|
|
// /etc/passwd contains user account details
|
|
{
|
|
vfsapi.mkFile(0,'/etc/passwd');
|
|
let n = vfsapi.getNode(0,'/etc/passwd');
|
|
if (!n) {
|
|
clite.log.write('access config failure');
|
|
return false;
|
|
}
|
|
n.data.content = `
|
|
root:$-7a1c0437:0:0:root:/root:/bin/sh
|
|
guest:x:1:1:guest:/usr/home/guest:/bin/sh
|
|
`;
|
|
n.mode = clite.lib.modestr('-rw-------');
|
|
}
|
|
// /etc/group contains group details
|
|
{
|
|
vfsapi.mkFile(0,'/etc/group');
|
|
let n = vfsapi.getNode(0,'/etc/group');
|
|
if (!n) {
|
|
clite.log.write('group config failure');
|
|
return false;
|
|
}
|
|
n.data.content = `
|
|
root:x:0:root
|
|
guest:x:1:guest
|
|
`;
|
|
n.mode = clite.lib.modestr('-rw-r--r--');
|
|
}
|
|
// /etc/greeting is displayed by the shell after login
|
|
{
|
|
vfsapi.mkFile(0,'/etc/greeting');
|
|
let n = vfsapi.getNode(0,'/etc/greeting');
|
|
if (!n) {
|
|
clite.log.write('config failure');
|
|
return false;
|
|
}
|
|
n.data.content =`
|
|
_____ _ _____ _
|
|
/ ____| | |_ _| |
|
|
| | | | | | | |_ ___
|
|
| | | | | | | __/ _ \\
|
|
| |____| |____ _| |_| || __/
|
|
\\_____|______|_____|\\__\\___|
|
|
`;
|
|
n.mode = clite.lib.modestr('-rw-r--r--');
|
|
}
|
|
// /etc/shrc shell startup file
|
|
{
|
|
vfsapi.mkFile(0,'/etc/shrc');
|
|
let n = vfsapi.getNode(0,'/etc/shrc');
|
|
if (!n) {
|
|
clite.log.write('shell config failure');
|
|
return false;
|
|
}
|
|
n.data.content =`#!/bin/sh
|
|
cat /etc/greeting
|
|
`;
|
|
n.mode = clite.lib.modestr('-rwxr-xr-x');
|
|
}
|
|
}
|
|
|
|
function loadCommands(nextfn) {
|
|
clite.commands = {
|
|
data:null,
|
|
load:function(name,fn,suid,sgid) {
|
|
vfsapi.mkFile(0,'/bin/'+name);
|
|
var f = vfsapi.getNode(0,'/bin/'+name);
|
|
if (!f)
|
|
return;
|
|
f.mode = clite.lib.modestr('-rwxr-xr-x');
|
|
if (typeof fn === 'string') {
|
|
f.data.content = new Function('args','env','io',fn+';return main(args.length,args);');
|
|
}else{
|
|
f.data.content = fn;
|
|
}
|
|
if (typeof suid === 'boolean' && suid == true)
|
|
f.mode |= clite.io.modes.S_ISUID;
|
|
if (typeof sgid === 'boolean' && sgid == true)
|
|
f.mode |= clite.io.modes.S_ISGID;
|
|
vfsapi.mkFile(0,'/usr/src/'+name+'.js');
|
|
f = vfsapi.getNode(0,'/usr/src/'+name+'.js');
|
|
if (!f)
|
|
return;
|
|
f.mode = clite.lib.modestr('-rw-rw-r--');
|
|
if (typeof fn === 'string') {
|
|
f.data.content = fn;
|
|
}else{
|
|
f.data.content = fn.toString().substring(23);
|
|
var e = f.data.content.lastIndexOf('return main(');
|
|
if (e > -1)
|
|
f.data.content = f.data.content.substring(0,e);
|
|
}
|
|
}
|
|
}
|
|
|
|
var index = 0;
|
|
|
|
function cb() {
|
|
if (clite.commands.data)
|
|
clite.core.execSafe(clite.commands.data);
|
|
clite.commands.data = null;
|
|
|
|
if (index >= clite.includes.prog.length) {
|
|
clite.commands = null;
|
|
clite.core.execSafeAsync(nextfn);
|
|
return;
|
|
}
|
|
|
|
var f = clite.includes.prog[index];
|
|
index++;
|
|
clite.state.bios.core.load.script('clite/'+f,cb);
|
|
}
|
|
cb();
|
|
}
|
|
|
|
function loadLibs(nextfn) {
|
|
clite.libs = {
|
|
index:[],
|
|
data:null,
|
|
load:function(name,header,fn) {
|
|
vfsapi.mkFile(0,'/lib/'+name+'.so');
|
|
var f = vfsapi.getNode(0,'/lib/'+name+'.so');
|
|
if (!f)
|
|
return;
|
|
f.mode = clite.lib.modestr('-rw-r--r--');
|
|
f.data.content = fn;
|
|
vfsapi.mkFile(0,'/usr/src/libs/'+name+'.js');
|
|
f = vfsapi.getNode(0,'/usr/src/libs/'+name+'.js');
|
|
if (!f)
|
|
return;
|
|
f.mode = clite.lib.modestr('-rw-rw-r--');
|
|
f.data.content = 'function init'+fn.toString().substring(8);
|
|
clite.libs.index.push({header:header,file:name});
|
|
}
|
|
}
|
|
|
|
var index = 0;
|
|
|
|
function cb() {
|
|
if (clite.libs.data)
|
|
clite.core.execSafe(clite.libs.data);
|
|
clite.libs.data = null;
|
|
|
|
if (index >= clite.includes.libs.length) {
|
|
clite.libs.load = null;
|
|
clite.core.execSafeAsync(nextfn);
|
|
return;
|
|
}
|
|
|
|
var f = clite.includes.libs[index];
|
|
index++;
|
|
clite.state.bios.core.load.script('clite/libs/'+f,cb);
|
|
}
|
|
cb();
|
|
}
|
|
|
|
function init1() {
|
|
clite.log.write('CLIte Version '+clite.state.version);
|
|
// setup vfs
|
|
clite.log.write('Setting up VFS');
|
|
clite.vfs.init();
|
|
vfsapi = clite.vfs.getApi();
|
|
clite.log.write('Setting up TTY');
|
|
clite.tty.init(vfsapi);
|
|
clite.log.init(vfsapi);
|
|
clite.log.write('Mounting wfs on /');
|
|
// mount core (root) filesystem using data/filesys.txt
|
|
if (window.location.protocol == 'file:') {
|
|
// work around the file: problems by just not loading files, here's a temporary filesystem
|
|
var d = `
|
|
clite/core.js:/usr/clite/core.js:0:0:-rw-r--r--
|
|
clite/core.css:/usr/clite/web/core.css:0:0:-rw-r--r--
|
|
data/intro.txt:/usr/share/introduction:0:0:-rw-rw-r--
|
|
data/about.txt:/usr/share/site/about:0:0:-rw-rw-r--
|
|
readme.txt:/usr/clite/readme:0:0:-rw-r--r--
|
|
license.txt:/etc/license:0:0:-rw-r--r--`;
|
|
init2(d);
|
|
return;
|
|
}
|
|
clite.state.bios.core.load.file('data/filesys.txt',init2);
|
|
}
|
|
function init2(data) {
|
|
if (data == null) {
|
|
clite.log.write('No Filesystem Found');
|
|
return;
|
|
}
|
|
vfsapi.mkFile(0,'/dev/wfs');
|
|
var n = vfsapi.getNode(0,'/dev/wfs');
|
|
if (!n) {
|
|
clite.log.write('wfs device failure');
|
|
return;
|
|
}
|
|
n.data.content = data;
|
|
n.mode = clite.lib.modestr('cr--r--r--');
|
|
n.data.isdev = true;
|
|
var fd = clite.io.open(0,'/dev/wfs',clite.io.flags.O_RDONLY|clite.io.flags.O_SYNC);
|
|
var l;
|
|
while ((l = clite.io.readLine(0,fd)) != null) {
|
|
if (l.length <1 || l[0] == '#')
|
|
continue;
|
|
var parts = l.split(':');
|
|
if (parts.length != 5)
|
|
continue;
|
|
var url = parts[0];
|
|
var path = parts[1];
|
|
var uid = parseInt(parts[2]);
|
|
var gid = parseInt(parts[3]);
|
|
var perms = parts[4];
|
|
|
|
var fn = vfsapi.getNode(0,path);
|
|
if (!fn) {
|
|
if (perms[0] == 'd') {
|
|
vfsapi.mkPath(0,path);
|
|
}else{
|
|
var dir = clite.lib.dirname(path);
|
|
vfsapi.mkPath(0,dir);
|
|
vfsapi.mkFile(0,path);
|
|
}
|
|
fn = vfsapi.getNode(0,path);
|
|
}
|
|
if (!fn)
|
|
continue;
|
|
fn.mode = clite.lib.modestr(perms);
|
|
fn.uid = uid;
|
|
fn.gid = gid;
|
|
fn.name = clite.lib.basename(path);
|
|
fn.data.remote = url;
|
|
fn.data.content = null;
|
|
}
|
|
clite.io.close(0,fd);
|
|
// populate /etc (mostly for environment)
|
|
clite.log.write('Populating /etc');
|
|
defaultConfig();
|
|
var b = clite.state.bios.io.read(2);
|
|
if (b) {
|
|
let list = b.getList();
|
|
// if we're restoring data, assume cookies were accepted previously
|
|
if (list.length > 0)
|
|
clite.state.cookiesAccepted = true;
|
|
list.forEach(function(fn) {
|
|
clite.vfs.restoreFile(fn);
|
|
});
|
|
}
|
|
// populate /dev (data/filesys.txt is /dev/wfs already)
|
|
clite.log.write('Populating /dev');
|
|
defaultDevices();
|
|
// populate /bin
|
|
clite.log.write('Populating /bin');
|
|
loadCommands(init3);
|
|
}
|
|
function init3() {
|
|
clite.log.write('Populating /lib');
|
|
loadLibs(init4);
|
|
}
|
|
function init4() {
|
|
clite.log.write('Bringing up the process manager');
|
|
if (!clite.proc.init(vfsapi)) {
|
|
clite.log.write('failed');
|
|
return;
|
|
}
|
|
|
|
var fd = clite.io.open(0,'/dev/initctl',clite.io.flags.O_WRONLY);
|
|
if (fd) {
|
|
clite.io.write(0,fd,'3');
|
|
clite.io.close(0,fd);
|
|
}
|
|
// check cookies for login
|
|
clite.log.write('Checking for user session');
|
|
clite.user.init(vfsapi);
|
|
|
|
{
|
|
// create the login environment
|
|
// spawns /bin/login on pid 1, optionally with default guest login
|
|
var env = clite.user.getEnv(0);
|
|
var io = {
|
|
pid:0,
|
|
stdin:clite.io.open(0,'/dev/tty0',clite.io.flags.O_RDONLY),
|
|
stdout:clite.io.open(0,'/dev/tty0',clite.io.flags.O_WRONLY),
|
|
stderr:clite.io.open(0,'/dev/tty0',clite.io.flags.O_WRONLY),
|
|
exit:null, // filled in by fork()
|
|
include:null, // filled in by fork()
|
|
};
|
|
var pid = clite.lib.fork(env,io,init5);
|
|
}
|
|
}
|
|
function init5(env,io) {
|
|
clite.tty.clear(0);
|
|
var args = ['login'];
|
|
// check for user-created account with password set
|
|
if (!clite.user.hasAltLogin())
|
|
args.push('guest');
|
|
clite.lib.exec('/bin/login',args,env,io);
|
|
}
|
|
|
|
init1();
|
|
|
|
});
|
|
}
|
|
};
|
|
|
|
clite.proc = {
|
|
init:function(vfs) {
|
|
var vfsapi = vfs;
|
|
var data = {
|
|
nextid:1,
|
|
procs:[],
|
|
groups:[]
|
|
};
|
|
|
|
function getProc(pid) {
|
|
var proc = null;
|
|
data.procs.forEach(function(p,i) {
|
|
if (proc)
|
|
return;
|
|
if (p.pid == pid)
|
|
proc = p;
|
|
});
|
|
return proc;
|
|
}
|
|
function getProcIndex(pid) {
|
|
var index = -1;
|
|
if (pid < 0)
|
|
return data.procs.length-1;
|
|
data.procs.forEach(function(p,i) {
|
|
if (index > -1)
|
|
return;
|
|
if (p.pid == pid)
|
|
index = i;
|
|
});
|
|
return index;
|
|
}
|
|
function mapCPID(cpid) {
|
|
//clite.log.write('mapCPID('+cpid+') -> '+data.groups.length);
|
|
if (data.groups.length < 1) {
|
|
addGroup(1);
|
|
return 1;
|
|
}
|
|
var parent = getProc(cpid);
|
|
if (parent && parent.gpid != cpid)
|
|
return parent.gpid;
|
|
return cpid;
|
|
}
|
|
function getGroup(cpid) {
|
|
for (var i=0; i<data.groups.length; i++) {
|
|
if (data.groups[i].gpid == cpid)
|
|
return data.groups[i];
|
|
}
|
|
return null;
|
|
}
|
|
function addGroup(cpid) {
|
|
if (getGroup(cpid))
|
|
return;
|
|
data.groups.push({gpid:cpid,waitAny:[],waitAll:[]});
|
|
}
|
|
function exitGroup(cpid,pid,ev) {
|
|
var group = getGroup(cpid);
|
|
if (!group)
|
|
return false;
|
|
group.waitAny.forEach(function(w) {
|
|
w(pid);
|
|
});
|
|
group.waitAny = [];
|
|
var c = 0;
|
|
data.procs.forEach(function(p) {
|
|
// we want a count of child processes, so ignore the controlling process
|
|
if (p.gpid == cpid && p.pid != cpid)
|
|
c++;
|
|
});
|
|
if (c > 0)
|
|
return true;
|
|
group.waitAll.forEach(function(w) {
|
|
w(pid,ev);
|
|
});
|
|
group.waitAll = [];
|
|
return true;
|
|
}
|
|
|
|
clite.proc.add = function(cpid,fn) {
|
|
if (typeof fn !== 'function')
|
|
return 0;
|
|
var proc = Object.create({
|
|
ruid:0, // the real user id - owner of the process
|
|
rgid:0, // the real group id - owner of the process
|
|
uid:0, // the (effective) user id this process is running as
|
|
gid:0, // the (effective) group id this process is running as
|
|
gpid:mapCPID(cpid), // the group pid (parent process id) of this process
|
|
pid:data.nextid++, // the pid (process id) of this process
|
|
ctty:0, // the id of the controlling tty for this process
|
|
obj:fn, // the function this process executes
|
|
waits:[] // array of wait() calls pending for this process on exit
|
|
});
|
|
var parent = getProc(proc.gpid);
|
|
if (parent) {
|
|
proc.uid = parent.uid;
|
|
proc.gid = parent.gid;
|
|
proc.ruid = parent.ruid;
|
|
proc.rgid = parent.rgid;
|
|
proc.ctty = parent.ctty;
|
|
}
|
|
data.procs.push(proc);
|
|
vfsapi.mkFile(0,'/proc/'+proc.pid);
|
|
var n = vfsapi.getNode(0,'/proc/'+proc.pid);
|
|
n.data.content = 'pid: '+proc.pid+'\n';
|
|
return proc.pid;
|
|
}
|
|
clite.proc.update = function(pid,cl,obj) {
|
|
var proc = getProc(pid);
|
|
if (!proc)
|
|
return false;
|
|
var n = vfsapi.getNode(0,'/proc/'+proc.pid);
|
|
if (!n)
|
|
return false;
|
|
proc.obj = obj;
|
|
n.data.content += 'command: '+cl+'\n';
|
|
return true;
|
|
}
|
|
clite.proc.exit = function(pid,ev) {
|
|
var i = getProcIndex(pid);
|
|
if (i<0)
|
|
return false;
|
|
var proc = getProc(pid);
|
|
if (!proc)
|
|
return false;
|
|
data.procs.splice(i,1);
|
|
vfsapi.remove(0,'/proc/'+pid);
|
|
proc.waits.forEach(function(w) {
|
|
clite.core.execSafeAsync(function() {w(pid,ev)});
|
|
});
|
|
exitGroup(proc.gpid,pid,ev);
|
|
// special case, reboot if pid 1 exits
|
|
if (pid == 1) {
|
|
clite.core.execSafeAsync(function() {
|
|
var fd = clite.io.open(0,'/dev/initctl',clite.io.flags.O_WRONLY);
|
|
if (!fd) {
|
|
clite.core.reboot();
|
|
return;
|
|
}
|
|
clite.io.write(0,fd,6);
|
|
clite.io.close(0,fd);
|
|
});
|
|
}
|
|
return true;
|
|
}
|
|
clite.proc.count = function() {
|
|
return data.procs.length;
|
|
}
|
|
clite.proc.addWait = function(pid,w) {
|
|
var proc = getProc(pid);
|
|
if (!proc) {
|
|
clite.core.execSafeAsync(function() {w(pid)});
|
|
return false;
|
|
}
|
|
proc.waits.push(w);
|
|
return true;
|
|
}
|
|
clite.proc.addWaitGroupAny = function(pid,w) {
|
|
var cpid = mapCPID(pid);
|
|
var group = getGroup(cpid);
|
|
if (!group) {
|
|
clite.core.execSafeAsync(function() {w(pid)});
|
|
return true;
|
|
}
|
|
group.waitAny.push(w);
|
|
return true;
|
|
}
|
|
clite.proc.addWaitGroupAll = function(pid,w) {
|
|
var cpid = mapCPID(pid);
|
|
var group = getGroup(cpid);
|
|
if (!group) {
|
|
clite.core.execSafeAsync(function() {w(pid)});
|
|
return false;
|
|
}
|
|
group.waitAll.push(w);
|
|
return true;
|
|
}
|
|
clite.proc.addGroup = function(pid) {
|
|
var proc = getProc(pid);
|
|
if (!proc)
|
|
return false;
|
|
if (proc.gpid == pid)
|
|
return true;
|
|
proc.gpid = pid;
|
|
if (getGroup(pid))
|
|
return true;
|
|
addGroup(pid);
|
|
return true;
|
|
}
|
|
clite.proc.getUID = function(pid) {
|
|
var proc = getProc(pid);
|
|
if (!proc)
|
|
return 0;
|
|
return proc.uid;
|
|
}
|
|
clite.proc.getGID = function(pid) {
|
|
var proc = getProc(pid);
|
|
if (!proc)
|
|
return 0;
|
|
return proc.gid;
|
|
}
|
|
clite.proc.getRUID = function(pid) {
|
|
var proc = getProc(pid);
|
|
if (!proc)
|
|
return 0;
|
|
return proc.ruid;
|
|
}
|
|
clite.proc.getRGID = function(pid) {
|
|
var proc = getProc(pid);
|
|
if (!proc)
|
|
return 0;
|
|
return proc.rgid;
|
|
}
|
|
clite.proc.setUID = function(pid,uid,force) {
|
|
var proc = getProc(pid);
|
|
if (!proc)
|
|
return false;
|
|
if (!clite.user.getPWData(uid))
|
|
return false;
|
|
if (!force && proc.uid != 0)
|
|
return false;
|
|
proc.uid = uid;
|
|
return true;
|
|
}
|
|
clite.proc.setGID = function(pid,gid,force) {
|
|
var proc = getProc(pid);
|
|
if (!proc)
|
|
return false;
|
|
if (!clite.user.getGRData(gid))
|
|
return false;
|
|
if (!force && gid == 0 && clite.proc.getUID(pid) != 0)
|
|
return false;
|
|
proc.gid = gid;
|
|
return true;
|
|
}
|
|
clite.proc.setRUID = function(pid,uid,force) {
|
|
var proc = getProc(pid);
|
|
if (!proc)
|
|
return false;
|
|
if (!clite.user.getPWData(uid))
|
|
return false;
|
|
if (!force && proc.uid != 0)
|
|
return false;
|
|
addGroup(pid);
|
|
proc.gpid = pid;
|
|
proc.ruid = uid;
|
|
return true;
|
|
}
|
|
clite.proc.setRGID = function(pid,gid,force) {
|
|
var proc = getProc(pid);
|
|
if (!proc)
|
|
return false;
|
|
if (!clite.user.getGRData(gid))
|
|
return false;
|
|
if (!force && gid == 0 && proc.uid != 0)
|
|
return false;
|
|
proc.rgid = gid;
|
|
return true;
|
|
}
|
|
clite.proc.getTTY = function(pid) {
|
|
var proc = getProc(pid);
|
|
if (!proc)
|
|
return -1;
|
|
return proc.ctty;
|
|
}
|
|
clite.proc.setLogin = function(pid,uid) {
|
|
var proc = getProc(pid);
|
|
if (!proc)
|
|
return false;
|
|
proc.ruid = uid;
|
|
proc.uid = uid;
|
|
proc.rgid = clite.user.getGID(uid);
|
|
proc.gid = proc.rgid;
|
|
return true;
|
|
}
|
|
clite.proc.dump = function() {
|
|
var r = '';
|
|
data.procs.forEach(function(p) {
|
|
r += '{\n';
|
|
r += ' ruid:'+p.ruid+',\n';
|
|
r += ' rgid:'+p.rgid+',\n';
|
|
r += ' uid:'+p.uid+',\n';
|
|
r += ' gid:'+p.gid+',\n';
|
|
r += ' gpid:'+p.gpid+',\n';
|
|
r += ' pid:'+p.pid+',\n';
|
|
r += ' ctty:'+p.ctty+'\n';
|
|
r += '}\n';
|
|
});
|
|
return r;
|
|
}
|
|
clite.proc.init = null;
|
|
return true;
|
|
},
|
|
add:function(cl,fn) {return 0;},
|
|
update:function(cl) {return false;},
|
|
exit:function(pid) {},
|
|
count:function() {return 0;},
|
|
addWait:function() {return false;},
|
|
addWaitGroupAny:function() {return false;},
|
|
addWaitGroupAll:function() {return false;},
|
|
addGroup:function() {return false;},
|
|
getUID:function(pid) {return 0;},
|
|
getGID:function(pid) {return 0;},
|
|
getTTY:function(pid) {return 0;},
|
|
setLogin:function(pid,uid) {return false;}
|
|
};
|
|
|
|
clite.user = {
|
|
init:function(vfsapi) { // returns true if there's a user login or false for guest
|
|
var data = {
|
|
users:[{
|
|
name:'root',
|
|
pass:'',
|
|
uid:0,
|
|
gid:0,
|
|
groups:[],
|
|
env:{
|
|
HOME:'/',
|
|
USER:this.name,
|
|
}
|
|
}],
|
|
groups:[{
|
|
name:'root',
|
|
gid:0,
|
|
users:[]
|
|
}]
|
|
};
|
|
var env = {};
|
|
function getNextUid() {
|
|
let id = data.users.length;
|
|
let accepted = false;
|
|
while (!accepted) {
|
|
accepted = true;
|
|
for (var i=0; i<data.users.length; i++) {
|
|
if (data.users[i].uid == id)
|
|
accepted = false;
|
|
}
|
|
if (!accepted)
|
|
id++;
|
|
}
|
|
return id;
|
|
}
|
|
function getNextGid() {
|
|
let id = data.groups.length;
|
|
let accepted = false;
|
|
while (!accepted) {
|
|
accepted = true;
|
|
for (var i=0; i<data.groups.length; i++) {
|
|
if (data.groups[i].gid == id)
|
|
accepted = false;
|
|
}
|
|
if (!accepted)
|
|
id++;
|
|
}
|
|
return id;
|
|
}
|
|
function getUser(uid) {
|
|
for (var i=0; i<data.users.length; i++) {
|
|
if (data.users[i].uid == uid)
|
|
return data.users[i];
|
|
}
|
|
return null;
|
|
}
|
|
function getUserByName(n) {
|
|
for (var i=0; i<data.users.length; i++) {
|
|
if (data.users[i].name == n)
|
|
return data.users[i];
|
|
}
|
|
return null;
|
|
}
|
|
function getGroup(gid) {
|
|
for (var i=0; i<data.groups.length; i++) {
|
|
if (data.groups[i].gid == gid)
|
|
return data.groups[i];
|
|
}
|
|
return null;
|
|
}
|
|
function saveUsers() {
|
|
var n = vfsapi.getNode(0,'/etc/passwd');
|
|
if (!n)
|
|
return false;
|
|
n.data.content = '';
|
|
//root:$-7a1c0437:0:0:root:/root:/bin/sh
|
|
data.users.forEach(function(u) {
|
|
var pw = 'x';
|
|
if (u.pass != 'x')
|
|
pw = '$'+u.pass;
|
|
n.data.content += u.name+':'+pw+':'+u.uid+':'+u.gid+'::'+u.env.HOME+':'+u.env.SHELL+'\n';
|
|
});
|
|
clite.vfs.saveFile('/etc/passwd');
|
|
n = vfsapi.getNode(0,'/etc/group');
|
|
if (!n)
|
|
return false;
|
|
n.data.content = '';
|
|
//root:x:0:
|
|
data.groups.forEach(function(g) {
|
|
n.data.content += g.name+':x:'+g.gid+':'+g.users.join(',')+'\n'
|
|
});
|
|
clite.vfs.saveFile('/etc/group');
|
|
}
|
|
function loadUsers() {
|
|
// load in user data from /etc/passwd
|
|
var n = vfsapi.getFile(0,'/etc/passwd');
|
|
if (!n)
|
|
return false;
|
|
var lines = n.split('\n');
|
|
lines.forEach(function(line) {
|
|
if (line[0] == '#')
|
|
return;
|
|
var sects = line.split(':');
|
|
// 0 username
|
|
// 1 password hash
|
|
// 2 uid
|
|
// 3 gid
|
|
// 4 real name
|
|
// 5 home dir
|
|
// 6 shell
|
|
if (sects.length != 7)
|
|
return;
|
|
var uid = parseInt(sects[2]);
|
|
var udata = getUser(uid);
|
|
if (!udata) {
|
|
udata = {};
|
|
data.users.push(udata);
|
|
}
|
|
|
|
udata.name = sects[0];
|
|
udata.pass = sects[1];
|
|
if (udata.pass[0] == '$')
|
|
udata.pass = udata.pass.substring(1);
|
|
udata.uid = parseInt(sects[2]);
|
|
udata.gid = parseInt(sects[3]);
|
|
udata.groups = [];
|
|
udata.env = {};
|
|
udata.env.HOME = sects[5];
|
|
udata.env.SHELL = sects[6];
|
|
udata.env.USER = udata.name;
|
|
udata.env.PWD = udata.env.HOME;
|
|
});
|
|
// load in data from /etc/group
|
|
n = vfsapi.getFile(0,'/etc/group');
|
|
if (!n)
|
|
return false;
|
|
var lines = n.split('\n');
|
|
lines.forEach(function(line) {
|
|
if (line[0] == '#')
|
|
return;
|
|
var sects = line.split(':');
|
|
// 0 groupname
|
|
// 1 null
|
|
// 2 gid
|
|
// 3 csv users
|
|
if (sects.length != 4)
|
|
return;
|
|
var gid = parseInt(sects[2]);
|
|
var gdata = getGroup(gid);
|
|
if (!gdata) {
|
|
gdata = {};
|
|
data.groups.push(gdata);
|
|
}
|
|
|
|
gdata.name = sects[0];
|
|
gdata.gid = parseInt(sects[2]);
|
|
gdata.users = sects[3].split(',');
|
|
gdata.users.forEach(function(u) {
|
|
var ud = getUserByName(u);
|
|
if (ud)
|
|
ud.groups.push(gdata.name);
|
|
});
|
|
});
|
|
|
|
// load the environment
|
|
n = vfsapi.getFile(0,'/etc/env');
|
|
if (!n)
|
|
return false;
|
|
lines = n.split('\n');
|
|
lines.forEach(function(line) {
|
|
if (line[0] == '#')
|
|
return;
|
|
var sects = line.split('=');
|
|
if (sects.length != 2)
|
|
return;
|
|
var name = sects[0].trim();
|
|
var value = sects[1].trim();
|
|
env[name] = value;
|
|
data.users.forEach(function(u) {
|
|
u.env[name] = value;
|
|
});
|
|
});
|
|
|
|
return true;
|
|
}
|
|
clite.user.checkGID = function(uid,gid) {
|
|
var udata = getUser(uid);
|
|
if (!udata)
|
|
return false;
|
|
if (gid == udata.gid)
|
|
return true;
|
|
if (udata.groups.indexOf(gid) > -1)
|
|
return true;
|
|
return false;
|
|
}
|
|
clite.user.getGID = function(uid) {
|
|
var udata = getUser(uid);
|
|
if (!udata)
|
|
return 0;
|
|
return udata.gid;
|
|
}
|
|
clite.user.genGuest = function() {
|
|
vfsapi.mkDir(0,'/usr/home/guest');
|
|
var n = vfsapi.getNode(0,'/usr/home/guest');
|
|
if (n) {
|
|
n.mode = clite.lib.modestr('drwx------');
|
|
n.uid = 1;
|
|
n.gid = 1;
|
|
}
|
|
vfsapi.mkLink(1,'/usr/home/guest/web','/usr/share/site');
|
|
|
|
vfsapi.mkFile(1,'/usr/home/guest/.shrc');
|
|
n = vfsapi.getNode(1,'/usr/home/guest/.shrc');
|
|
if (!n)
|
|
return;
|
|
n.mode = clite.lib.modestr('-rwx------');
|
|
n.uid = 1;
|
|
n.gid = 1;
|
|
n.data.content = `#!/bin/sh
|
|
cat -l /usr/share/introduction
|
|
`;
|
|
}
|
|
clite.user.getEnv = function(uid) {
|
|
var udata = getUser(uid);
|
|
if (!udata)
|
|
return structuredClone(env);
|
|
return structuredClone(udata.env);
|
|
}
|
|
clite.user.getPWData = function(uid) {
|
|
var udata = getUser(uid);
|
|
if (!udata)
|
|
return null;
|
|
|
|
return Object.create({
|
|
pw_name:udata.name, // User's login name.
|
|
pw_uid:udata.uid, // Numerical user ID.
|
|
pw_gid:udata.gid, // Numerical group ID.
|
|
pw_dir:udata.env.HOME, // Initial working directory.
|
|
pw_shell:udata.env.SHELL // Program to use as shell.
|
|
});
|
|
}
|
|
clite.user.getPWDataInd = function(i) {
|
|
if (i >= data.user.length)
|
|
return null;
|
|
|
|
var udata = data.users[i];
|
|
|
|
return Object.create({
|
|
pw_name:udata.name, // User's login name.
|
|
pw_uid:udata.uid, // Numerical user ID.
|
|
pw_gid:udata.gid, // Numerical group ID.
|
|
pw_dir:udata.env.HOME, // Initial working directory.
|
|
pw_shell:udata.env.SHELL // Program to use as shell.
|
|
});
|
|
}
|
|
clite.user.removePW = function(pid,uid) {
|
|
if (clite.proc.getRUID(pid) != 0)
|
|
return false;
|
|
loadUsers();
|
|
var ind = -1;
|
|
for (var i=0; i<data.users.length; i++) {
|
|
if (data.users[i].uid == uid)
|
|
ind = i;
|
|
}
|
|
if (ind<0)
|
|
return false;
|
|
|
|
data.users.splice(ind,1);
|
|
|
|
var udata = getUser(uid);
|
|
if (udata)
|
|
return false;
|
|
saveUsers();
|
|
return true;
|
|
}
|
|
clite.user.setPWData = function(pid,pw) {
|
|
if (typeof pw.pw_name !== 'string')
|
|
return false;
|
|
if (typeof pw.pw_uid !== 'number')
|
|
return false;
|
|
if (typeof pw.pw_gid !== 'number')
|
|
return false;
|
|
var gr = clite.user.getGRData(pw.pw_gid);
|
|
if (gr == null)
|
|
return false;
|
|
if (typeof pw.pw_dir !== 'string')
|
|
return false;
|
|
if (typeof pw.pw_shell !== 'string')
|
|
return false;
|
|
loadUsers();
|
|
if (pw.pw_uid < 0) {
|
|
if (clite.user.mapUserName(pw.pw_name) > -1)
|
|
return false;
|
|
var udata = {};
|
|
udata.name = pw.pw_name;
|
|
udata.pass = 'x';
|
|
udata.uid = getNextUid();
|
|
udata.gid = pw.pw_gid;
|
|
udata.groups = [];
|
|
udata.env = {};
|
|
udata.env.HOME = pw.pw_dir;
|
|
udata.env.SHELL = pw.pw_shell;
|
|
udata.env.USER = udata.name;
|
|
udata.env.PWD = udata.env.HOME;
|
|
|
|
data.users.push(udata);
|
|
|
|
saveUsers();
|
|
loadUsers();
|
|
return true;
|
|
}
|
|
var uid = clite.proc.getUID(pid);
|
|
if (pw.pw_uid != uid && uid != 0)
|
|
return false;
|
|
|
|
var udata = getUser(pw.pw_uid);
|
|
if (!udata)
|
|
return false;
|
|
udata.name = pw.pw_name;
|
|
udata.uid = pw.pw_uid;
|
|
udata.gid = pw.pw_gid;
|
|
udata.env.HOME = pw.pw_dir;
|
|
udata.env.SHELL = pw.pw_shell;
|
|
udata.env.USER = udata.name;
|
|
udata.env.PWD = udata.env.HOME;
|
|
|
|
saveUsers();
|
|
loadUsers();
|
|
return true;
|
|
}
|
|
clite.user.getGRData = function(gid) {
|
|
var gdata = getGroup(gid);
|
|
if (!gdata)
|
|
return null;
|
|
|
|
return Object.create({
|
|
gr_name:gdata.name, // The name of the group.
|
|
gr_gid:gdata.gid, // Numerical group ID.
|
|
gr_mem:gdata.users // array of member names.
|
|
});
|
|
}
|
|
clite.user.getGRDataInd = function(i) {
|
|
if (i >= data.groups.length)
|
|
return null;
|
|
|
|
var gdata = data.groups[i];
|
|
|
|
return Object.create({
|
|
gr_name:gdata.name, // The name of the group.
|
|
gr_gid:gdata.gid, // Numerical group ID.
|
|
gr_mem:gdata.users // array of member names.
|
|
});
|
|
}
|
|
clite.user.removeGR = function(pid,gid) {
|
|
if (clite.proc.getRUID(pid) != 0)
|
|
return false;
|
|
loadUsers();
|
|
var ind = -1;
|
|
for (var i=0; i<data.groups.length; i++) {
|
|
if (data.groups[i].gid == gid)
|
|
ind = i;
|
|
}
|
|
if (ind<0)
|
|
return false;
|
|
|
|
data.groups.splice(ind,1);
|
|
|
|
var gdata = getUser(gid);
|
|
if (gdata)
|
|
return false;
|
|
saveUsers();
|
|
return true;
|
|
}
|
|
clite.user.setGRData = function(pid,gr) {
|
|
if (typeof gr.gr_name !== 'string')
|
|
return false;
|
|
if (typeof gr.gr_gid !== 'number')
|
|
return false;
|
|
if (!Array.isArray(gr.gr_mem))
|
|
return false;
|
|
loadUsers();
|
|
if (gr.gr_gid < 0) {
|
|
if (clite.user.mapGroupName(gr.gr_name) > -1)
|
|
return false;
|
|
var gdata = {};
|
|
gdata.name = gr.gr_name;
|
|
gdata.gid = getNextGid();
|
|
gdata.users = Array.from(gr.gr_mem);
|
|
data.groups.push(gdata);
|
|
saveUsers();
|
|
loadUsers();
|
|
|
|
return true;
|
|
}
|
|
var uid = clite.proc.getUID(pid);
|
|
if (!clite.user.checkGID(uid,gr.gr_gid) && uid != 0)
|
|
return false;
|
|
var gdata = getGroup(gr.gr_gid);
|
|
if (!gdata)
|
|
return false;
|
|
gdata.name = gr.gr_name;
|
|
gdata.gid = gr.gr_gid;
|
|
gdata.users = Array.from(gr.gr_mem);
|
|
|
|
saveUsers();
|
|
loadUsers();
|
|
return true;
|
|
}
|
|
clite.user.mapUserName = function(name) {
|
|
for (var i=0; i<data.users.length; i++) {
|
|
if (data.users[i].name == name)
|
|
return data.users[i].uid;
|
|
}
|
|
return -1;
|
|
}
|
|
clite.user.mapGroupName = function(name) {
|
|
for (var i=0; i<data.groups.length; i++) {
|
|
if (data.groups[i].name == name)
|
|
return data.groups[i].gid;
|
|
}
|
|
return -1;
|
|
}
|
|
clite.user.setPasswd = function(pid,uid,pass) {
|
|
var udata = getUser(uid);
|
|
if (!udata)
|
|
return false;
|
|
|
|
var puid = clite.proc.getUID(pid);
|
|
if (puid != uid && puid != 0)
|
|
return false;
|
|
|
|
var ps = pass+':'+udata.name;
|
|
var cp = clite.lib.hash(ps).toString(16);
|
|
|
|
udata.pass = cp;
|
|
saveUsers();
|
|
loadUsers();
|
|
return true;
|
|
}
|
|
clite.user.checkPasswd = function(uid,pass) {
|
|
var udata = getUser(uid);
|
|
if (!udata)
|
|
return false;
|
|
|
|
var ps = pass+':'+udata.name;
|
|
var cp = clite.lib.hash(ps).toString(16);
|
|
|
|
if (cp == udata.pass)
|
|
return true;
|
|
return false;
|
|
}
|
|
clite.user.dump = function() {
|
|
var d = '';
|
|
data.users.forEach(function(u) {
|
|
d += u.name+':'+u.pass+':'+u.uid+':'+u.gid+':'+u.env.HOME+':'+u.env.SHELL+'\n';
|
|
});
|
|
return d;
|
|
}
|
|
clite.user.hasAltLogin = function() {
|
|
let r = false;
|
|
data.users.forEach(function(u) {
|
|
if (u.name != 'root' && u.pass != 'x')
|
|
r = true;
|
|
});
|
|
return r;
|
|
}
|
|
|
|
loadUsers();
|
|
clite.user.genGuest();
|
|
|
|
return false;
|
|
},
|
|
getGID:function(uid) { // get the primary group id of a user
|
|
return 0;
|
|
},
|
|
checkGID:function(uid,gid) { // check if the user is in a group
|
|
return true;
|
|
},
|
|
getEnv:function(uid) { // get the current environment (variables)
|
|
return {};
|
|
},
|
|
genGuest:null
|
|
};
|
|
|
|
clite.io = {
|
|
init:function() {
|
|
var vfsapi = clite.vfs.getApi();
|
|
|
|
function getFileDesPre(pid,path,link) {
|
|
var uid = clite.proc.getUID(pid);
|
|
var n = vfsapi.getNode(uid,path);
|
|
if (!n)
|
|
return null;
|
|
if (n.data.islink && !link) // if link is false, follow links
|
|
n = vfsapi.getNode(uid,n.data.content);
|
|
var p = (n.data.remote && n.data.content == null);
|
|
var fd = Object.create({
|
|
node:n,
|
|
pos:0,
|
|
canread:false,
|
|
canwrite:false,
|
|
canexec:false,
|
|
path:path,
|
|
remote:{
|
|
ispending:p,
|
|
callback:null
|
|
}
|
|
});
|
|
fd.canread = clite.lib.checkNodeReadable(fd.node,pid);
|
|
fd.canwrite = clite.lib.checkNodeWritable(fd.node,pid);
|
|
fd.canexec = clite.lib.checkNodeExecutable(fd.node,pid);
|
|
return fd;
|
|
}
|
|
|
|
function getFileDesPost(fd,cb) {
|
|
if (typeof cb === 'function') {
|
|
fd.remote.callback = cb;
|
|
if (fd.remote.ispending) {
|
|
clite.state.bios.core.load.file(fd.node.data.remote,function(d) {
|
|
fd.node.data.content = d;
|
|
fd.remote.ispending = false;
|
|
try{
|
|
fd.remote.callback(fd);
|
|
} catch(err) {}
|
|
});
|
|
}else{
|
|
clite.core.execSafeAsync(function() {fd.remote.callback(fd);});
|
|
}
|
|
}
|
|
return fd;
|
|
}
|
|
|
|
function getFileDes(pid,path,link,cb) {
|
|
var fd = getFileDesPre(pid,path,link);
|
|
if (!fd)
|
|
return null;
|
|
return getFileDesPost(fd,cb);
|
|
}
|
|
clite.io.creat = function(pid,path,type) {
|
|
var uid = clite.proc.getUID(pid);
|
|
switch (type) {
|
|
case 'd':
|
|
return vfsapi.mkDir(uid,path);
|
|
break;
|
|
case 'l':
|
|
return vfsapi.mkLink(uid,path);
|
|
break;
|
|
default:
|
|
return vfsapi.mkFile(uid,path);
|
|
}
|
|
}
|
|
clite.io.open = function(pid,path,flags,cb) {
|
|
var read = false;
|
|
var write = false;
|
|
var exec = false;
|
|
var search = false;
|
|
|
|
var append = false;
|
|
var create = false;
|
|
var directory = false;
|
|
var excl = false;
|
|
var nofollow = false;
|
|
var nonblock = false;
|
|
var trunc = false;
|
|
|
|
if ((flags&clite.io.flags.O_RDONLY) == clite.io.flags.O_RDONLY)
|
|
read = true;
|
|
if ((flags&clite.io.flags.O_WRONLY) == clite.io.flags.O_WRONLY)
|
|
write = true;
|
|
if ((flags&clite.io.flags.O_RDWR) == clite.io.flags.O_RDWR) {
|
|
read = true;
|
|
write = true;
|
|
}
|
|
if ((flags&clite.io.flags.O_EXEC) == clite.io.flags.O_EXEC)
|
|
exec = true;
|
|
if ((flags&clite.io.flags.O_SEARCH) == clite.io.flags.O_SEARCH)
|
|
search = true;
|
|
|
|
// must have one of these set at least
|
|
if (!read && !write && !exec && !search)
|
|
return null;
|
|
|
|
if ((flags&clite.io.flags.O_APPEND) == clite.io.flags.O_APPEND) {
|
|
if (!read && !write)
|
|
return null;
|
|
append = true;
|
|
}
|
|
if ((flags&clite.io.flags.O_CREAT) == clite.io.flags.O_CREAT)
|
|
create = true;
|
|
if ((flags&clite.io.flags.O_DIRECTORY) == clite.io.flags.O_DIRECTORY)
|
|
directory = true;
|
|
if ((flags&clite.io.flags.O_EXCL) == clite.io.flags.O_EXCL)
|
|
excl = true;
|
|
if ((flags&clite.io.flags.O_NOFOLLOW) == clite.io.flags.O_NOFOLLOW)
|
|
nofollow = true;
|
|
if ((flags&clite.io.flags.O_SYNC) == clite.io.flags.O_SYNC)
|
|
cb = false;
|
|
if ((flags&clite.io.flags.O_TRUNC) == clite.io.flags.O_TRUNC)
|
|
trunc = true;
|
|
|
|
var fd = getFileDesPre(pid,path,nofollow);
|
|
if (!fd) {
|
|
if (create) {
|
|
if (directory) {
|
|
if (!clite.io.mkdir(pid,path))
|
|
return null;
|
|
}else{
|
|
if (!clite.io.creat(pid,path,'-'))
|
|
return null;
|
|
}
|
|
fd = getFileDesPre(pid,path,nofollow);
|
|
}
|
|
if (!fd)
|
|
return null;
|
|
}else if (create && excl) {
|
|
return null;
|
|
}
|
|
|
|
|
|
var type = clite.lib.getFileType(fd);
|
|
|
|
if (directory) {
|
|
if (type != clite.io.types.FT_DIR)
|
|
return null;
|
|
|
|
if (read || search) {
|
|
if (!fd.canread || !fd.canexec)
|
|
return null
|
|
}else{
|
|
fd.canread = false;
|
|
}
|
|
if (write)
|
|
return null;
|
|
|
|
return getFileDesPost(fd,cb);
|
|
}
|
|
// check again (search is only for directories, and is basically read)
|
|
if (!read && !write && !exec)
|
|
return null;
|
|
|
|
if (trunc && write) {
|
|
if (type == clite.io.types.FT_SCRIPT || type == clite.io.types.FT_TEXT) {
|
|
fd.node.data.content = '';
|
|
}else if (type == clite.io.types.FT_REMOTE) {
|
|
return null; // cannot truncate unloaded remote data
|
|
}
|
|
}
|
|
if (append && write)
|
|
fd.pos = fd.node.data.content.length;
|
|
|
|
return getFileDesPost(fd,cb);
|
|
}
|
|
clite.io.close = function(pid,fd) {
|
|
try{
|
|
fd.node = null;
|
|
delete fd;
|
|
} catch(err) {}
|
|
}
|
|
clite.io.read = function(pid,fd,cb) {
|
|
if (fd.node.data.content == null)
|
|
return null;
|
|
if (!fd.canread)
|
|
return null;
|
|
if (fd.node.data.istty) {
|
|
try{
|
|
if (typeof cb === 'function') {
|
|
if (fd.node.data.content.read.length == 2) {
|
|
var ctty = clite.proc.getTTY(pid);
|
|
fd.node.data.content.read(ctty,cb);
|
|
}else{
|
|
fd.node.data.content.read(cb);
|
|
}
|
|
return true;
|
|
}
|
|
} catch(err) {}
|
|
return false;
|
|
}else if (fd.node.data.isdev) {
|
|
if (typeof fd.node.data.content !== 'string') {
|
|
if (typeof cb === 'function' && fd.node.data.content.read.length == 1) {
|
|
try{
|
|
return fd.node.data.content.read(cb);
|
|
} catch(err) {
|
|
return null;
|
|
}
|
|
}else{
|
|
try{
|
|
return fd.node.data.content.read();
|
|
} catch(err) {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (fd.pos >= fd.node.data.content.length)
|
|
return null;
|
|
if (fd.node.data.isdir) {
|
|
var e = fd.node.data.content[fd.pos++];
|
|
return e.name;
|
|
}
|
|
return fd.node.data.content[fd.pos++];
|
|
}
|
|
clite.io.readLine = function(pid,fd,cb) {
|
|
if (fd.node.data.content == null)
|
|
return null;
|
|
if (!fd.canread)
|
|
return null;
|
|
if (fd.node.data.istty) {
|
|
try{
|
|
if (typeof cb === 'function') {
|
|
if (fd.node.data.content.read.length == 2) {
|
|
var ctty = clite.proc.getTTY(pid);
|
|
fd.node.data.content.read(ctty,cb);
|
|
}else{
|
|
fd.node.data.content.read(cb);
|
|
}
|
|
return true;
|
|
}
|
|
} catch(err) {}
|
|
return false;
|
|
}else if (fd.node.data.isdev) {
|
|
if (typeof fd.node.data.content !== 'string') {
|
|
try{
|
|
return fd.node.data.content.read();
|
|
} catch(err) {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
if (fd.pos >= fd.node.data.content.length)
|
|
return null;
|
|
if (typeof fd.node.data.content != 'string')
|
|
return null;
|
|
var e = fd.node.data.content.indexOf('\n',fd.pos);
|
|
var l = '';
|
|
if (e < 0) {
|
|
l = fd.node.data.content.substring(fd.pos);
|
|
}else{
|
|
l = fd.node.data.content.substring(fd.pos,e);
|
|
fd.pos++;
|
|
}
|
|
fd.pos += l.length;
|
|
return l;
|
|
}
|
|
clite.io.readAll = function(pid,fd) {
|
|
if (fd.node.data.istty)
|
|
return false;
|
|
if (fd.node.data.content == null)
|
|
return null;
|
|
if (!fd.canread)
|
|
return null;
|
|
if (fd.node.data.isdev) {
|
|
if (typeof fd.node.data.content !== 'string') {
|
|
try{
|
|
return fd.node.data.content.read();
|
|
} catch(err) {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
return fd.node.data.content;
|
|
}
|
|
clite.io.write = function(pid,fd,data) {
|
|
if (!fd.canwrite)
|
|
return false;
|
|
if (fd.node.data.isdev) {
|
|
if (typeof fd.node.data.content != 'string') {
|
|
fd.node.time.modify = clite.time.sec();
|
|
try{
|
|
if (fd.node.data.content.write.length == 2) {
|
|
var ctty = clite.proc.getTTY(pid);
|
|
return fd.node.data.content.write(ctty,data);
|
|
}else{
|
|
return fd.node.data.content.write(data);
|
|
}
|
|
} catch(err) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
if (typeof data === 'function') {
|
|
if (typeof fd.node.data.content !== 'string' && typeof fd.node.data.content !== 'function')
|
|
return false;
|
|
fd.node.data.content = data;
|
|
fd.pos = 0;
|
|
}else{
|
|
if (typeof fd.node.data.content != 'string')
|
|
return false;
|
|
if (typeof data != 'string')
|
|
return false;
|
|
if (fd.pos + data.length >= fd.node.data.content.length) {
|
|
fd.node.data.content = fd.node.data.content.substring(0,fd.pos)+data;
|
|
fd.pos = fd.node.data.content.length;
|
|
}else{
|
|
var b = fd.node.data.content.substring(0,fd.pos);
|
|
var e = fd.node.data.content.substring(fd.pos+data.length);
|
|
fd.node.data.content = b+data+e;
|
|
fd.pos += data.length;
|
|
}
|
|
}
|
|
fd.node.time.modify = clite.time.sec();
|
|
clite.vfs.saveFile(fd.path);
|
|
return true;
|
|
}
|
|
clite.io.ftruncate = function(pid,fd,len) {
|
|
if (!fd.canwrite)
|
|
return false;
|
|
if (typeof fd.data.content != 'string')
|
|
return false;
|
|
fd.data.content = fd.data.content.substring(0,len);
|
|
if (fd.pos > len)
|
|
fd.pos = len;
|
|
fd.node.time.modify = clite.time.sec();
|
|
clite.vfs.saveFile(fd.path);
|
|
return true;
|
|
}
|
|
clite.io.truncate = function(pid,path,len) {
|
|
var fd = getFileDes(pid,path,false,null);
|
|
if (!fd)
|
|
return false;
|
|
var r = clite.io.ftruncate(pid,fd,len);
|
|
close(fd);
|
|
return r;
|
|
}
|
|
clite.io.seek = function(pid,fd,pos) {
|
|
if (fd.node.data.content == null)
|
|
return 0;
|
|
if (fd.node.data.content.length < 1)
|
|
return 0;
|
|
if (pos<0)
|
|
return fd.pos;
|
|
fd.pos = pos;
|
|
if (fd.pos >= fd.node.data.content.length)
|
|
fd.pos = fd.node.data.content.length-1;
|
|
return fd.pos;
|
|
}
|
|
clite.io.remove = function(pid,path) {
|
|
var fd = getFileDes(pid,path,true,null);
|
|
if (!fd || !fd.canwrite || fd.node.data.isdev)
|
|
return false;
|
|
var uid = clite.proc.getUID(pid);
|
|
clite.vfs.saveFile(fd.path);
|
|
var r = vfsapi.remove(uid,path);
|
|
if (r)
|
|
clite.vfs.saveFile(path);
|
|
return r;
|
|
}
|
|
clite.io.link = function(pid,path,target) {
|
|
if (!getFileDes(pid,target,false,null))
|
|
return false;
|
|
var fd = getFileDes(pid,path,true,null);
|
|
if (fd) {
|
|
if (!fd.canwrite)
|
|
return false;
|
|
fd.node.data.content = target;
|
|
clite.vfs.saveFile(path);
|
|
return true;
|
|
}
|
|
var uid = clite.proc.getUID(pid);
|
|
var r = vfsapi.mkLink(uid,path,target);
|
|
if (r)
|
|
clite.vfs.saveFile(path);
|
|
return r;
|
|
},
|
|
clite.io.fstatat = function(pid,fd,flags) {
|
|
if (!fd || !fd.node || !fd.canread)
|
|
return null;
|
|
|
|
if (fd.node.islink && (flags&clite.io.flags.AT_SYMLINK_NOFOLLOW) == 0)
|
|
fd = getFileDesPre(pid,n.node.data.content,true);
|
|
if (!fd || !fd.node || !fd.canread)
|
|
return null;
|
|
|
|
var stat = Object.create({
|
|
name:fd.node.name,
|
|
type:clite.lib.getFileType(fd),
|
|
st_dev:0,
|
|
st_ino:0,
|
|
st_mode:fd.node.mode,
|
|
st_nlink:0,
|
|
st_uid:fd.node.uid,
|
|
st_gid:fd.node.gid,
|
|
st_size:0,
|
|
st_atim:{
|
|
tv_sec:fd.node.time.access,
|
|
tv_nsec:0
|
|
},
|
|
st_mtim:{
|
|
tv_sec:fd.node.time.modify,
|
|
tv_nsec:0
|
|
},
|
|
st_ctim:{
|
|
tv_sec:fd.node.time.change,
|
|
tv_nsec:0
|
|
}
|
|
});
|
|
|
|
if (stat.type == clite.io.types.FT_TEXT || stat.type == clite.io.types.FT_SCRIPT)
|
|
stat.st_size = fd.node.data.content.length;
|
|
|
|
return stat;
|
|
},
|
|
clite.io.fchmod = function(pid,fd,mode) {
|
|
if (typeof mode != 'number')
|
|
return false;
|
|
var uid = clite.proc.getUID(pid);
|
|
if (fd.node.uid != uid && uid != 0)
|
|
return false;
|
|
|
|
mode &= ~clite.io.modes.S_IFMT;
|
|
mode |= (fd.node.mode&clite.io.modes.S_IFMT);
|
|
|
|
fd.node.mode = mode;
|
|
fd.node.time.change = clite.time.sec();
|
|
clite.vfs.saveFile(fd.path);
|
|
return true;
|
|
}
|
|
clite.io.chmod = function(pid,path,mode) {
|
|
var fd = getFileDesPre(pid,path,true);
|
|
return clite.io.fchmod(pid,fd,mode);
|
|
}
|
|
|
|
clite.io.chown = function(pid,path,uid,gid) {
|
|
var fd = getFileDesPre(pid,path,true);
|
|
if (!fd)
|
|
return false;
|
|
var euid = clite.proc.getUID(pid);
|
|
if (euid != 0) {
|
|
if (fd.node.uid != euid)
|
|
return false;
|
|
if (uid > -1 && uid != euid)
|
|
return false;
|
|
var egid = clite.proc.getGID(pid);
|
|
if (gid > -1 && gid != egid)
|
|
return false;
|
|
}
|
|
|
|
if (uid > -1)
|
|
fd.node.uid = uid;
|
|
if (gid > -1)
|
|
fd.node.gid = gid;
|
|
clite.vfs.saveFile(path);
|
|
return true;
|
|
}
|
|
clite.io.lchown = function(pid,path,uid,gid) {
|
|
var fd = getFileDesPre(pid,path,false);
|
|
if (!fd)
|
|
return false;
|
|
var euid = clite.proc.getUID(pid);
|
|
if (euid != 0) {
|
|
if (fd.node.uid != euid)
|
|
return false;
|
|
if (uid > -1 && uid != euid)
|
|
return false;
|
|
var egid = clite.proc.getGID(pid);
|
|
if (gid > -1 && gid != egid)
|
|
return false;
|
|
}
|
|
|
|
if (uid > -1)
|
|
fd.node.uid = uid;
|
|
if (gid > -1)
|
|
fd.node.gid = gid;
|
|
clite.vfs.saveFile(path);
|
|
return true;
|
|
}
|
|
|
|
clite.io.isatty = function(pid,fd) {
|
|
return fd.node.data.istty;
|
|
}
|
|
clite.io.mkdir = function(pid,path,mode) {
|
|
if (typeof mode != 'number' || mode == 0) {
|
|
mode = clite.io.modes.S_IRWXU|clite.io.modes.S_IRGRP|clite.io.modes.S_IXGRP;
|
|
}
|
|
mode &= ~clite.io.modes.S_IFMT;
|
|
mode |= clite.io.modes.S_IFDIR;
|
|
var uid = clite.proc.getUID(pid);
|
|
var r = vfsapi.mkDir(uid,path);
|
|
if (!r)
|
|
return false;
|
|
var n = vfsapi.getNode(uid,path);
|
|
if (!n)
|
|
return false;
|
|
n.mode = mode;
|
|
clite.vfs.saveFile(path);
|
|
return true;
|
|
}
|
|
|
|
clite.io.vfprintf = function(pid,fd,fmt,args) {
|
|
var str = '';
|
|
var parts = fmt.replace(/%%/g,'%').split('%');
|
|
var i = 0;
|
|
var d = false;
|
|
parts.forEach(function(p,k) {
|
|
var fs = p;
|
|
if (!k || d) {
|
|
str += p;
|
|
d = false;
|
|
return;
|
|
}
|
|
if (p == '' && j<parts.length-1) {
|
|
str += '%';
|
|
d = true;
|
|
return;
|
|
}
|
|
// %[parameter][flags][width][.precision][length]type
|
|
//clite.log.write(fs);
|
|
var param = i;
|
|
var nparam = true;
|
|
var ind = fs.indexOf('$');
|
|
if (ind > -1) {
|
|
var pp = fs.substring(0,ind);
|
|
var pi = parseInt(pp);
|
|
if (pp == pi.toString()) {
|
|
param = pi;
|
|
nparam = false;
|
|
fs = fs.substring(ind+1);
|
|
}
|
|
}
|
|
//clite.log.write('(param='+param+')'+fs);
|
|
// TODO: support multiple flags
|
|
var flag = false;
|
|
if (fs[0] == '-' || fs[0] == '+' || fs[0] == ' ' || fs[0] == '0' || fs[0] == "'" || fs[0] == '#') {
|
|
flag = fs[0];
|
|
fs = fs.substring(1);
|
|
}
|
|
//clite.log.write('(param='+param+')(flag='+flag+')'+fs);
|
|
var width = 0;
|
|
if (fs[0] == '*') {
|
|
if (param >= args.length)
|
|
param = args.length-1;
|
|
width = parseInt(args[param]);
|
|
param++;
|
|
}else{
|
|
ind = -1;
|
|
for (var j=0; j<fs.length; j++) {
|
|
var v = parseInt(fs[j],10);
|
|
if (v > -1 && v < 10) {
|
|
ind = j;
|
|
}else{
|
|
break;
|
|
}
|
|
}
|
|
if (ind > -1) {
|
|
var pp = fs.substring(0,ind+1);
|
|
var pi = parseInt(pp);
|
|
if (pi.toString() == pp) {
|
|
width = pi;
|
|
fs = fs.substring(ind+1);
|
|
}
|
|
}
|
|
}
|
|
//clite.log.write('(param='+param+')(flag='+flag+')(width='+width+')'+fs);
|
|
var precision = 0;
|
|
if (fs[0] == '.') {
|
|
fs = fs.substring(1);
|
|
ind = -1;
|
|
for (var j=0; j<fs.length; j++) {
|
|
var v = parseInt(fs[j],10);
|
|
if (v > -1 && v < 10) {
|
|
ind = j;
|
|
}else{
|
|
break;
|
|
}
|
|
}
|
|
if (ind > -1) {
|
|
var pp = fs.substring(0,ind+1);
|
|
var pi = parseInt(pp);
|
|
if (pp.toString() == pp) {
|
|
precision = pi;
|
|
fs = fs.substring(ind+1);
|
|
}
|
|
}
|
|
}
|
|
// ignore length
|
|
if (fs.substring(0,2) == 'hh' || fs.substring(0,2) == 'll') {
|
|
fs = fs.substring(2);
|
|
}else if (['h','l','L','z','j','t'].indexOf(fs[0]) > -1) {
|
|
fs = fs.substring(1);
|
|
}
|
|
|
|
if (param >= args.length)
|
|
return false;
|
|
|
|
//clite.log.write('(param='+param+')(flag='+flag+')(width='+width+')'+fs);
|
|
var type = fs[0];
|
|
fs = fs.substring(1);
|
|
//clite.log.write('(param='+param+')(flag='+flag+')(width='+width+')(type='+type+')'+fs);
|
|
switch (type) {
|
|
case 'd':
|
|
case 'i': // signed int
|
|
var val = parseInt(args[param],10);
|
|
if (typeof flag === 'string' && val > 0) {
|
|
if (flag == '+')
|
|
str += '+';
|
|
if (flag == ' ')
|
|
str += ' ';
|
|
}
|
|
if (width > 0) {
|
|
var c = ' ';
|
|
if (flag == '0')
|
|
c = '0';
|
|
if (flag == '-') {
|
|
str += val.toFixed(0).padEnd(width,c);
|
|
}else{
|
|
str += val.toFixed(0).padStart(width,c);
|
|
}
|
|
}else{
|
|
str += val.toFixed(0);
|
|
}
|
|
break;
|
|
case 'u': // unsigned int
|
|
var sval = args[param].toString(10);
|
|
if (sval[0] == '-')
|
|
sval = sval.substring(1);
|
|
var val = parseInt(sval,10);
|
|
if (width > 0) {
|
|
var c = ' ';
|
|
if (flag == '0')
|
|
c = '0';
|
|
if (flag == '-') {
|
|
str += val.toFixed(0).padEnd(width,c);
|
|
}else{
|
|
str += val.toFixed(0).padStart(width,c);
|
|
}
|
|
}else{
|
|
str += val.toFixed(0);
|
|
}
|
|
break;
|
|
case 'f': // float
|
|
var val = parseFloat(args[param].toString(10));
|
|
if (isNaN(val)) {
|
|
str += 'nan';
|
|
}else if (!Number.isFinite(val)) {
|
|
str += 'inf';
|
|
}else{
|
|
if (precision == 0) {
|
|
var s = String(val).split('.');
|
|
if (s.length > 1)
|
|
precision = s[1].length;
|
|
}
|
|
var s = val.toFixed(precision).toLowerCase();
|
|
if (precision == 0)
|
|
s += '.';
|
|
if (typeof flag === 'string' && val > 0) {
|
|
if (flag == '+')
|
|
s = '+'+s;
|
|
if (flag == ' ') {
|
|
clite.log.write('pad space');
|
|
s = ' '+s;
|
|
}
|
|
}
|
|
if (width > 0) {
|
|
var c = ' ';
|
|
if (flag == '0')
|
|
c = '0';
|
|
if (flag == '-') {
|
|
s = s.padEnd(width,c);
|
|
}else{
|
|
s = s.padStart(width,c);
|
|
}
|
|
}
|
|
str += s;
|
|
}
|
|
break;
|
|
case 'F': // FLOAT
|
|
var val = parseFloat(args[param].toString(10));
|
|
if (isNaN(val)) {
|
|
str += 'NAN';
|
|
}else if (!Number.isFinite(val)) {
|
|
str += 'INF';
|
|
}else{
|
|
if (precision == 0) {
|
|
var s = String(val).split('.');
|
|
if (s.length > 1)
|
|
precision = s[1].length;
|
|
}
|
|
var s = val.toFixed(precision).toUpperCase();
|
|
if (precision == 0)
|
|
s += '.';
|
|
if (typeof flag === 'string' && val > 0) {
|
|
if (flag == '+')
|
|
s = '+'+s;
|
|
if (flag == ' ')
|
|
s = ' '+s;
|
|
}
|
|
if (width > 0) {
|
|
var c = ' ';
|
|
if (flag == '0')
|
|
c = '0';
|
|
if (flag == '-') {
|
|
s = s.padEnd(width,c);
|
|
}else{
|
|
s = s.padStart(width,c);
|
|
}
|
|
}
|
|
str += s;
|
|
}
|
|
break;
|
|
case 'e': // float with exponent
|
|
var val = parseFloat(args[param].toString(10));
|
|
if (isNaN(val)) {
|
|
str += 'NAN';
|
|
}else if (!Number.isFinite(val)) {
|
|
str += 'INF';
|
|
}else{
|
|
str += val.toExponential().toLowerCase();
|
|
}
|
|
break;
|
|
case 'E': // FLOAT with exponent
|
|
var val = parseFloat(args[param].toString(10));
|
|
if (isNaN(val)) {
|
|
str += 'NAN';
|
|
}else if (!Number.isFinite(val)) {
|
|
str += 'INF';
|
|
}else{
|
|
str += val.toExponential().toUpperCase();
|
|
}
|
|
break;
|
|
case 'g': // either float or float with exponent
|
|
var val = parseFloat(args[param].toString(10));
|
|
if (isNaN(val)) {
|
|
str += 'nan';
|
|
}else if (!Number.isFinite(val)) {
|
|
str += 'inf';
|
|
}else{
|
|
str += val.toString(10).toLowerCase();
|
|
}
|
|
break;
|
|
case 'G': // either FLOAT or FLOAT with exponent
|
|
var val = parseFloat(args[param].toString(10));
|
|
if (isNaN(val)) {
|
|
str += 'NAN';
|
|
}else if (!Number.isFinite(val)) {
|
|
str += 'INF';
|
|
}else{
|
|
str += val.toString(10).toUpperCase();
|
|
}
|
|
break;
|
|
case 'x': // hexadecimal
|
|
var val = parseInt(args[param]);
|
|
var s = '';
|
|
if (flag == '#')
|
|
s += '0x';
|
|
s += val.toString(16).toLowerCase();
|
|
if (width > 0) {
|
|
if (flag == '-') {
|
|
s = s.padEnd(width,' ');
|
|
}else{
|
|
s = s.padStart(width,' ');
|
|
}
|
|
}
|
|
str += s;
|
|
break;
|
|
case 'X': // HEXADECIMAL
|
|
var val = parseInt(args[param]);
|
|
var s = '';
|
|
if (flag == '#')
|
|
s += '0X';
|
|
s += val.toString(16).toUpperCase();
|
|
if (width > 0) {
|
|
if (flag == '-') {
|
|
s = s.padEnd(width,' ');
|
|
}else{
|
|
s = s.padStart(width,' ');
|
|
}
|
|
}
|
|
str += s;
|
|
break;
|
|
case 'o': // octal
|
|
var val = parseInt(args[param],10);
|
|
var s = '';
|
|
if (flag == '#')
|
|
s += '0';
|
|
s += val.toString(8);
|
|
if (width > 0) {
|
|
if (flag == '-') {
|
|
s = s.padEnd(width,' ');
|
|
}else{
|
|
s = s.padStart(width,' ');
|
|
}
|
|
}
|
|
str += s;
|
|
break;
|
|
case 's': // string
|
|
var s = args[param].toString();
|
|
if (width > 0) {
|
|
if (flag == '-') {
|
|
s = s.padEnd(width,' ');
|
|
}else{
|
|
s = s.padStart(width,' ');
|
|
}
|
|
}
|
|
if (precision > 0)
|
|
s = s.substring(0,precision);
|
|
str += s;
|
|
break;
|
|
case 'c': // character
|
|
if (width > 0) {
|
|
if (flag == '-') {
|
|
str += args[param].toString()[0].padEnd(width,' ');
|
|
}else{
|
|
str += args[param].toString()[0].padStart(width,' ');
|
|
}
|
|
}else{
|
|
str += args[param].toString()[0];
|
|
}
|
|
break;
|
|
case 'p': // pointer (NULL)
|
|
if (width > 0) {
|
|
if (flag == '-') {
|
|
str += 'NULL'.padEnd(width,' ');
|
|
}else{
|
|
str += 'NULL'.padStart(width,' ');
|
|
}
|
|
}else{
|
|
str += 'NULL';
|
|
}
|
|
break;
|
|
case 'a': // hexfloat
|
|
var val = parseFloat(args[param]);
|
|
str += val.toString(16).toLowerCase();
|
|
break;
|
|
case 'A': // HEXFLOAT
|
|
var val = parseFloat(args[param]);
|
|
str += val.toString(16).toUpperCase();
|
|
break;
|
|
case 'n': // number of chars written so far returned
|
|
args[param].value = str.length;
|
|
break;
|
|
default:;
|
|
}
|
|
|
|
if (nparam)
|
|
i = param+1;
|
|
str += fs;
|
|
})
|
|
|
|
str = str.replace(/%/g,'%');
|
|
return clite.io.write(pid,fd,str);
|
|
}
|
|
},
|
|
// 0: unknown
|
|
// 1: text file
|
|
// 2: executable/binary (function)
|
|
// 3: directory
|
|
// 4: link
|
|
// 5: device
|
|
// 6: unloaded remote file
|
|
// 7: shell script
|
|
// 8: image
|
|
// 9: shared object (library)
|
|
types:{
|
|
FT_UNKNOWN:0,
|
|
FT_TEXT:1,
|
|
FT_BINARY:2,
|
|
FT_DIR:3,
|
|
FT_LINK:4,
|
|
FT_DEV:5,
|
|
FT_REMOTE:6,
|
|
FT_SCRIPT:7,
|
|
FT_IMAGE:8,
|
|
FT_LIBRARY:9
|
|
},
|
|
flags:{
|
|
// at least one of these
|
|
O_EXEC: parseInt('0x010000',16), // open for executing only, no op on directories
|
|
O_RDONLY: parseInt('0x020000',16), // open for reading only
|
|
O_RDWR: parseInt('0x060000',16), // open for reading and writing
|
|
O_SEARCH: parseInt('0x080000',16), // open for directory search only, no op on non-directories
|
|
O_WRONLY: parseInt('0x040000',16), // open for writing only
|
|
// any of these
|
|
O_APPEND: parseInt('0x000001',16), // append to the file, sets pos to content.length, requires O_RDONLY or O_RDWR
|
|
O_CLOEXEC: parseInt('0x000002',16), // close on exec
|
|
O_CREAT: parseInt('0x000004',16), // create if file does not exist
|
|
O_DIRECTORY: parseInt('0x000008',16), // only open if directory
|
|
O_EXCL: parseInt('0x000010',16), // if O_CREAT is set, fail if file exists
|
|
O_NOCTTY: parseInt('0x0',16), // no op
|
|
O_NOFOLLOW: parseInt('0x000020',16), // if file is a symlink, don't follow it
|
|
AT_SYMLINK_NOFOLLOW: parseInt('0x000020',16), // if file is a symlink, don't follow it
|
|
O_NONBLOCK: parseInt('0x000040',16), // no callbacks, return immediately
|
|
O_SYNC: parseInt('0x000080',16), // write immediately (may fail on remote data)
|
|
O_TRUNC: parseInt('0x000100',16), // set file size to 0 before writing, requires O_RDWR or O_WRONLY
|
|
O_TTY_INIT: parseInt('0x0',16) // no op
|
|
},
|
|
modes:{
|
|
// permissions
|
|
S_IRWXU: parseInt('00700',8), // Read, write, execute/search by owner.
|
|
S_IRUSR: parseInt('00400',8), // Read permission, owner.
|
|
S_IWUSR: parseInt('00200',8), // Write permission, owner.
|
|
S_IXUSR: parseInt('00100',8), // Execute/search permission, owner.
|
|
S_IRWXG: parseInt('00070',8), // Read, write, execute/search by group.
|
|
S_IRGRP: parseInt('00040',8), // Read permission, group.
|
|
S_IWGRP: parseInt('00020',8), // Write permission, group.
|
|
S_IXGRP: parseInt('00010',8), // Execute/search permission, group.
|
|
S_IRWXO: parseInt('00007',8), // Read, write, execute/search by others.
|
|
S_IROTH: parseInt('00004',8), // Read permission, others.
|
|
S_IWOTH: parseInt('00002',8), // Write permission, others.
|
|
S_IXOTH: parseInt('00001',8), // Execute/search permission, others.
|
|
S_ISUID: parseInt('04000',8), // Set-user-ID on execution.
|
|
S_ISGID: parseInt('02000',8), // Set-group-ID on execution.
|
|
S_ISVTX: parseInt('01000',8), // On directories, restricted deletion flag.
|
|
// file types
|
|
S_IFMT: parseInt('110000',8), // format (file type) mask
|
|
S_IFBLK: parseInt('010000',8), // Block special
|
|
S_IFCHR: parseInt('020000',8), // Character special
|
|
S_IFIFO: parseInt('040000',8), // FIFO special
|
|
S_IFREG: parseInt('000000',8), // Regular file
|
|
S_IFDIR: parseInt('100000',8), // Directory
|
|
S_IFLNK: parseInt('200000',8), // Symbolic link
|
|
S_IFSOCK: parseInt('400000',8), // Socket
|
|
},
|
|
creat:null,
|
|
open:null,
|
|
close:null,
|
|
read:null,
|
|
readLine:null,
|
|
readAll:null,
|
|
write:null,
|
|
ftruncate:null,
|
|
truncate:null,
|
|
seek:null,
|
|
remove:null,
|
|
link:null,
|
|
stat:null,
|
|
fstat:null,
|
|
chmod:null,
|
|
fchmod:null,
|
|
isatty:null,
|
|
mkdir:null,
|
|
vfprintf:null
|
|
};
|
|
|
|
clite.vfs = {
|
|
isinit:false,
|
|
init:function() {
|
|
var vfsdata = {
|
|
fs:{},
|
|
api:{}
|
|
};
|
|
|
|
function findNodeChild(node,name) {
|
|
for (var i=0; i<node.data.content.length; i++) {
|
|
var nn = node.data.content[i];
|
|
if (nn.name == name)
|
|
return nn;
|
|
}
|
|
}
|
|
function checkCanOpen(uid,node) {
|
|
if (!uid)
|
|
return true;
|
|
if ((node.mode&clite.io.modes.S_IROTH) == clite.io.modes.S_IROTH)
|
|
return true;
|
|
if (clite.user.checkGID(uid,node.gid) && (node.mode&clite.io.modes.S_IRGRP) == clite.io.modes.S_IRGRP)
|
|
return true;
|
|
if (node.uid == uid && (node.mode&clite.io.modes.S_IRUSR) == clite.io.modes.S_IRUSR)
|
|
return true;
|
|
return false;
|
|
}
|
|
function checkCanWrite(uid,node) {
|
|
if (!uid)
|
|
return true;
|
|
if ((node.mode&clite.io.modes.S_IWOTH) == clite.io.modes.S_IWOTH)
|
|
return true;
|
|
if (clite.user.checkGID(uid,node.gid) && (node.mode&clite.io.modes.S_IWGRP) == clite.io.modes.S_IWGRP)
|
|
return true;
|
|
if (node.uid == uid && (node.mode&clite.io.modes.S_IWUSR) == clite.io.modes.S_IWUSR)
|
|
return true;
|
|
return false;
|
|
}
|
|
function mkNode() {
|
|
var fsnode = {
|
|
name: '',
|
|
uid: 0,
|
|
gid: 0,
|
|
mode:clite.io.modes.S_IRUSR|clite.io.modes.S_IWUSR|clite.io.modes.S_IRGRP|clite.io.modes.S_IROTH,
|
|
time:{
|
|
access:clite.time.sec(),
|
|
modify:clite.time.sec(),
|
|
change:clite.time.sec()
|
|
},
|
|
data:{
|
|
remote:null,
|
|
isdir:false,
|
|
islink:false,
|
|
isdev:false,
|
|
istty:false,
|
|
parent:null,
|
|
content:null
|
|
}
|
|
};
|
|
return Object.create(fsnode);
|
|
}
|
|
|
|
// set up the root node
|
|
vfsdata.fs = mkNode();
|
|
vfsdata.fs.data.content = [];
|
|
vfsdata.fs.data.isdir = true;
|
|
vfsdata.fs.mode = clite.lib.modestr('drwxr-xr-x');
|
|
|
|
clite.vfs.getApi = function() {
|
|
if (clite.state.runlevel == 1)
|
|
return vfsdata.api;
|
|
return null;
|
|
}
|
|
|
|
vfsdata.api.getNode = function(uid,path) {
|
|
var n = vfsdata.fs;
|
|
var parts = path.split('/');
|
|
while (parts.length > 0) {
|
|
if (!checkCanOpen(uid,n))
|
|
return null;
|
|
if (n.data.islink) {
|
|
n = vfsdata.api.getNode(uid,n.data.content);
|
|
}
|
|
if (!n.data.isdir)
|
|
return null;
|
|
var p = parts.shift();
|
|
if (!p || p == '')
|
|
continue;
|
|
var nn = findNodeChild(n,p);
|
|
if (!nn)
|
|
return null;
|
|
n = nn;
|
|
}
|
|
if (!checkCanOpen(uid,n))
|
|
return null;
|
|
n.time.access = clite.time.sec();
|
|
return n;
|
|
}
|
|
vfsdata.api.getFile = function(uid,path) {
|
|
var n = vfsdata.api.getNode(uid,path);
|
|
if (!n)
|
|
return null;
|
|
if (n.data.islink) // get the actual file data, not the link data
|
|
return vfsdata.api.getFile(uid,n.data.content);
|
|
return n.data.content;
|
|
}
|
|
vfsdata.api.mkDir = function(uid,path) {
|
|
if (vfsdata.api.getNode(0,path) != null)
|
|
return false;
|
|
var parts = path.split('/');
|
|
var name = parts.pop();
|
|
var dir = parts.join('/');
|
|
if (dir.length < 1)
|
|
dir = '/';
|
|
var parent = vfsdata.api.getNode(uid,dir);
|
|
if (!parent || !parent.data.isdir)
|
|
return false;
|
|
if (!checkCanWrite(uid,parent))
|
|
return false;
|
|
var n = mkNode();
|
|
n.name = name;
|
|
n.data.parent = parent;
|
|
n.data.content = [];
|
|
n.data.isdir = true;
|
|
n.mode = clite.lib.modestr('drwxr-xr-x');
|
|
n.uid = uid;
|
|
n.gid = clite.user.getGID(uid);
|
|
parent.data.content.push(n);
|
|
parent.time.modify = clite.time.sec();
|
|
return true;
|
|
}
|
|
vfsdata.api.mkFile = function(uid,path) {
|
|
if (vfsdata.api.getNode(uid,path) != null)
|
|
return false;
|
|
var parts = path.split('/');
|
|
var name = parts.pop();
|
|
var dir = parts.join('/');
|
|
if (dir.length < 1)
|
|
dir = '/';
|
|
var parent = vfsdata.api.getNode(0,dir);
|
|
if (!parent || !parent.data.isdir)
|
|
return false;
|
|
if (!checkCanWrite(uid,parent))
|
|
return false;
|
|
var n = mkNode();
|
|
n.name = name;
|
|
n.data.parent = parent;
|
|
n.data.content = '';
|
|
n.mode = clite.lib.modestr('-rw-r--r--');
|
|
n.uid = uid;
|
|
n.gid = clite.user.getGID(uid);
|
|
parent.data.content.push(n);
|
|
parent.time.modify = clite.time.sec();
|
|
return true;
|
|
}
|
|
vfsdata.api.mkLink = function(uid,path,target) {
|
|
if (vfsdata.api.getNode(uid,path) != null)
|
|
return false;
|
|
var parts = path.split('/');
|
|
var name = parts.pop();
|
|
var dir = parts.join('/');
|
|
if (dir.length < 1)
|
|
dir = '/';
|
|
var parent = vfsdata.api.getNode(uid,dir);
|
|
if (!parent || !parent.data.isdir)
|
|
return false;
|
|
if (!checkCanWrite(uid,parent))
|
|
return false;
|
|
var n = mkNode();
|
|
n.name = name;
|
|
n.data.parent = parent;
|
|
n.data.islink = true;
|
|
n.data.content = target;
|
|
n.mode = clite.lib.modestr('lrw-r--r--');
|
|
n.uid = uid;
|
|
n.gid = clite.user.getGID(uid);
|
|
parent.data.content.push(n);
|
|
parent.time.modify = clite.time.sec();
|
|
return true;
|
|
}
|
|
vfsdata.api.mkPath = function(uid,path) {
|
|
var parts = path.split('/');
|
|
var p = '';
|
|
parts.forEach(function(part) {
|
|
if (part == '')
|
|
return;
|
|
p += '/'+part;
|
|
vfsdata.api.mkDir(uid,p);
|
|
});
|
|
if (vfsdata.api.getNode(uid,path))
|
|
return true;
|
|
return false;
|
|
}
|
|
vfsdata.api.remove = function(uid,path) {
|
|
var n = vfsdata.api.getNode(uid,path);
|
|
if (!n || (n.data.isdir && n.data.content.length > 0))
|
|
return false;
|
|
if (!checkCanWrite(uid,n))
|
|
return false;
|
|
var dir = clite.lib.dirname(path);
|
|
var parent = vfsdata.api.getNode(0,dir);
|
|
if (!parent)
|
|
return false;
|
|
if (!checkCanWrite(uid,parent))
|
|
return false;
|
|
var c = [];
|
|
parent.data.content.forEach(function(nn) {
|
|
if (nn == n)
|
|
return;
|
|
c.push(nn);
|
|
});
|
|
parent.data.content = c;
|
|
return true;
|
|
}
|
|
clite.vfs.getSavedFile = function(path) {
|
|
let n = vfsdata.api.getNode(0,path);
|
|
if (!n)
|
|
return null;
|
|
|
|
var str = n.uid.toString(10)+':'+n.gid.toString(10)+':';
|
|
str += n.mode.toString(10)+':'+n.time.access.toString(10)+':';
|
|
str += n.time.modify.toString(10)+':'+n.time.change.toString(10)+':';
|
|
let t = clite.lib.getFileType({node:n});
|
|
str += t.toString(10)+':';
|
|
if (typeof n.data.content === 'string' && (t == clite.io.types.FT_LINK || t == clite.io.types.FT_SCRIPT || t == clite.io.types.FT_TEXT))
|
|
str += n.data.content.length.toString(10)+':'+n.data.content;
|
|
return str
|
|
}
|
|
clite.vfs.setSavedFile = function(path,data) {
|
|
let ind = data.indexOf(':');
|
|
if (ind < 0)
|
|
return false;
|
|
let v = data.substring(0,ind);
|
|
let str = data.substring(ind+1);
|
|
let uid = parseInt(v,10);
|
|
ind = str.indexOf(':');
|
|
if (ind < 0)
|
|
return false;
|
|
v = str.substring(0,ind);
|
|
str = str.substring(ind+1);
|
|
let gid = parseInt(v,10);
|
|
ind = str.indexOf(':');
|
|
if (ind < 0)
|
|
return false;
|
|
v = str.substring(0,ind);
|
|
str = str.substring(ind+1);
|
|
let mode = parseInt(v,10)
|
|
ind = str.indexOf(':');
|
|
if (ind < 0)
|
|
return false;
|
|
v = str.substring(0,ind);
|
|
str = str.substring(ind+1);
|
|
let access = parseInt(v,10);
|
|
ind = str.indexOf(':');
|
|
if (ind < 0)
|
|
return false;
|
|
v = str.substring(0,ind);
|
|
str = str.substring(ind+1);
|
|
let modify = parseInt(v,10);
|
|
ind = str.indexOf(':');
|
|
if (ind < 0)
|
|
return false;
|
|
v = str.substring(0,ind);
|
|
str = str.substring(ind+1);
|
|
let change = parseInt(v,10);
|
|
ind = str.indexOf(':');
|
|
if (ind < 0)
|
|
return false;
|
|
v = str.substring(0,ind);
|
|
str = str.substring(ind+1);
|
|
let type = parseInt(v,10);
|
|
|
|
let size = 0;
|
|
let content = null;
|
|
ind = str.indexOf(':');
|
|
if (ind > -1) {
|
|
v = str.substring(0,ind);
|
|
content = str.substring(ind+1);
|
|
size = parseInt(v,10);
|
|
}
|
|
let n = vfsdata.api.getNode(0,path);
|
|
if (!n) {
|
|
var dir = clite.lib.dirname(path);
|
|
vfsdata.api.mkPath(0,dir);
|
|
switch (type) {
|
|
case clite.io.types.FT_DIR:
|
|
vfsdata.api.mkDir(0,path);
|
|
break;
|
|
case clite.io.types.FT_LINK:
|
|
vfsdata.api.mkLink(0,path,content);
|
|
break;
|
|
case clite.io.types.FT_SCRIPT:
|
|
case clite.io.types.FT_TEXT:
|
|
vfsdata.api.mkFile(0,path);
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
n = vfsdata.api.getNode(0,path);
|
|
if (!n)
|
|
return false;
|
|
}
|
|
let ntype = clite.lib.getFileType({node:n});
|
|
if (ntype != type) {
|
|
if (!(
|
|
(ntype == clite.io.types.FT_SCRIPT || ntype == clite.io.types.FT_TEXT)
|
|
&& (type == clite.io.types.FT_SCRIPT || type == clite.io.types.FT_TEXT)
|
|
))
|
|
return false;
|
|
}
|
|
|
|
n.uid = uid;
|
|
n.gid = gid;
|
|
n.mode = mode;
|
|
n.time.access = access;
|
|
n.time.modify = modify;
|
|
n.time.change = change;
|
|
if (content != null)
|
|
n.data.content = content;
|
|
|
|
return true;
|
|
}
|
|
clite.vfs.saveFile = function(path) {
|
|
if (!clite.state.cookiesAccepted)
|
|
return true;
|
|
var b = clite.state.bios.io.read(2);
|
|
if (!b)
|
|
return false;
|
|
var str = clite.vfs.getSavedFile(path);
|
|
if (!str) {
|
|
b.delFile(path);
|
|
return true;
|
|
}
|
|
b.addFile(path,str);
|
|
return true;
|
|
}
|
|
clite.vfs.restoreFile = function(path) {
|
|
var b = clite.state.bios.io.read(2);
|
|
if (!b)
|
|
return false;
|
|
var d = b.getFile(path);
|
|
if (!d)
|
|
return false;
|
|
return clite.vfs.setSavedFile(path,d);
|
|
}
|
|
|
|
// make some directories
|
|
vfsdata.api.mkDir(0,'/bin');
|
|
vfsdata.api.mkDir(0,'/dev');
|
|
vfsdata.api.mkDir(0,'/etc');
|
|
vfsdata.api.mkDir(0,'/lib');
|
|
vfsdata.api.mkDir(0,'/proc');
|
|
vfsdata.api.mkDir(0,'/root');
|
|
vfsdata.api.mkDir(0,'/tmp');
|
|
vfsdata.api.mkDir(0,'/usr');
|
|
vfsdata.api.mkDir(0,'/usr/clite');
|
|
vfsdata.api.mkDir(0,'/usr/clite/web');
|
|
vfsdata.api.mkDir(0,'/usr/home');
|
|
vfsdata.api.mkDir(0,'/usr/share');
|
|
vfsdata.api.mkDir(0,'/usr/share/docs');
|
|
vfsdata.api.mkDir(0,'/usr/share/site');
|
|
vfsdata.api.mkDir(0,'/usr/src');
|
|
vfsdata.api.mkDir(0,'/usr/src/libs');
|
|
vfsdata.api.mkDir(0,'/var');
|
|
vfsdata.api.mkFile(0,'/var/logs');
|
|
|
|
var n = vfsdata.api.getNode(0,'/root');
|
|
if (n)
|
|
n.mode = clite.lib.modestr('drwx------');
|
|
n = vfsdata.api.getNode(0,'/tmp');
|
|
if (n)
|
|
n.mode = clite.lib.modestr('drwxrwxrwt');
|
|
|
|
clite.io.init();
|
|
vfsdata.api.isinit = true;
|
|
|
|
clite.core.execSafeAsync(function() {clite.vfs.init = null;});
|
|
},
|
|
getSavedFile:function(path) {
|
|
return null;
|
|
},
|
|
setSavedFile:function(path,data) {
|
|
return false;
|
|
},
|
|
getApi:null
|
|
};
|
|
|
|
clite.log = {
|
|
logs:[],
|
|
init:function(vfsapi) {
|
|
clite.log.write = function(txt) {
|
|
if (clite.state.runlevel != 3) {
|
|
try {
|
|
clite.console.write(txt+'\n');
|
|
} catch(e) {}
|
|
}
|
|
var n = vfsapi.getNode(0,'/var/logs');
|
|
if (n)
|
|
n.data.content += txt+'\n';
|
|
console.log(txt);
|
|
}
|
|
var n = vfsapi.getNode(0,'/var/logs');
|
|
if (n)
|
|
n.data.content = clite.log.logs.join('\n')+'\n';
|
|
clite.core.execSafeAsync(function(){clite.log.init = null;});
|
|
},
|
|
write:function(txt) {
|
|
clite.log.logs.push(txt);
|
|
try {
|
|
clite.console.write(txt+'\n');
|
|
} catch(e) {}
|
|
console.log(txt);
|
|
}
|
|
};
|
|
|
|
clite.tty = {
|
|
data:{
|
|
active:null,
|
|
activeID:-1,
|
|
ttys:[],
|
|
control:false
|
|
},
|
|
internal:{
|
|
genTTY:function() {return null;},
|
|
write:function(id,ch) {
|
|
var cc = ch.charCodeAt(0);
|
|
var updateLine = false;
|
|
var updateAll = false;
|
|
var t = clite.tty.data.ttys[id];
|
|
var updateX = t.x;
|
|
var updateY = t.y;
|
|
|
|
switch (cc) {
|
|
case 8: //BS
|
|
t.x--;
|
|
updateLine = true;
|
|
break;
|
|
case 10: //LF
|
|
t.y++;
|
|
break;
|
|
case 13: //CR
|
|
t.x = 0;
|
|
updateLine = true;
|
|
break;
|
|
default:
|
|
t.data[updateY][updateX].ch = ch;
|
|
t.x++;
|
|
}
|
|
|
|
if (t.x < 0) {
|
|
t.y--;
|
|
t.x = 79;
|
|
}
|
|
|
|
if (t.y < 0)
|
|
t.y = 0;
|
|
|
|
if (t.x > 79) {
|
|
t.y++;
|
|
t.x = 0;
|
|
}
|
|
// theoretically this should be fine, in practice it may not
|
|
if (t.y > 24) {
|
|
var l = t.data.shift()
|
|
for (var i=0; i<80; i++) {
|
|
l[i].ch = ' ';
|
|
l[i].fg = t.fg;
|
|
l[i].bg = t.bg;
|
|
}
|
|
t.y = 24;
|
|
t.data.push(l);
|
|
updateAll = true;
|
|
}
|
|
|
|
if (clite.tty.data.activeID != id)
|
|
return;
|
|
|
|
if (updateAll) {
|
|
clite.console.drawBuff(t.data);
|
|
}else if (updateLine) {
|
|
clite.console.drawLine(updateY,t.data[updateY]);
|
|
}else{
|
|
clite.console.drawAt(updateX,updateY,t.data[updateY][updateX]);
|
|
}
|
|
clite.console.setCursor(t.x,t.y);
|
|
},
|
|
popLine:function(t) {
|
|
var off = -1;
|
|
var l = '';
|
|
for (var i=0; i<t.buff.length; i++) {
|
|
if (t.buff[i].code == 10) {
|
|
off = i+1;
|
|
break;
|
|
}
|
|
// special case, returns any non-character key found
|
|
if (t.buff[i].code <32 || t.buff[i].code > 126) {
|
|
var k = t.buff.splice(i,1);
|
|
return k.code;
|
|
}
|
|
l += t.buff[i].char;
|
|
}
|
|
if (off < 0)
|
|
return null;
|
|
|
|
t.buff = t.buff.slice(off);
|
|
return l;
|
|
},
|
|
getControl:function(ch) {
|
|
var key = {
|
|
down:false,
|
|
char:0,
|
|
lchar:0,
|
|
code:0
|
|
};
|
|
var ind = '-abcdefghijklmnopqrstuvwxyz[\\]^_?';
|
|
var i = ind.indexOf(ch);
|
|
if (i>0) {
|
|
key.char = String.fromCharCode(i);
|
|
key.lchar = key.char;
|
|
key.code = i;
|
|
return key;
|
|
}
|
|
return null;
|
|
}
|
|
},
|
|
init:function(vfsapi) {
|
|
clite.tty.internal.genTTY = function() {
|
|
var l = [];
|
|
for (var y=0; y<25; y++) {
|
|
l[y] = [];
|
|
for (var x=0; x<80; x++) {
|
|
l[y].push({bg:0,fg:15,bk:false,ch:' '});
|
|
}
|
|
}
|
|
var id = clite.tty.data.ttys.length;
|
|
var t = {
|
|
id:id,
|
|
x:0,
|
|
y:0,
|
|
fg:15,
|
|
bg:0,
|
|
data:l,
|
|
rcb:null,
|
|
buff:[],
|
|
raw:false,
|
|
echo:true
|
|
};
|
|
clite.tty.data.ttys.push(t);
|
|
|
|
|
|
vfsapi.mkFile(0,'/dev/tty'+id);
|
|
let n = vfsapi.getNode(0,'/dev/tty'+id);
|
|
if (!n) {
|
|
clite.log.write('tty creation failure (/dev/tty'+id+')');
|
|
return null;
|
|
}
|
|
n.data.content = {
|
|
ttyid:id,
|
|
read:function(cb) {return clite.tty.read(n.data.content.ttyid,cb);},
|
|
write:function(data) {return clite.tty.write(n.data.content.ttyid,data);}
|
|
};
|
|
n.mode = clite.lib.modestr('crw-rw-rw-');
|
|
n.data.isdev = true;
|
|
n.data.istty = true;
|
|
|
|
clite.tty.data.ttys.push(t);
|
|
|
|
return t;
|
|
}
|
|
clite.tty.data.active = clite.tty.internal.genTTY();
|
|
if (!clite.tty.data.active)
|
|
return false;
|
|
clite.tty.data.activeID = clite.tty.data.active.id;
|
|
return true;
|
|
},
|
|
input:function(key) {
|
|
if (key.down) {
|
|
if (key.code == -1)
|
|
clite.tty.data.control = true;
|
|
return;
|
|
}
|
|
if (key.code < 0) {
|
|
if (key.code == -1) {
|
|
clite.tty.data.control = false;
|
|
return;
|
|
}else{
|
|
if (clite.tty.data.active && typeof clite.tty.data.active.rcb === 'function') {
|
|
var f = clite.tty.data.active.rcb;
|
|
clite.tty.data.active.rcb = null;
|
|
|
|
f(key.code);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if (clite.tty.data.control) {
|
|
var k = clite.tty.internal.getControl(key.char);
|
|
if (k)
|
|
key = k;
|
|
}
|
|
if (!clite.tty.data.active)
|
|
return;
|
|
var t = clite.tty.data.active;
|
|
// TODO: this hides control characters from echoing... do we want that?
|
|
if (t.echo && ((key.code > 31 && key.code < 127) || key.code == 10 || key.code == 9))
|
|
clite.tty.write(t.id,key.char);
|
|
if (t.raw) {
|
|
if (typeof t.rcb === 'function') {
|
|
var f = t.rcb;
|
|
t.rcb = null;
|
|
if ((key.code > 31 && key.code < 127) || key.code == 10 || key.code == 9) {
|
|
f(key.char);
|
|
}else{
|
|
f(key.code);
|
|
}
|
|
}else{
|
|
t.buff.push(key);
|
|
}
|
|
return;
|
|
}
|
|
// Backspace
|
|
if (key.code == 8) {
|
|
if (t.buff.pop() && t.echo) {
|
|
clite.tty.write(t.id,String.fromCharCode(8));
|
|
clite.tty.write(t.id,' ');
|
|
clite.tty.write(t.id,String.fromCharCode(8));
|
|
}
|
|
return;
|
|
}
|
|
t.buff.push(key);
|
|
if (typeof t.rcb !== 'function')
|
|
return;
|
|
if (key.code != 10)
|
|
return;
|
|
var l = clite.tty.internal.popLine(t);
|
|
if (l == null)
|
|
return;
|
|
|
|
var f = t.rcb;
|
|
t.rcb = null;
|
|
|
|
f(l);
|
|
},
|
|
fake:function(str) {
|
|
for (var i=0; i<str.length; i++) {
|
|
let k = {
|
|
down:false,
|
|
code:str.charCodeAt(i),
|
|
char:str[i],
|
|
lchar:str[i].toLowerCase()
|
|
};
|
|
clite.tty.input(k);
|
|
}
|
|
},
|
|
read:function(id,cb) {
|
|
if (id<0 || id>=clite.tty.data.ttys.length)
|
|
return false;
|
|
|
|
var t = clite.tty.data.ttys[id];
|
|
if (t.raw) {
|
|
if (t.buff.length > 0) {
|
|
var k = t.buff.shift();
|
|
cb(k.char);
|
|
return true;
|
|
}
|
|
}else{
|
|
var l = clite.tty.internal.popLine(t);
|
|
if (l != null) {
|
|
cb(l);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
clite.tty.data.ttys[id].rcb = cb;
|
|
|
|
return true;
|
|
},
|
|
write:function(id,str) {
|
|
if (id<0 || id>=clite.tty.data.ttys.length)
|
|
return;
|
|
|
|
for (var i=0; i<str.length; i++) {
|
|
var ch = str[i];
|
|
var cc = ch.charCodeAt(0);
|
|
// TODO: escape/control sequences
|
|
if (cc < 32 || cc > 126) {
|
|
switch (cc) {
|
|
case 8:
|
|
clite.tty.internal.write(id,String.fromCharCode(8));
|
|
break;
|
|
case 9:
|
|
{
|
|
var cr = clite.tty.getCursor(id);
|
|
if (Array.isArray(cr)) {
|
|
var m = 4-(cr[0]%4);
|
|
for (var j=0; j<m; j++) {
|
|
clite.tty.internal.write(id,' ');
|
|
}
|
|
}else{
|
|
clite.tty.internal.write(id,'^');
|
|
}
|
|
break;
|
|
}
|
|
case 10: // NL/LF to CR+LF
|
|
clite.tty.internal.write(id,String.fromCharCode(13));
|
|
clite.tty.internal.write(id,String.fromCharCode(10));
|
|
break;
|
|
default:
|
|
clite.tty.internal.write(id,'^');
|
|
}
|
|
}else{
|
|
clite.tty.internal.write(id,ch);
|
|
}
|
|
}
|
|
},
|
|
clear:function(id) {
|
|
if (id<0 || id>=clite.tty.data.ttys.length)
|
|
return;
|
|
var t = clite.tty.data.ttys[id];
|
|
|
|
for (var y=0; y<25; y++) {
|
|
t.data[y] = [];
|
|
for (var x=0; x<80; x++) {
|
|
t.data[y].push({bg:t.bg,fg:t.fg,bk:false,ch:' '});
|
|
}
|
|
}
|
|
|
|
t.x = 0;
|
|
t.y = 0;
|
|
|
|
if (id != clite.tty.data.activeID)
|
|
return;
|
|
|
|
clite.console.drawBuff(t.data);
|
|
clite.console.setCursor(0,0);
|
|
},
|
|
ttyctrl:function(id,fn,v) {
|
|
if (id<0 || id>=clite.tty.data.ttys.length)
|
|
return;
|
|
var t = clite.tty.data.ttys[id];
|
|
switch (fn) {
|
|
case 'raw':
|
|
if (typeof v === 'boolean')
|
|
t.raw = v;
|
|
return t.raw;
|
|
break;
|
|
case 'atton':
|
|
break;
|
|
case 'attoff':
|
|
break;
|
|
case 'echo':
|
|
if (typeof v === 'boolean')
|
|
t.echo = v;
|
|
return t.echo;
|
|
break;
|
|
case 'cols':
|
|
return t.data[0].length;
|
|
break;
|
|
case 'rows':
|
|
return t.data.length;
|
|
break;
|
|
case 'sane':
|
|
t.raw = false;
|
|
t.echo = true;
|
|
return true;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
},
|
|
getCursor:function(id) {
|
|
if (id<0 || id>=clite.tty.data.ttys.length)
|
|
return;
|
|
var t = clite.tty.data.ttys[id];
|
|
return [t.x,t.y];
|
|
},
|
|
setCursor:function(id,x,y) {
|
|
if (id<0 || id>=clite.tty.data.ttys.length)
|
|
return;
|
|
var t = clite.tty.data.ttys[id];
|
|
if (x > -1 && x < 80)
|
|
t.x = x;
|
|
if (y > -1 && y < 25)
|
|
t.y = y;
|
|
if (id == clite.tty.data.activeID)
|
|
clite.console.setCursor(x,y);
|
|
}
|
|
};
|
|
|
|
clite.console = {
|
|
data:{
|
|
vgaio:null,
|
|
state:0,
|
|
size:[80,25],
|
|
cursor:[0,0]
|
|
},
|
|
init:function() {
|
|
// set graphics to text mode, and get the VGA api
|
|
clite.state.bios.io.write(1,1);
|
|
clite.console.data.vgaio = clite.state.bios.io.read(1);
|
|
// set a keyboard handler, then flush the buffer
|
|
clite.state.bios.io.write(2,clite.tty.input);
|
|
clite.state.bios.io.write(2);
|
|
clite.console.data.state = 1;
|
|
clite.console.clear();
|
|
},
|
|
clear:function() {
|
|
if (clite.console.data.state == 1) {
|
|
for (var y=0; y<25; y++) {
|
|
for (var x=0; x<80; x++) {
|
|
clite.console.data.vgaio.write(x,y,' ');
|
|
clite.console.data.vgaio.writeSet(0,x,y,0);
|
|
clite.console.data.vgaio.writeSet(1,x,y,15);
|
|
clite.console.data.vgaio.writeSet(2,x,y,false);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
drawBuff:function(buff) {
|
|
if (clite.console.data.state == 1) {
|
|
for (var y=0; y<25; y++) {
|
|
for (var x=0; x<80; x++) {
|
|
clite.console.data.vgaio.write(x,y,buff[y][x].ch);
|
|
clite.console.data.vgaio.writeSet(0,x,y,buff[y][x].bg);
|
|
clite.console.data.vgaio.writeSet(1,x,y,buff[y][x].fg);
|
|
clite.console.data.vgaio.writeSet(2,x,y,buff[y][x].bk);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
drawLine:function(y,buff) {
|
|
if (clite.console.data.state == 1) {
|
|
for (var x=0; x<80; x++) {
|
|
clite.console.data.vgaio.write(x,y,buff[x].ch);
|
|
clite.console.data.vgaio.writeSet(0,x,y,buff[x].bg);
|
|
clite.console.data.vgaio.writeSet(1,x,y,buff[x].fg);
|
|
clite.console.data.vgaio.writeSet(2,x,y,buff[x].bk);
|
|
}
|
|
}
|
|
},
|
|
drawAt:function(x,y,data) {
|
|
if (clite.console.data.state == 1) {
|
|
if (x<0 || x>79 || y<0 || y>24)
|
|
return;
|
|
clite.console.data.vgaio.write(x,y,data.ch);
|
|
clite.console.data.vgaio.writeSet(0,x,y,data.bg);
|
|
clite.console.data.vgaio.writeSet(1,x,y,data.fg);
|
|
clite.console.data.vgaio.writeSet(2,x,y,data.bk);
|
|
}
|
|
},
|
|
moveCursor:function() {
|
|
clite.console.data.vgaio.writeSet(3,clite.console.data.cursor[0],clite.console.data.cursor[1],true);
|
|
},
|
|
setCursor:function(x,y) {
|
|
clite.console.data.cursor[0] = x;
|
|
clite.console.data.cursor[1] = y;
|
|
clite.console.moveCursor();
|
|
},
|
|
read:function() {
|
|
},
|
|
write:function(str) {
|
|
var d = {
|
|
ch:'',
|
|
bg:0,
|
|
fg:15,
|
|
bk:false
|
|
};
|
|
clite.console.moveCursor();
|
|
var x = clite.console.data.cursor[0];
|
|
var y = clite.console.data.cursor[1];
|
|
for (var i=0; i<str.length; i++) {
|
|
if (str[i] == '\n') {
|
|
x = 0;
|
|
y++;
|
|
continue;
|
|
}
|
|
if (x >= 80) {
|
|
x = 0;
|
|
y++;
|
|
}
|
|
d.ch = str[i];
|
|
clite.console.drawAt(x,y,d);
|
|
x++;
|
|
}
|
|
clite.console.setCursor(x,y);
|
|
}
|
|
}
|
|
|
|
clite.lib = {
|
|
// makes text html safe
|
|
htmlEncode:function(txt) {
|
|
var rtxt = txt.replace(/&/g, '&').replace(/>/g, '>').replace(/</g, '<');
|
|
if (rtxt.length > 0 && rtxt[rtxt.length-1] == ' ')
|
|
return rtxt.substring(0,rtxt.length-1)+' ';
|
|
return rtxt;
|
|
},
|
|
// returns the file name of a path /tmp/test/foo = foo
|
|
basename:function(txt) {
|
|
var parts = txt.split('/');
|
|
return parts[parts.length-1];
|
|
},
|
|
// returns the directory name of a path /tmp/test/foo = /tmp/test
|
|
dirname:function(txt) {
|
|
var parts = txt.split('/');
|
|
parts.pop();
|
|
return parts.join('/');
|
|
},
|
|
// returns an int identifier for the file type
|
|
getFileType:function(fd) {
|
|
if (fd.node.data.content == null) {
|
|
if (fd.node.data.remote != null)
|
|
return clite.io.types.FT_REMOTE;
|
|
return clite.io.types.FT_UNKNOWN;
|
|
}
|
|
if (fd.node.data.islink)
|
|
return clite.io.types.FT_LINK;
|
|
if (fd.node.data.isdir)
|
|
return clite.io.types.FT_DIR;
|
|
if (fd.node.data.isdev)
|
|
return clite.io.types.FT_DEV;
|
|
const type = typeof fd.node.data.content;
|
|
switch (type) {
|
|
case 'string': // text file
|
|
if (fd.node.data.content.substring(0,2) == '#!')
|
|
return clite.io.types.FT_SCRIPT;
|
|
return clite.io.types.FT_TEXT;
|
|
break;
|
|
case 'function': // executable
|
|
if (fd.node.data.content.length == 2) {
|
|
return clite.io.types.FT_LIBRARY;
|
|
}else if (fd.node.data.content.length == 3) {
|
|
return clite.io.types.FT_BINARY;
|
|
}else{
|
|
return clite.io.types.FT_UNKNOWN;
|
|
}
|
|
break;
|
|
case 'object':
|
|
if (Array.isArray(fd.node.data.content)) // directory
|
|
return clite.io.types.FT_DIR;
|
|
if (fd.node.data.content instanceof HTMLImageElement)
|
|
return clite.io.types.FT_IMAGE;
|
|
case 'symbol':
|
|
case 'boolean':
|
|
case 'number':
|
|
case 'bigint':
|
|
case 'undefined':
|
|
default:
|
|
return clite.io.types.FT_UNKNOWN;
|
|
}
|
|
return clite.io.types.FT_UNKNOWN;
|
|
},
|
|
checkNodeReadable:function(node,pid) {
|
|
var uid = clite.proc.getUID(pid);
|
|
if (uid == 0)
|
|
return true;
|
|
if ((node.mode&clite.io.modes.S_IROTH) == clite.io.modes.S_IROTH)
|
|
return true;
|
|
if (clite.user.checkGID(uid,node.gid) && (node.mode&clite.io.modes.S_IRGRP) == clite.io.modes.S_IRGRP)
|
|
return true;
|
|
if (node.uid == uid && (node.mode&clite.io.modes.S_IRUSR) == clite.io.modes.S_IRUSR)
|
|
return true;
|
|
return false;
|
|
},
|
|
checkNodeWritable:function(node,pid) {
|
|
var uid = clite.proc.getUID(pid);
|
|
if (uid == 0)
|
|
return true;
|
|
if (node.uid == uid && (node.mode&clite.io.modes.S_IWUSR) == clite.io.modes.S_IWUSR)
|
|
return true;
|
|
// check for directory sticky bit on parent
|
|
if (node.parent && (node.parent.mode&clite.io.modes.S_ISVTX) == clite.io.modes.S_ISVTX && node.uid != uid)
|
|
return false;
|
|
if ((node.mode&clite.io.modes.S_IWOTH) == clite.io.modes.S_IWOTH)
|
|
return true;
|
|
if (clite.user.checkGID(uid,node.gid) && (node.mode&clite.io.modes.S_IWGRP) == clite.io.modes.S_IWGRP)
|
|
return true;
|
|
return false;
|
|
},
|
|
checkNodeExecutable:function(node,pid) {
|
|
var uid = clite.proc.getUID(pid);
|
|
if (uid == 0)
|
|
return true;
|
|
if ((node.mode&clite.io.modes.S_IXOTH) == clite.io.modes.S_IXOTH)
|
|
return true;
|
|
if (clite.user.checkGID(uid,node.gid) && (node.mode&clite.io.modes.S_IXGRP) == clite.io.modes.S_IXGRP)
|
|
return true;
|
|
if (node.uid == uid && (node.mode&clite.io.modes.S_IXUSR) == clite.io.modes.S_IXUSR)
|
|
return true;
|
|
return false;
|
|
},
|
|
// really basic hashing function
|
|
hash:function(str) {
|
|
let hash = 0;
|
|
if (str.length == 0)
|
|
return hash;
|
|
|
|
for (var i=0; i<str.length; i++) {
|
|
let char = str.charCodeAt(i);
|
|
hash = ((hash << 5) - hash) + char;
|
|
hash |= 0;
|
|
}
|
|
|
|
return hash;
|
|
},
|
|
// creates a string from a file mode (0777 -> -rwxrwxrwx)
|
|
strmode:function(mode) {
|
|
var str = mode.toString(8);
|
|
var t = '0';
|
|
var s = '0';
|
|
|
|
var su = false;
|
|
var sg = false;
|
|
var sd = false;
|
|
|
|
var p = '';
|
|
|
|
var perms = ['---','---','---'];
|
|
|
|
str = str.padStart(3,'0');
|
|
|
|
if (str.length > 4) {
|
|
t = str.substring(0,str.length-4);
|
|
str = str.substring(t.length);
|
|
}
|
|
t = t.padStart(2,'0');
|
|
if (str.length == 4) {
|
|
s = str.substring(0,1);
|
|
str = str.substring(1);
|
|
}
|
|
|
|
if (s != '0') {
|
|
var si = parseInt(s.padEnd(4,'0'),8);
|
|
if ((si & clite.io.modes.S_ISUID) == clite.io.modes.S_ISUID)
|
|
su = true;
|
|
if ((si & clite.io.modes.S_ISGID) == clite.io.modes.S_ISGID)
|
|
sg = true;
|
|
if ((si & clite.io.modes.S_ISVTX) == clite.io.modes.S_ISVTX)
|
|
sd = true;
|
|
}
|
|
switch (t) {
|
|
case '01':
|
|
p = 'b';
|
|
break;
|
|
case '02':
|
|
p = 'c';
|
|
break;
|
|
case '04':
|
|
p = 'f';
|
|
break;
|
|
case '10':
|
|
p = 'd';
|
|
break;
|
|
case '20':
|
|
p = 'l';
|
|
break;
|
|
case '40':
|
|
p = 's';
|
|
break;
|
|
case '00':
|
|
default:
|
|
p = '-';
|
|
}
|
|
|
|
|
|
for (var i=0; i<str.length; i++) {
|
|
switch(str[i]) {
|
|
case '0':
|
|
perms[i] = '---';
|
|
break;
|
|
case '1':
|
|
perms[i] = '--x';
|
|
break;
|
|
case '2':
|
|
perms[i] = '-w-';
|
|
break;
|
|
case '3':
|
|
perms[i] = '-wx';
|
|
break;
|
|
case '4':
|
|
perms[i] = 'r--';
|
|
break;
|
|
case '5':
|
|
perms[i] = 'r-x';
|
|
break;
|
|
case '6':
|
|
perms[i] = 'rw-';
|
|
break;
|
|
case '7':
|
|
perms[i] = 'rwx';
|
|
break;
|
|
}
|
|
}
|
|
if (su) {
|
|
if (perms[0][2] == 'x') {
|
|
perms[0] = perms[0].substring(0,2)+'s';
|
|
}else{
|
|
perms[0] = perms[0].substring(0,2)+'S';
|
|
}
|
|
}
|
|
if (sg) {
|
|
if (perms[1][2] == 'x') {
|
|
perms[1] = perms[1].substring(0,2)+'s';
|
|
}else{
|
|
perms[1] = perms[1].substring(0,2)+'S';
|
|
}
|
|
}
|
|
if (sd) {
|
|
if (perms[2][2] == 'x') {
|
|
perms[2] = perms[2].substring(0,2)+'t';
|
|
}else{
|
|
perms[2] = perms[2].substring(0,2)+'T';
|
|
}
|
|
}
|
|
return p+perms.join('');
|
|
},
|
|
// creates a file mode from a string (-rwxrwxrwx -> 0777)
|
|
modestr:function(perms) {
|
|
var m = 0;
|
|
perms = perms.padStart(10,'-');
|
|
if (perms[0] != '-') {
|
|
switch (perms[0]) {
|
|
case 'b':
|
|
m |= clite.io.modes.S_IFBLK;
|
|
break;
|
|
case 'c':
|
|
m |= clite.io.modes.S_IFCHR;
|
|
break;
|
|
case 'f':
|
|
m |= clite.io.modes.S_IFIFO;
|
|
break;
|
|
case 'd':
|
|
m |= clite.io.modes.S_IFDIR;
|
|
break;
|
|
case 'l':
|
|
m |= clite.io.modes.S_IFLNK;
|
|
break;
|
|
case 's':
|
|
m |= clite.io.modes.S_IFSOCK;
|
|
break;
|
|
default:;
|
|
}
|
|
}
|
|
if (perms[1] == 'r')
|
|
m |= clite.io.modes.S_IRUSR;
|
|
if (perms[2] == 'w')
|
|
m |= clite.io.modes.S_IWUSR;
|
|
if (perms[3] == 'x')
|
|
m |= clite.io.modes.S_IXUSR;
|
|
if (perms[3] == 'S')
|
|
m |= clite.io.modes.S_ISUID;
|
|
if (perms[3] == 's') {
|
|
m |= clite.io.modes.S_IXUSR;
|
|
m |= clite.io.modes.S_ISUID;
|
|
}
|
|
if (perms[4] == 'r')
|
|
m |= clite.io.modes.S_IRGRP;
|
|
if (perms[5] == 'w')
|
|
m |= clite.io.modes.S_IWGRP;
|
|
if (perms[6] == 'x')
|
|
m |= clite.io.modes.S_IXGRP;
|
|
if (perms[6] == 'S')
|
|
m |= clite.io.modes.S_ISGID;
|
|
if (perms[6] == 's') {
|
|
m |= clite.io.modes.S_IXGRP;
|
|
m |= clite.io.modes.S_ISGID;
|
|
}
|
|
if (perms[7] == 'r')
|
|
m |= clite.io.modes.S_IROTH;
|
|
if (perms[8] == 'w')
|
|
m |= clite.io.modes.S_IWOTH;
|
|
if (perms[9] == 'x')
|
|
m |= clite.io.modes.S_IXOTH;
|
|
if (perms[9] == 'T')
|
|
m |= clite.io.modes.S_ISVTX;
|
|
if (perms[9] == 't') {
|
|
m |= clite.io.modes.S_IXOTH;
|
|
m |= clite.io.modes.S_ISVTX;
|
|
}
|
|
|
|
return m;
|
|
},
|
|
fork:function(env,io,call) {
|
|
var pid = clite.proc.add(io.pid,call);
|
|
if (!pid)
|
|
return 0;
|
|
var nio = Object.create(io);
|
|
nio.pid = pid;
|
|
nio.exit = function(v) {
|
|
clite.lib.exit(pid,v);
|
|
}
|
|
var nenv = structuredClone(env);
|
|
// this is esssentially the dynamic linker
|
|
nio.include = function(name) {
|
|
var file = '';
|
|
clite.libs.index.forEach(function(e) {
|
|
if (e.header == name)
|
|
file = e.file;
|
|
});
|
|
if (file == '')
|
|
return null;
|
|
|
|
var fd = clite.io.open(0,'/lib/'+file+'.so',clite.io.flags.O_RDONLY);
|
|
if (!fd)
|
|
return null;
|
|
|
|
var lib = null;
|
|
try{
|
|
lib = fd.node.data.content(nio,nenv);
|
|
}catch(err) {}
|
|
clite.io.close(0,fd);
|
|
if (lib)
|
|
return lib;
|
|
return null;
|
|
}
|
|
clite.core.execSafeAsync(function() {
|
|
call(nenv,nio);
|
|
});
|
|
return pid;
|
|
},
|
|
exec:function(path,args,env,io) {
|
|
var fd = clite.io.open(io.pid,path,clite.io.flags.O_RDONLY|clite.io.flags.O_EXEC|clite.io.flags.O_SYNC);
|
|
if (!fd)
|
|
return -1;
|
|
|
|
if (!fd.canexec) {
|
|
clite.io.close(io.pid,fd);
|
|
return -3;
|
|
}
|
|
|
|
var ft = clite.lib.getFileType(fd);
|
|
if (ft == clite.io.types.FT_SCRIPT) {
|
|
var fl = clite.io.readLine(io.pid,fd);
|
|
clite.io.close(fd);
|
|
if (!fl)
|
|
return -5;
|
|
var e = fl.substring(2);
|
|
fd = clite.io.open(io.pid,e,clite.io.flags.O_RDONLY|clite.io.flags.O_EXEC|clite.io.flags.O_SYNC);
|
|
if (!fd)
|
|
return -6;
|
|
ft = clite.lib.getFileType(fd);
|
|
}
|
|
if (ft != clite.io.types.FT_BINARY) {
|
|
clite.io.close(io.pid,fd);
|
|
return -4;
|
|
}
|
|
|
|
if ((fd.node.mode&clite.io.modes.S_ISGID) == clite.io.modes.S_ISGID) {
|
|
if (!clite.proc.setGID(io.pid,fd.node.gid,true))
|
|
return -7;
|
|
}
|
|
|
|
if ((fd.node.mode&clite.io.modes.S_ISUID) == clite.io.modes.S_ISUID) {
|
|
if (!clite.proc.setUID(io.pid,fd.node.uid,true))
|
|
return -8;
|
|
}
|
|
io.include = function(name) {
|
|
var file = '';
|
|
clite.libs.index.forEach(function(e) {
|
|
if (e.header == name)
|
|
file = e.file;
|
|
});
|
|
if (file == '')
|
|
return null;
|
|
|
|
var fd = clite.io.open(0,'/lib/'+file+'.so',clite.io.flags.O_RDONLY);
|
|
if (!fd)
|
|
return null;
|
|
|
|
var lib = null;
|
|
try{
|
|
lib = fd.node.data.content(io,env);
|
|
}catch(err) {}
|
|
clite.io.close(0,fd);
|
|
if (lib)
|
|
return lib;
|
|
return null;
|
|
}
|
|
|
|
clite.proc.update(io.pid,args.join(' '),fd.node.data.content);
|
|
|
|
var r = clite.core.execSafe(function() {
|
|
var rr = fd.node.data.content(args,env,io);
|
|
if (typeof rr == 'number')
|
|
io.exit(rr);
|
|
});
|
|
clite.io.close(io.pid,fd);
|
|
if (!r) {
|
|
io.exit(-2);
|
|
return -2;
|
|
}
|
|
return 0;
|
|
},
|
|
exit:function(pid,v) {
|
|
if (pid > -1)
|
|
clite.proc.exit(pid,v);
|
|
}
|
|
}
|
|
|
|
window.BIOSentry(clite.init);
|