198 lines
5.2 KiB
TypeScript
Executable File
198 lines
5.2 KiB
TypeScript
Executable File
import { VirtualFS, type TreeNode } from './fs';
|
|
import { Terminal, type PrintData } from '../terminal/terminal';
|
|
import { Stack } from '../stack';
|
|
import { PASSWD, type User } from './etc/userData';
|
|
import { ExitCode } from './metadata';
|
|
import { COMMANDS, type CommandArgs, type CommandResultData, type ICommand } from './commandRegistry';
|
|
import { GROUP, type Group } from './etc/groupData';
|
|
|
|
export type BashInitArgs = {
|
|
io?: Terminal;
|
|
instanceId: number;
|
|
user: {username: string, password: string};
|
|
fs?: VirtualFS;
|
|
};
|
|
|
|
export type Result = {
|
|
exitCode: ExitCode;
|
|
path: number; //the inode of the place that the command was executed in
|
|
resultData?: CommandResultData;
|
|
};
|
|
|
|
export class Bash {
|
|
private readonly _instanceId: number;
|
|
private vfs: VirtualFS;
|
|
private _passwd: User[];
|
|
private _userInstances: Stack<User>;
|
|
private _group: Group[];
|
|
private _terminal!: Terminal;
|
|
private user: User;
|
|
private readonly _commands: Record<string, ICommand>;
|
|
|
|
constructor(args: BashInitArgs) {
|
|
this._instanceId = args.instanceId
|
|
this._commands = COMMANDS;
|
|
this._passwd = PASSWD;
|
|
this._group = GROUP;
|
|
this._userInstances = new Stack<User>();
|
|
this._terminal = args.io!;
|
|
|
|
const loginResult = this.userLogin(args.user.username, args.user.password);
|
|
if(loginResult == ExitCode.ERROR)
|
|
this._terminal.throwExeption(
|
|
`Failed to initialize bash instance - access denied for user ${args.user.username}`,
|
|
ExitCode.ERROR
|
|
)
|
|
|
|
|
|
this.user = this._userInstances.peek()!
|
|
this.vfs = this._terminal.fileSystem;
|
|
}
|
|
|
|
private _initNewUserSession(user: User): User {
|
|
if(!this._passwd.includes(user))
|
|
this._terminal.throwExeption(`user not found under the name ${user.username}`, ExitCode.ERROR)
|
|
|
|
this._userInstances.push(user);
|
|
return user
|
|
}
|
|
|
|
private _appendNewResult(inode: number, output: any, cmd: string) {
|
|
const data: PrintData = {
|
|
path: this.vfs.formatPath(this.vfs.getPathByInode(inode)),
|
|
output: output,
|
|
cmd: cmd
|
|
};
|
|
console.log(data);
|
|
this._terminal.PrintOutput(data);
|
|
}
|
|
|
|
updateHistory(input: string): void {
|
|
if ((this.user.history.length = 255)) {
|
|
this.user.history.unshift(...this.user.history.splice(-1));
|
|
this.user.history[0] = input;
|
|
} else {
|
|
this.user.history.push(input);
|
|
this.user.history.unshift(...this.user.history.splice(-1));
|
|
}
|
|
}
|
|
|
|
clearTerminal(): void {
|
|
this._terminal.clearTerminal();
|
|
}
|
|
|
|
getCwd(): number {
|
|
return this.vfs.cwd;
|
|
}
|
|
|
|
getPwd(): number {
|
|
return this.vfs.pwd;
|
|
}
|
|
|
|
getUser(): User {
|
|
return this.user;
|
|
}
|
|
|
|
getFs(): VirtualFS {
|
|
return this.vfs;
|
|
}
|
|
|
|
getTerminalWidth(): number {
|
|
return this._terminal.getTerminalWidth();
|
|
}
|
|
|
|
getTerminalFontSize(): number {
|
|
return this._terminal.getFontSize();
|
|
}
|
|
|
|
hasSudoPerms(uid: number): boolean {
|
|
return this._group[1].members.includes(uid);
|
|
}
|
|
|
|
async executeCommand(commandName: string, args: CommandArgs) {
|
|
let result: Result = { exitCode: ExitCode.ERROR, path: this.getCwd() };
|
|
const command = this._commands[commandName];
|
|
if (!command) this.throwError(result);
|
|
|
|
if (command.root) {
|
|
if (this.hasSudoPerms(this.user.uid)) {
|
|
let out: Result = command.method.call(this, args);
|
|
this._appendNewResult(this.getCwd(), out, this.user.history[0]);
|
|
}
|
|
this.throwError(result);
|
|
}
|
|
|
|
let out: Result = command.method.call(this, args);
|
|
console.log(out);
|
|
this._appendNewResult(out.path, out.resultData?.data, this.user.history[0]);
|
|
}
|
|
|
|
throwError(result: Result): void {
|
|
switch (result.exitCode) {
|
|
default: {
|
|
throw new Error(`Error, dont know where, just look for it;`);
|
|
}
|
|
}
|
|
}
|
|
|
|
userLogout() {
|
|
this._userInstances.pop();
|
|
if (this._userInstances.size() === 0) {
|
|
//TODO: Implement system logout
|
|
} else {
|
|
//this.changeUser(this._instances.peek()!);
|
|
}
|
|
}
|
|
|
|
userLogin(username: string, passwd: string): ExitCode {
|
|
const user: User | undefined =
|
|
this._passwd.find((user) => user.username === username);
|
|
|
|
if(user && user.passwd === passwd) {
|
|
this._initNewUserSession(user);
|
|
return ExitCode.SUCCESS;
|
|
}
|
|
return ExitCode.ERROR;
|
|
}
|
|
|
|
formatBytes(bytes: number, dPoint?: number, pow: 1024 | 1000 = 1024): string {
|
|
if (!+bytes) return '0';
|
|
|
|
const k: number = pow;
|
|
const dp: number = dPoint ? (dPoint < 0 ? 0 : dPoint) : 1;
|
|
const units: string[] = ['', 'K', 'M', 'G', 'T', 'P'];
|
|
|
|
const i: number = Math.floor(Math.log(bytes) / Math.log(k));
|
|
|
|
return `${(bytes / Math.pow(k, i)).toFixed(dp)}${units[i]}`;
|
|
}
|
|
|
|
getGroupByName(name: string): Group {
|
|
const out: Group | undefined = this._group.find((group) => group.groupname === name);
|
|
|
|
if (out) return out;
|
|
else throw new Error(`Cannot find a user group named ${name}`);
|
|
}
|
|
|
|
getUserByName(name: string): User {
|
|
const out: User | undefined = this._passwd.find((user) => user.username === name);
|
|
|
|
if (out) return out;
|
|
else throw new Error(`Cannot find a user named ${name}`);
|
|
}
|
|
|
|
getGroupByGid(gid: number): Group {
|
|
const out: Group | undefined = this._group.find((group) => group.gid === gid);
|
|
|
|
if (out) return out;
|
|
else throw new Error(`Cannot find a user group with id of ${gid}`);
|
|
}
|
|
|
|
getUserByUid(uid: number): User {
|
|
const out: User | undefined = this._passwd.find((user) => user.uid === uid);
|
|
|
|
if (out) return out;
|
|
else throw new Error(`Cannot find a user with id of ${uid}`);
|
|
}
|
|
}
|