1614 lines
62 KiB
C
1614 lines
62 KiB
C
// User application entry points for shell_native.
|
|
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <ntlite/syscall.h>
|
|
#include <ntlite/status.h>
|
|
|
|
#define MAX_CMD 256
|
|
#define SHELL_FOREGROUND_POLL_MS 30u
|
|
#define MAX_PROCS 64 // Maximum number of processes we will fetch at once
|
|
#define MAX_THREADS 128
|
|
#define SHELL_PROMPT "> "
|
|
|
|
// Output helpers.
|
|
static void print(const char* s) { SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) }; SysWriteConsole(&req); }
|
|
|
|
// Opens file.
|
|
static uint32_t open_file(const char* path, uint32_t* handleOut) {
|
|
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
|
|
return SysOpenFile(&req);
|
|
}
|
|
|
|
// Creates file.
|
|
static uint32_t create_file(const char* path, uint32_t* handleOut) {
|
|
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
|
|
return SysCreateFile(&req);
|
|
}
|
|
|
|
// Opens dir.
|
|
static uint32_t open_dir(const char* path, uint32_t* handleOut) {
|
|
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
|
|
return SysOpenDir(&req);
|
|
}
|
|
|
|
// Updates cwd.
|
|
static uint32_t set_cwd(const char* path) {
|
|
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
|
|
return SysSetCwd(&req);
|
|
}
|
|
|
|
// Releases file.
|
|
static uint32_t delete_file(const char* path) {
|
|
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
|
|
return SysDeleteFile(&req);
|
|
}
|
|
|
|
// Creates process.
|
|
static uint32_t create_process(const char* path, uint32_t* handleOut, const char* cmdline) {
|
|
SYS_CREATE_PROCESS_REQUEST req = { SysSvFromCstr(path), SysSvFromCstr(cmdline), handleOut };
|
|
return SysCreateProcess(&req);
|
|
}
|
|
|
|
// Prints u Int.
|
|
static void printUInt(uint32_t n) {
|
|
char buf[12]; int i = 0;
|
|
if (n == 0) { print("0"); return; }
|
|
while (n > 0) { buf[i++] = (char)('0' + n % 10); n /= 10; }
|
|
while (i > 0) { char c[2] = { buf[--i], '\0' }; print(c); }
|
|
}
|
|
|
|
// Prints int.
|
|
static void printInt(int32_t n) {
|
|
if (n < 0) { print("-"); printUInt((uint32_t)-(n + 1) + 1); }
|
|
else { printUInt((uint32_t)n); }
|
|
}
|
|
|
|
// Parses an unsigned decimal integer.
|
|
static bool parseUInt(const char* text, uint32_t* out) {
|
|
if (!text || !out) return false;
|
|
while (*text == ' ' || *text == '\t') text++;
|
|
if (*text < '0' || *text > '9') return false;
|
|
|
|
uint32_t value = 0;
|
|
while (*text >= '0' && *text <= '9') {
|
|
uint32_t digit = (uint32_t)(*text - '0');
|
|
if (value > (0xffffffffu - digit) / 10u) return false;
|
|
value = value * 10u + digit;
|
|
text++;
|
|
}
|
|
|
|
while (*text == ' ' || *text == '\t') text++;
|
|
if (*text != '\0') return false;
|
|
*out = value;
|
|
return true;
|
|
}
|
|
|
|
// Prints u Int64.
|
|
static void printUInt64(uint64_t n) {
|
|
if (n == 0) { print("0"); return; }
|
|
char buf[20]; int i = 0;
|
|
while (n > 0) { buf[i++] = (char)('0' + (int)(n % 10)); n /= 10; }
|
|
while (i > 0) { char c[2] = { buf[--i], '\0' }; print(c); }
|
|
}
|
|
|
|
// Implements the u64 Digits operation.
|
|
static uint32_t u64Digits(uint64_t n) {
|
|
uint32_t digits = 1;
|
|
while (n >= 10u) { n /= 10u; digits++; }
|
|
return digits;
|
|
}
|
|
|
|
// Prints spaces.
|
|
static void printSpaces(size_t count) {
|
|
while (count--) print(" ");
|
|
}
|
|
|
|
// Prints u Int Width.
|
|
static void printUIntWidth(uint32_t n, size_t width) {
|
|
uint32_t digits = u64Digits(n);
|
|
if (width > digits) printSpaces(width - digits);
|
|
printUInt(n);
|
|
}
|
|
|
|
// Prints u Int Zero Width.
|
|
static void printUIntZeroWidth(uint32_t n, size_t width) {
|
|
uint32_t digits = u64Digits(n);
|
|
while (width > digits) { print("0"); width--; }
|
|
printUInt(n);
|
|
}
|
|
|
|
// Prints u Int64 Width.
|
|
static void printUInt64Width(uint64_t n, size_t width) {
|
|
uint32_t digits = u64Digits(n);
|
|
if (width > digits) printSpaces(width - digits);
|
|
printUInt64(n);
|
|
}
|
|
|
|
// Names nt-lite process-abort exit statuses.
|
|
static const char* processExitStatusName(uint32_t code) {
|
|
switch (code) {
|
|
case STATUS_PROCESS_KILLED: return "killed";
|
|
case STATUS_PROCESS_PAGE_FAULT: return "page-fault";
|
|
case STATUS_PROCESS_GENERAL_PROTECTION: return "general-protection";
|
|
case STATUS_PROCESS_ILLEGAL_OPCODE: return "illegal-opcode";
|
|
case STATUS_PROCESS_DIVIDE_BY_ZERO: return "divide-by-zero";
|
|
case STATUS_PROCESS_BREAKPOINT: return "breakpoint";
|
|
case STATUS_PROCESS_ALIGNMENT_FAULT: return "alignment-fault";
|
|
case STATUS_PROCESS_UNKNOWN_FAULT: return "unknown-fault";
|
|
default: return NULL;
|
|
}
|
|
}
|
|
|
|
// Prints hex32.
|
|
static void printHex32(uint32_t v) {
|
|
static const char hex[] = "0123456789abcdef";
|
|
char buf[9];
|
|
for (int i = 7; i >= 0; i--) {
|
|
buf[i] = hex[v & 0xFu];
|
|
v >>= 4;
|
|
}
|
|
buf[8] = '\0';
|
|
print(buf);
|
|
}
|
|
|
|
// Prints dir Time.
|
|
static void printDirTime(const SYS_SYSTEM_TIME* t) {
|
|
if (!t || !(t->Flags & SYS_TIME_FLAG_VALID)) {
|
|
print("---- -- -- --:-- ");
|
|
return;
|
|
}
|
|
printUIntZeroWidth(t->Year, 4);
|
|
print("-");
|
|
printUIntZeroWidth(t->Month, 2);
|
|
print("-");
|
|
printUIntZeroWidth(t->Day, 2);
|
|
print(" ");
|
|
printUIntZeroWidth(t->Hour, 2);
|
|
print(":");
|
|
printUIntZeroWidth(t->Minute, 2);
|
|
print(" ");
|
|
}
|
|
|
|
// Prints string Width.
|
|
static void printStringWidth(const char* s, size_t width) {
|
|
size_t len = s ? strlen(s) : 0;
|
|
if (s) print(s);
|
|
if (width > len) printSpaces(width - len);
|
|
}
|
|
|
|
#define SHELL_HISTORY_DEPTH 16
|
|
|
|
static char gHistory[SHELL_HISTORY_DEPTH][MAX_CMD];
|
|
static size_t gHistoryCount;
|
|
|
|
static const char* const gShellBuiltinCommands[] = {
|
|
"cd",
|
|
"clear",
|
|
"crash",
|
|
"date",
|
|
"del",
|
|
"dir",
|
|
"display",
|
|
"exit",
|
|
"gpu",
|
|
"help",
|
|
"kill",
|
|
"mem",
|
|
"ps",
|
|
"stats",
|
|
"telemetry",
|
|
"threads",
|
|
"time",
|
|
"type",
|
|
"write",
|
|
};
|
|
|
|
// Converts a key event into the shell's printable character model.
|
|
static char shellKeyToChar(INPUT_KEY key, uint32_t modifiers) {
|
|
bool shifted = (modifiers & INPUT_MOD_SHIFT) != 0;
|
|
bool caps = (modifiers & INPUT_MOD_CAPS_LOCK) != 0;
|
|
|
|
if (key >= INPUT_KEY_A && key <= INPUT_KEY_Z) {
|
|
char c = (char)('a' + (key - INPUT_KEY_A));
|
|
if (shifted ^ caps) c = (char)(c - 'a' + 'A');
|
|
return c;
|
|
}
|
|
|
|
if (key >= INPUT_KEY_1 && key <= INPUT_KEY_9) {
|
|
static const char shiftedDigits[] = "!@#$%^&*(";
|
|
char normal = (char)('1' + (key - INPUT_KEY_1));
|
|
return shifted ? shiftedDigits[key - INPUT_KEY_1] : normal;
|
|
}
|
|
|
|
switch (key) {
|
|
case INPUT_KEY_0: return shifted ? ')' : '0';
|
|
case INPUT_KEY_MINUS: return shifted ? '_' : '-';
|
|
case INPUT_KEY_EQUAL: return shifted ? '+' : '=';
|
|
case INPUT_KEY_LEFT_BRACKET: return shifted ? '{' : '[';
|
|
case INPUT_KEY_RIGHT_BRACKET: return shifted ? '}' : ']';
|
|
case INPUT_KEY_BACKSLASH: return shifted ? '|' : '\\';
|
|
case INPUT_KEY_SEMICOLON: return shifted ? ':' : ';';
|
|
case INPUT_KEY_APOSTROPHE: return shifted ? '"' : '\'';
|
|
case INPUT_KEY_GRAVE: return shifted ? '~' : '`';
|
|
case INPUT_KEY_COMMA: return shifted ? '<' : ',';
|
|
case INPUT_KEY_PERIOD: return shifted ? '>' : '.';
|
|
case INPUT_KEY_SLASH: return shifted ? '?' : '/';
|
|
case INPUT_KEY_SPACE: return ' ';
|
|
case INPUT_KEY_TAB: return '\t';
|
|
case INPUT_KEY_KP_DIVIDE: return '/';
|
|
case INPUT_KEY_KP_MULTIPLY: return '*';
|
|
case INPUT_KEY_KP_MINUS: return '-';
|
|
case INPUT_KEY_KP_PLUS: return '+';
|
|
case INPUT_KEY_KP_0: return '0';
|
|
case INPUT_KEY_KP_1: return '1';
|
|
case INPUT_KEY_KP_2: return '2';
|
|
case INPUT_KEY_KP_3: return '3';
|
|
case INPUT_KEY_KP_4: return '4';
|
|
case INPUT_KEY_KP_5: return '5';
|
|
case INPUT_KEY_KP_6: return '6';
|
|
case INPUT_KEY_KP_7: return '7';
|
|
case INPUT_KEY_KP_8: return '8';
|
|
case INPUT_KEY_KP_9: return '9';
|
|
case INPUT_KEY_KP_PERIOD: return '.';
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
// Adds a non-empty command to the in-memory history ring.
|
|
static void shellHistoryAdd(const char* cmd) {
|
|
if (!cmd || !*cmd) return;
|
|
if (gHistoryCount > 0 && strcmp(gHistory[gHistoryCount - 1], cmd) == 0) return;
|
|
|
|
if (gHistoryCount == SHELL_HISTORY_DEPTH) {
|
|
memmove(gHistory[0], gHistory[1], (SHELL_HISTORY_DEPTH - 1) * MAX_CMD);
|
|
gHistoryCount--;
|
|
}
|
|
|
|
strncpy(gHistory[gHistoryCount], cmd, MAX_CMD - 1);
|
|
gHistory[gHistoryCount][MAX_CMD - 1] = '\0';
|
|
gHistoryCount++;
|
|
}
|
|
|
|
// Replaces the visible line while keeping the cursor at the end.
|
|
static void shellEditorReplaceLine(char* buf, int len, int* pos, const char* text) {
|
|
int oldPos = *pos;
|
|
for (int i = 0; i < oldPos; i++) print("\b");
|
|
for (int i = 0; i < oldPos; i++) print(" ");
|
|
for (int i = 0; i < oldPos; i++) print("\b");
|
|
|
|
int newPos = 0;
|
|
if (text) {
|
|
while (text[newPos] && newPos < len - 1) {
|
|
buf[newPos] = text[newPos];
|
|
newPos++;
|
|
}
|
|
}
|
|
buf[newPos] = '\0';
|
|
*pos = newPos;
|
|
print(buf);
|
|
}
|
|
|
|
// Returns true when a character separates shell tokens.
|
|
static bool shellIsTokenSeparator(char c) {
|
|
return c == ' ' || c == '\t';
|
|
}
|
|
|
|
// Returns true when a character separates path components.
|
|
static bool shellIsPathSeparator(char c) {
|
|
return c == '/' || c == '\\';
|
|
}
|
|
|
|
// Returns true when the token already names an explicit path.
|
|
static bool shellTokenHasPathSeparator(const char* token, size_t length) {
|
|
for (size_t i = 0; i < length; i++) {
|
|
if (shellIsPathSeparator(token[i])) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Returns true when name starts with prefix.
|
|
static bool shellNameStartsWith(const char* name, const char* prefix, size_t prefixLength) {
|
|
if (!name || !prefix) return false;
|
|
for (size_t i = 0; i < prefixLength; i++) {
|
|
if (name[i] == '\0' || name[i] != prefix[i]) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Extends an in-progress common prefix with one completion candidate.
|
|
static void shellUpdateCommonPrefix(char* common, size_t commonSize, const char* name, uint32_t matchCount) {
|
|
if (!common || commonSize == 0 || !name) return;
|
|
if (matchCount == 0) {
|
|
strncpy(common, name, commonSize - 1);
|
|
common[commonSize - 1] = '\0';
|
|
return;
|
|
}
|
|
|
|
size_t i = 0;
|
|
while (common[i] && name[i] && common[i] == name[i]) i++;
|
|
common[i] = '\0';
|
|
}
|
|
|
|
// Appends text to the edited line and echoes it.
|
|
static bool shellEditorAppend(char* buf, int len, int* pos, const char* text) {
|
|
if (!buf || len <= 0 || !pos || !text) return false;
|
|
|
|
bool changed = false;
|
|
while (*text && *pos < len - 1) {
|
|
buf[*pos] = *text;
|
|
(*pos)++;
|
|
buf[*pos] = '\0';
|
|
char echo[2] = { *text, '\0' };
|
|
print(echo);
|
|
text++;
|
|
changed = true;
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
// Prints one completion candidate.
|
|
static void shellPrintCompletionCandidate(const char* name, bool directory) {
|
|
print(" ");
|
|
print(name);
|
|
if (directory) print("/");
|
|
print("\n");
|
|
}
|
|
|
|
// Tracks completion candidates without storing the full candidate list.
|
|
typedef struct SHELL_COMPLETION_STATE {
|
|
uint32_t MatchCount;
|
|
bool LastIsDirectory;
|
|
bool LastAddsSpace;
|
|
char Common[SYS_DIR_NAME_MAX];
|
|
} SHELL_COMPLETION_STATE;
|
|
|
|
// Records one completion candidate.
|
|
static void shellRecordCompletion(SHELL_COMPLETION_STATE* state, const char* name, bool directory, bool addsSpace) {
|
|
if (!state || !name || !*name) return;
|
|
shellUpdateCommonPrefix(state->Common, sizeof(state->Common), name, state->MatchCount);
|
|
state->MatchCount++;
|
|
state->LastIsDirectory = directory;
|
|
state->LastAddsSpace = addsSpace;
|
|
}
|
|
|
|
// Collects builtin command matches for a command-position token.
|
|
static void shellCollectBuiltinCompletions(const char* prefix, size_t prefixLength,
|
|
SHELL_COMPLETION_STATE* state,
|
|
bool printMatches) {
|
|
size_t count = sizeof(gShellBuiltinCommands) / sizeof(gShellBuiltinCommands[0]);
|
|
for (size_t i = 0; i < count; i++) {
|
|
const char* name = gShellBuiltinCommands[i];
|
|
if (!shellNameStartsWith(name, prefix, prefixLength)) continue;
|
|
if (printMatches) shellPrintCompletionCandidate(name, false);
|
|
else shellRecordCompletion(state, name, false, true);
|
|
}
|
|
}
|
|
|
|
// Splits a token into a directory to scan and a filename prefix to match.
|
|
static bool shellSplitCompletionPath(const char* token, size_t tokenLength,
|
|
char* dir, size_t dirSize,
|
|
const char** prefixOut, size_t* prefixLengthOut) {
|
|
if (!token || !dir || dirSize == 0 || !prefixOut || !prefixLengthOut) return false;
|
|
|
|
size_t lastSeparator = (size_t)-1;
|
|
for (size_t i = 0; i < tokenLength; i++) {
|
|
if (shellIsPathSeparator(token[i])) lastSeparator = i;
|
|
}
|
|
|
|
if (lastSeparator == (size_t)-1) {
|
|
if (dirSize < 2) return false;
|
|
dir[0] = '.';
|
|
dir[1] = '\0';
|
|
*prefixOut = token;
|
|
*prefixLengthOut = tokenLength;
|
|
return true;
|
|
}
|
|
|
|
size_t dirLength = lastSeparator;
|
|
if (lastSeparator == 0) dirLength = 1;
|
|
if (lastSeparator >= 2 && token[1] == ':' && shellIsPathSeparator(token[2])) dirLength = lastSeparator + 1;
|
|
if (dirLength + 1 > dirSize) return false;
|
|
|
|
memcpy(dir, token, dirLength);
|
|
dir[dirLength] = '\0';
|
|
*prefixOut = token + lastSeparator + 1;
|
|
*prefixLengthOut = tokenLength - lastSeparator - 1;
|
|
return true;
|
|
}
|
|
|
|
// Collects filesystem path matches using the existing directory enumeration ABI.
|
|
static void shellCollectPathCompletions(const char* token, size_t tokenLength,
|
|
SHELL_COMPLETION_STATE* state,
|
|
bool printMatches) {
|
|
char dir[MAX_CMD];
|
|
const char* prefix = token;
|
|
size_t prefixLength = tokenLength;
|
|
|
|
if (!shellSplitCompletionPath(token, tokenLength, dir, sizeof(dir), &prefix, &prefixLength)) return;
|
|
|
|
uint32_t hDir = 0;
|
|
if (open_dir(dir, &hDir) != 0) return;
|
|
|
|
SYS_DIR_ENTRY entry;
|
|
while (SysReadDir(hDir, &entry) == 0) {
|
|
if (!entry.Name[0]) continue;
|
|
if (!shellNameStartsWith(entry.Name, prefix, prefixLength)) continue;
|
|
|
|
bool directory = (entry.Attr & 0x10u) != 0;
|
|
if (printMatches) shellPrintCompletionCandidate(entry.Name, directory);
|
|
else shellRecordCompletion(state, entry.Name, directory, true);
|
|
}
|
|
|
|
SysCloseHandle(hDir);
|
|
}
|
|
|
|
// Redraws the prompt and current edit buffer after a completion list.
|
|
static void shellRedrawPromptAndLine(const char* buf) {
|
|
print(SHELL_PROMPT);
|
|
print(buf ? buf : "");
|
|
}
|
|
|
|
// Completes the token at the end of the current line.
|
|
static void shellCompleteLine(char* buf, int len, int* pos) {
|
|
if (!buf || len <= 0 || !pos || *pos < 0) return;
|
|
|
|
size_t cursor = (size_t)*pos;
|
|
size_t tokenStart = cursor;
|
|
while (tokenStart > 0 && !shellIsTokenSeparator(buf[tokenStart - 1])) tokenStart--;
|
|
|
|
const char* token = buf + tokenStart;
|
|
size_t tokenLength = cursor - tokenStart;
|
|
bool commandPosition = tokenStart == 0;
|
|
bool explicitPath = shellTokenHasPathSeparator(token, tokenLength);
|
|
|
|
char completionDir[MAX_CMD];
|
|
const char* namePrefix = token;
|
|
size_t namePrefixLength = tokenLength;
|
|
(void)shellSplitCompletionPath(token, tokenLength, completionDir, sizeof(completionDir), &namePrefix, &namePrefixLength);
|
|
|
|
SHELL_COMPLETION_STATE state;
|
|
memset(&state, 0, sizeof(state));
|
|
|
|
if (commandPosition && !explicitPath) {
|
|
shellCollectBuiltinCompletions(token, tokenLength, &state, false);
|
|
}
|
|
shellCollectPathCompletions(token, tokenLength, &state, false);
|
|
|
|
if (state.MatchCount == 0) return;
|
|
|
|
size_t commonLength = strlen(state.Common);
|
|
bool expanded = commonLength > namePrefixLength;
|
|
if (expanded) {
|
|
shellEditorAppend(buf, len, pos, state.Common + namePrefixLength);
|
|
}
|
|
|
|
if (state.MatchCount == 1) {
|
|
if (state.LastIsDirectory) {
|
|
if (*pos < len - 1 && *pos > 0 && !shellIsPathSeparator(buf[*pos - 1])) shellEditorAppend(buf, len, pos, "/");
|
|
} else if (state.LastAddsSpace) {
|
|
if (*pos < len - 1 && (*pos == 0 || buf[*pos - 1] != ' ')) shellEditorAppend(buf, len, pos, " ");
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (expanded) return;
|
|
|
|
print("\n");
|
|
if (commandPosition && !explicitPath) {
|
|
shellCollectBuiltinCompletions(token, tokenLength, NULL, true);
|
|
}
|
|
shellCollectPathCompletions(token, tokenLength, NULL, true);
|
|
shellRedrawPromptAndLine(buf);
|
|
}
|
|
|
|
// Reads one editable command line from the keyboard.
|
|
static bool readLine(char* buf, int len) {
|
|
int pos = 0;
|
|
size_t historyCursor = gHistoryCount;
|
|
if (len <= 0) return false;
|
|
buf[0] = '\0';
|
|
|
|
while (true) {
|
|
INPUT_KEY_RECORD input;
|
|
if (SysReadInputEvent(&input) != 0) { SysYield(); continue; }
|
|
|
|
INPUT_KEY key = input.Key;
|
|
if (key == INPUT_KEY_NONE) { SysYield(); continue; }
|
|
|
|
uint32_t modifiers = input.Modifiers;
|
|
if ((modifiers & INPUT_MOD_CTRL) && key == INPUT_KEY_C) {
|
|
buf[0] = '\0';
|
|
print("^C\n");
|
|
return true;
|
|
}
|
|
|
|
if (key == INPUT_KEY_ENTER || key == INPUT_KEY_KP_ENTER) {
|
|
print("\n");
|
|
break;
|
|
}
|
|
|
|
if (key == INPUT_KEY_BACKSPACE) {
|
|
if (pos > 0) {
|
|
pos--;
|
|
buf[pos] = '\0';
|
|
print("\b \b");
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (key == INPUT_KEY_UP) {
|
|
if (historyCursor > 0) {
|
|
historyCursor--;
|
|
shellEditorReplaceLine(buf, len, &pos, gHistory[historyCursor]);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (key == INPUT_KEY_DOWN) {
|
|
if (historyCursor < gHistoryCount) {
|
|
historyCursor++;
|
|
shellEditorReplaceLine(buf, len, &pos,
|
|
historyCursor < gHistoryCount ? gHistory[historyCursor] : "");
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (key == INPUT_KEY_TAB) {
|
|
shellCompleteLine(buf, len, &pos);
|
|
continue;
|
|
}
|
|
|
|
char c = input.Char ? input.Char : shellKeyToChar(key, modifiers);
|
|
if (c < 0x20 || c == 127) continue;
|
|
if (pos < len - 1) {
|
|
buf[pos++] = c;
|
|
buf[pos] = '\0';
|
|
char echo[2] = { c, '\0' };
|
|
print(echo);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Implements the trim operation.
|
|
static char* trim(char* s) {
|
|
while (*s == ' ') s++;
|
|
int len = (int)strlen(s);
|
|
while (len > 0 && s[len - 1] == ' ') len--;
|
|
s[len] = '\0';
|
|
return s;
|
|
}
|
|
|
|
// Built-in commands.
|
|
static void cmd_help() {
|
|
print("Available commands:\n"
|
|
" help - Show this list\n"
|
|
" mem - Show memory usage\n"
|
|
" time - Show system wall-clock time\n"
|
|
" display - Show display provider and counters\n"
|
|
" stats - Show scheduler/CPU/MM telemetry\n"
|
|
" ps - List processes\n"
|
|
" threads <pid> - Show one process's thread statuses\n"
|
|
" kill <pid> - Terminate a process\n"
|
|
" dir [path] - List directory contents\n"
|
|
" cd <path> - Change directory\n"
|
|
" type <file> - Display file contents\n"
|
|
" del <file> - Delete a file\n"
|
|
" write <file> <tx> - Create a file with text\n"
|
|
" clear - Clear the screen\n"
|
|
" exit - Close the shell\n");
|
|
}
|
|
|
|
// Prints size Ki B.
|
|
static void printSizeKiB(uint64_t bytes) {
|
|
printUInt64(bytes / 1024u);
|
|
print(" KiB");
|
|
}
|
|
|
|
|
|
// Prints pages Ki B.
|
|
static void printPagesKiB(uint64_t pages) {
|
|
printUInt64(pages * 4u);
|
|
print(" KiB");
|
|
}
|
|
|
|
// Prints cpu Flags.
|
|
static void printCpuFlags(uint32_t flags) {
|
|
if (flags & SYS_CPU_FLAG_BSP) print("bsp ");
|
|
if (flags & SYS_CPU_FLAG_ONLINE) print("online ");
|
|
if (flags & SYS_CPU_FLAG_PARKED) print("parked ");
|
|
if (flags & SYS_CPU_FLAG_PRIMARY) print("primary ");
|
|
if (flags & SYS_CPU_FLAG_STARTED) print("started ");
|
|
if (flags & SYS_CPU_FLAG_PRESENT) print("present ");
|
|
}
|
|
|
|
// Implements the cmd stats operation.
|
|
static void cmd_stats(void) {
|
|
SYS_SCHEDULER_STATS sched;
|
|
SYS_PMM_STATS pmm;
|
|
SYS_CPU_STATS cpu;
|
|
SYS_PFN_STATS pfn;
|
|
SYS_VAD_STATS vad;
|
|
SYS_SWAP_STATS swap;
|
|
SYS_FILE_CACHE_STATS fileCache;
|
|
SYS_FILESYSTEM_STATS filesystem;
|
|
SYS_DISK_STATS disk;
|
|
SYS_PTE_STATS pte;
|
|
SYS_SURFACE_STATS surface;
|
|
uint32_t status;
|
|
|
|
status = SysQueryKernelStats(SYS_KERNEL_STATS_SCHEDULER, &sched, sizeof(sched), NULL);
|
|
if (status != 0) {
|
|
print("stats: scheduler query failed: ");
|
|
printUInt(status);
|
|
print("\n");
|
|
return;
|
|
}
|
|
|
|
status = SysQueryKernelStats(SYS_KERNEL_STATS_PMM, &pmm, sizeof(pmm), NULL);
|
|
if (status != 0) {
|
|
print("stats: pmm query failed: ");
|
|
printUInt(status);
|
|
print("\n");
|
|
return;
|
|
}
|
|
|
|
status = SysQueryKernelStats(SYS_KERNEL_STATS_CPU, &cpu, sizeof(cpu), NULL);
|
|
if (status != 0) {
|
|
print("stats: cpu query failed: ");
|
|
printUInt(status);
|
|
print("\n");
|
|
return;
|
|
}
|
|
|
|
status = SysQueryKernelStats(SYS_KERNEL_STATS_PFN, &pfn, sizeof(pfn), NULL);
|
|
if (status != 0) {
|
|
print("stats: pfn query failed: ");
|
|
printUInt(status);
|
|
print("\n");
|
|
return;
|
|
}
|
|
|
|
status = SysQueryKernelStats(SYS_KERNEL_STATS_VAD, &vad, sizeof(vad), NULL);
|
|
if (status != 0) {
|
|
print("stats: vad query failed: ");
|
|
printUInt(status);
|
|
print("\n");
|
|
return;
|
|
}
|
|
|
|
status = SysQueryKernelStats(SYS_KERNEL_STATS_SWAP, &swap, sizeof(swap), NULL);
|
|
if (status != 0) {
|
|
print("stats: swap query failed: ");
|
|
printUInt(status);
|
|
print("\n");
|
|
return;
|
|
}
|
|
|
|
status = SysQueryKernelStats(SYS_KERNEL_STATS_FILE_CACHE, &fileCache, sizeof(fileCache), NULL);
|
|
if (status != 0) {
|
|
print("stats: file cache query failed: ");
|
|
printUInt(status);
|
|
print("\n");
|
|
return;
|
|
}
|
|
|
|
status = SysQueryKernelStats(SYS_KERNEL_STATS_FILESYSTEM, &filesystem, sizeof(filesystem), NULL);
|
|
if (status != 0) {
|
|
print("stats: filesystem query failed: ");
|
|
printUInt(status);
|
|
print("\n");
|
|
return;
|
|
}
|
|
|
|
status = SysQueryKernelStats(SYS_KERNEL_STATS_DISK, &disk, sizeof(disk), NULL);
|
|
if (status != 0) {
|
|
print("stats: disk query failed: ");
|
|
printUInt(status);
|
|
print("\n");
|
|
return;
|
|
}
|
|
|
|
status = SysQueryKernelStats(SYS_KERNEL_STATS_PTE, &pte, sizeof(pte), NULL);
|
|
if (status != 0) {
|
|
print("stats: pte query failed: ");
|
|
printUInt(status);
|
|
print("\n");
|
|
return;
|
|
}
|
|
|
|
status = SysQueryKernelStats(SYS_KERNEL_STATS_SURFACE, &surface, sizeof(surface), NULL);
|
|
if (status != 0) {
|
|
print("stats: surface query failed: ");
|
|
printUInt(status);
|
|
print("\n");
|
|
return;
|
|
}
|
|
|
|
print("Scheduler:\n");
|
|
print(" ticks: "); printUInt64(sched.Ticks); print("\n");
|
|
print(" context switches: "); printUInt64(sched.ContextSwitches); print("\n");
|
|
print(" quantum expires: "); printUInt64(sched.QuantumExpirations); print("\n");
|
|
print(" priority preempts: "); printUInt64(sched.PriorityPreemptions); print("\n");
|
|
print(" idle selections: "); printUInt64(sched.IdleSelections); print("\n");
|
|
print(" keep-current: "); printUInt64(sched.KeepCurrentDecisions); print("\n");
|
|
print(" demotions: "); printUInt64(sched.Demotions); print("\n");
|
|
print(" aging promotions: "); printUInt64(sched.AgingPromotions); print("\n");
|
|
print(" voluntary blocks: "); printUInt64(sched.VoluntaryBlocks); print("\n");
|
|
print(" SMT deferrals: "); printUInt64(sched.SmtSiblingDeferrals); print("\n");
|
|
print(" SMT primary pokes: "); printUInt64(sched.SmtPrimaryPokes); print("\n");
|
|
|
|
print("CPUs:\n");
|
|
uint32_t n = cpu.CpuCount;
|
|
if (n > SYS_KERNEL_STATS_MAX_CPUS)
|
|
n = SYS_KERNEL_STATS_MAX_CPUS;
|
|
for (uint32_t i = 0; i < n; i++) {
|
|
SYS_CPU_STAT* c = &cpu.Cpu[i];
|
|
print(" cpu"); printUInt(c->CpuIndex);
|
|
print(" lapic="); printUInt(c->LapicId);
|
|
print(" current=");
|
|
if (c->CurrentThreadFlags & SYS_CPU_THREAD_FLAG_IDLE) {
|
|
print("idle/"); printUInt(c->CpuIndex);
|
|
} else {
|
|
print("tid/"); printUInt(c->CurrentTid);
|
|
}
|
|
print(" ticks="); printUInt64(c->SchedulerTicks);
|
|
print(" switches="); printUInt64(c->ContextSwitches);
|
|
print(" flags="); printCpuFlags(c->Flags);
|
|
print("\n");
|
|
}
|
|
|
|
print("Physical memory:\n");
|
|
print(" total: "); printPagesKiB(pmm.TotalFrames); print(" (frames="); printUInt64(pmm.TotalFrames); print(")\n");
|
|
print(" used: "); printPagesKiB(pmm.UsedFrames); print(" (frames="); printUInt64(pmm.UsedFrames); print(")\n");
|
|
print(" free: "); printPagesKiB(pmm.FreeFrames); print(" (frames="); printUInt64(pmm.FreeFrames); print(")\n");
|
|
print(" largest free run: "); printPagesKiB(pmm.LargestFreeRun); print(" (frames="); printUInt64(pmm.LargestFreeRun); print(")\n");
|
|
print(" free runs: "); printUInt64(pmm.FreeRunCount); print("\n");
|
|
print(" available+reclaim: "); printSizeKiB(pfn.AvailableFrames * 4096ull);
|
|
print(" (reclaimable="); printSizeKiB(pfn.ReclaimableFrames * 4096ull); print(")\n");
|
|
print(" run histogram:\n");
|
|
print(" 1: "); printUInt64(pmm.FreeRunHistogram[0]); print("\n");
|
|
print(" 2-3: "); printUInt64(pmm.FreeRunHistogram[1]); print("\n");
|
|
print(" 4-7: "); printUInt64(pmm.FreeRunHistogram[2]); print("\n");
|
|
print(" 8-15: "); printUInt64(pmm.FreeRunHistogram[3]); print("\n");
|
|
print(" 16-63: "); printUInt64(pmm.FreeRunHistogram[4]); print("\n");
|
|
print(" 64-255: "); printUInt64(pmm.FreeRunHistogram[5]); print("\n");
|
|
print(" 256-1023: "); printUInt64(pmm.FreeRunHistogram[6]); print("\n");
|
|
print(" 1024+: "); printUInt64(pmm.FreeRunHistogram[7]); print("\n");
|
|
|
|
print("PFN database:\n");
|
|
print(" states: active="); printUInt64(pfn.ActiveFrames);
|
|
print(" free="); printUInt64(pfn.FreeFrames);
|
|
print(" zeroed="); printUInt64(pfn.ZeroedFrames);
|
|
print(" standby="); printUInt64(pfn.StandbyFrames);
|
|
print(" modified="); printUInt64(pfn.ModifiedFrames);
|
|
print(" reserved="); printUInt64(pfn.ReservedFrames); print("\n");
|
|
print(" eviction: evictable="); printUInt64(pfn.EvictableFrames);
|
|
print(" reclaimable="); printUInt64(pfn.ReclaimableFrames);
|
|
print(" available="); printUInt64(pfn.AvailableFrames);
|
|
print(" mapped="); printUInt64(pfn.MappedFrames);
|
|
print(" non-evictable="); printUInt64(pfn.NonEvictableFrames);
|
|
print(" pinned="); printUInt64(pfn.PinnedFrames); print("\n");
|
|
print(" usage frames: kernel="); printUInt64(pfn.KernelFrames);
|
|
print(" pt="); printUInt64(pfn.PageTableFrames);
|
|
print(" user="); printUInt64(pfn.UserFrames);
|
|
print(" driver="); printUInt64(pfn.DriverFrames);
|
|
print(" dma="); printUInt64(pfn.DmaFrames);
|
|
print(" boot="); printUInt64(pfn.BootloaderFrames);
|
|
print(" pfn-db="); printUInt64(pfn.PfnDatabaseFrames);
|
|
print(" mmio="); printUInt64(pfn.MmioFrames); print("\n");
|
|
print(" buckets: clean-file="); printUInt64(pfn.CleanFileCacheFrames);
|
|
print(" (active="); printUInt64(pfn.ActiveCleanFileCacheFrames);
|
|
print(" standby="); printUInt64(pfn.StandbyCleanFileCacheFrames); print(")");
|
|
print(" image="); printUInt64(pfn.ImageFrames);
|
|
print(" (active="); printUInt64(pfn.ActiveImageFrames);
|
|
print(" standby="); printUInt64(pfn.StandbyImageFrames); print(")");
|
|
print(" mapped-file="); printUInt64(pfn.MappedFileFrames);
|
|
print(" anon="); printUInt64(pfn.AnonymousFrames);
|
|
print(" cow="); printUInt64(pfn.CowFrames);
|
|
print(" surface="); printUInt64(pfn.SurfaceFrames); print("\n");
|
|
print(" operations: alloc="); printUInt64(pfn.AllocationCount);
|
|
print(" free="); printUInt64(pfn.FreeCount);
|
|
print(" retain="); printUInt64(pfn.RetainCount);
|
|
print(" release="); printUInt64(pfn.ReleaseCount);
|
|
print(" pin="); printUInt64(pfn.PinCount);
|
|
print(" unpin="); printUInt64(pfn.UnpinCount); print("\n");
|
|
print(" failures: alloc="); printUInt64(pfn.FailedAllocations);
|
|
print(" contiguous="); printUInt64(pfn.FailedContiguousAllocations); print("\n");
|
|
print(" alloc reasons: invalid-range="); printUInt64(pfn.FailedInvalidRange);
|
|
print(" empty-range="); printUInt64(pfn.FailedEmptyRange);
|
|
print(" reserve-denied="); printUInt64(pfn.FailedReserveDenied);
|
|
print(" no-frame="); printUInt64(pfn.FailedNoFrame); print("\n");
|
|
print(" contig reasons: invalid="); printUInt64(pfn.FailedContiguousInvalidRequest);
|
|
print(" reserve-denied="); printUInt64(pfn.FailedContiguousReserveDenied);
|
|
print(" no-run="); printUInt64(pfn.FailedContiguousNoRun); print("\n");
|
|
print(" reclaim: attempts="); printUInt64(pfn.ReclaimAttempts);
|
|
print(" requested="); printUInt64(pfn.ReclaimPagesRequested);
|
|
print(" succeeded="); printUInt64(pfn.ReclaimPagesSucceeded);
|
|
print(" failed="); printUInt64(pfn.ReclaimPagesFailed);
|
|
print(" reentrant-skips="); printUInt64(pfn.ReclaimReentrantSkips); print("\n");
|
|
|
|
print("VAD faults:\n");
|
|
print(" resolve: calls="); printUInt64(vad.ResolveFaultCalls);
|
|
print(" resolved="); printUInt64(vad.ResolvedFaults);
|
|
print(" failed="); printUInt64(vad.FailedFaults); print("\n");
|
|
print(" failure reasons: protection="); printUInt64(vad.ProtectionFaults);
|
|
print(" no-vad="); printUInt64(vad.NoVadFaults);
|
|
print(" uncommitted="); printUInt64(vad.UncommittedFaults);
|
|
print(" noaccess="); printUInt64(vad.NoAccessFaults); print("\n");
|
|
print(" materialization: demand-zero="); printUInt64(vad.DemandZeroPages);
|
|
print(" zero-maps="); printUInt64(vad.ZeroPageMaps);
|
|
print(" zero-splits="); printUInt64(vad.ZeroPageCowSplits);
|
|
print(" ensure-calls="); printUInt64(vad.EnsureResidentCalls);
|
|
print(" ensure-pages="); printUInt64(vad.EnsureResidentPages);
|
|
print(" already-resident="); printUInt64(vad.AlreadyResidentPages); print("\n");
|
|
print(" cow: faults="); printUInt64(vad.CowFaults);
|
|
print(" copied="); printUInt64(vad.CowPagesCopied);
|
|
print(" views="); printUInt64(vad.CowViewsCreated);
|
|
print(" shared-pages="); printUInt64(vad.CowPagesShared); print("\n");
|
|
print(" file-backed: faults="); printUInt64(vad.FileBackedFaults);
|
|
print(" reads="); printUInt64(vad.FileBackedReads);
|
|
print(" read-fail="); printUInt64(vad.FileBackedReadFailures);
|
|
print(" discards="); printUInt64(vad.FileBackedDiscards); print("\n");
|
|
print(" mapped-file: shared-faults="); printUInt64(vad.FileBackedSharedWritableFaults);
|
|
print(" dirty-faults="); printUInt64(vad.FileBackedDirtyFaults);
|
|
print(" dirty-syncs="); printUInt64(vad.FileBackedDirtySyncs); print("\n");
|
|
print(" image-backed: faults="); printUInt64(vad.ImageBackedFaults);
|
|
print(" zero-faults="); printUInt64(vad.ImageBackedZeroFaults);
|
|
print(" private-faults="); printUInt64(vad.ImageBackedPrivateFaults); print("\n");
|
|
print(" image fixups: calls="); printUInt64(vad.ImageFixupPrivateCalls);
|
|
print(" private-pages="); printUInt64(vad.ImageFixupPrivatePages); print("\n");
|
|
print(" swap-backed: page-outs="); printUInt64(vad.SwapPageOuts);
|
|
print(" page-ins="); printUInt64(vad.SwapPageIns); print("\n");
|
|
|
|
print("Swap:\n");
|
|
print(" enabled: "); printUInt64(swap.Enabled); print("\n");
|
|
print(" slots: total="); printUInt64(swap.TotalSlots);
|
|
print(" used="); printUInt64(swap.UsedSlots);
|
|
print(" free="); printUInt64(swap.FreeSlots);
|
|
print(" high-water="); printUInt64(swap.HighWaterSlots); print("\n");
|
|
print(" io: out="); printUInt64(swap.PageOuts);
|
|
print(" in="); printUInt64(swap.PageIns);
|
|
print(" out-fail="); printUInt64(swap.PageOutFailures);
|
|
print(" in-fail="); printUInt64(swap.PageInFailures); print("\n");
|
|
print(" slots ops: alloc="); printUInt64(swap.SlotAllocations);
|
|
print(" free="); printUInt64(swap.SlotFrees);
|
|
print(" alloc-fail="); printUInt64(swap.SlotAllocationFailures); print("\n");
|
|
|
|
print("File cache:\n");
|
|
print(" lookups: total="); printUInt64(fileCache.Lookups);
|
|
print(" hits="); printUInt64(fileCache.Hits);
|
|
print(" misses="); printUInt64(fileCache.Misses); print("\n");
|
|
print(" loads: ok="); printUInt64(fileCache.Loads);
|
|
print(" fail="); printUInt64(fileCache.LoadFailures);
|
|
print(" races="); printUInt64(fileCache.RaceDiscards);
|
|
print(" waits="); printUInt64(fileCache.InflightWaits); print("\n");
|
|
print(" lifetime: resident="); printUInt64(fileCache.ResidentPages);
|
|
print(" active="); printUInt64(fileCache.ActivePages);
|
|
print(" standby="); printUInt64(fileCache.StandbyPages);
|
|
print(" max="); printUInt64(fileCache.MaxResidentPages);
|
|
print(" invalidations="); printUInt64(fileCache.Invalidations); print("\n");
|
|
print(" dirty/writeback: dirty="); printUInt64(fileCache.DirtyPages);
|
|
print(" max-dirty="); printUInt64(fileCache.MaxDirtyPages);
|
|
print(" marks="); printUInt64(fileCache.DirtyMarks);
|
|
print(" wb-req="); printUInt64(fileCache.WritebackRequests);
|
|
print(" wb-pages="); printUInt64(fileCache.WritebackPages);
|
|
print(" wb-bytes="); printUInt64(fileCache.WritebackBytes);
|
|
print(" wb-fail="); printUInt64(fileCache.WritebackFailures); print("\n");
|
|
print(" mapped dirty: syncs="); printUInt64(fileCache.MappedDirtySyncs);
|
|
print(" pages="); printUInt64(fileCache.MappedDirtyPages); print("\n");
|
|
print(" flushes: calls="); printUInt64(fileCache.FlushFileCalls);
|
|
print(" fail="); printUInt64(fileCache.FlushFileFailures); print("\n");
|
|
print(" reclaim: drops="); printUInt64(fileCache.Drops);
|
|
print(" standby="); printUInt64(fileCache.StandbyTransitions);
|
|
print(" standby-hits="); printUInt64(fileCache.StandbyHits);
|
|
print(" drop-fail="); printUInt64(fileCache.DropFailures); print("\n");
|
|
print(" read-ahead: requests="); printUInt64(fileCache.ReadAheadRequests);
|
|
print(" requested="); printUInt64(fileCache.ReadAheadPagesRequested);
|
|
print(" queued="); printUInt64(fileCache.ReadAheadPagesQueued);
|
|
print(" loaded="); printUInt64(fileCache.ReadAheadPagesLoaded);
|
|
print(" skipped="); printUInt64(fileCache.ReadAheadPagesSkipped);
|
|
print(" fail="); printUInt64(fileCache.ReadAheadFailures);
|
|
print(" throttled="); printUInt64(fileCache.ReadAheadPagesThrottled); print("\n");
|
|
print(" read-ahead max: pages/request="); printUInt64(fileCache.MaxReadAheadPagesPerRequest); print("\n");
|
|
print(" batch fills: reads="); printUInt64(fileCache.BatchReads);
|
|
print(" requested="); printUInt64(fileCache.BatchPagesRequested);
|
|
print(" loaded="); printUInt64(fileCache.BatchPagesLoaded);
|
|
print(" max-request="); printUInt64(fileCache.MaxBatchPagesRequested);
|
|
print(" max-loaded="); printUInt64(fileCache.MaxBatchPagesLoaded); print("\n");
|
|
|
|
print("Filesystem:\n");
|
|
print(" path cache: lookups="); printUInt64(filesystem.PathLookups);
|
|
print(" hits="); printUInt64(filesystem.PathCacheHits);
|
|
print(" misses="); printUInt64(filesystem.PathCacheMisses);
|
|
print(" stores="); printUInt64(filesystem.PathCacheStores); print("\n");
|
|
print(" invalidation: count="); printUInt64(filesystem.PathCacheInvalidations);
|
|
print(" generation="); printUInt64(filesystem.PathCacheGeneration); print("\n");
|
|
print(" directory search: searches="); printUInt64(filesystem.DirectorySearches);
|
|
print(" clusters="); printUInt64(filesystem.DirectoryClustersRead);
|
|
print(" entries="); printUInt64(filesystem.DirectoryEntriesScanned); print("\n");
|
|
print(" FAT chain cache: lookups="); printUInt64(filesystem.FatChainCacheLookups);
|
|
print(" hits="); printUInt64(filesystem.FatChainCacheHits);
|
|
print(" misses="); printUInt64(filesystem.FatChainCacheMisses);
|
|
print(" stores="); printUInt64(filesystem.FatChainCacheStores); print("\n");
|
|
print(" FAT chain work: invalidations="); printUInt64(filesystem.FatChainCacheInvalidations);
|
|
print(" fat-steps="); printUInt64(filesystem.FatChainWalkSteps); print("\n");
|
|
print(" data read spans: runs="); printUInt64(filesystem.DataReadRuns);
|
|
print(" sectors="); printUInt64(filesystem.DataReadSectors);
|
|
print(" merges="); printUInt64(filesystem.DataReadRunMerges);
|
|
print(" max-sectors="); printUInt64(filesystem.MaxDataReadSpanSectors); print("\n");
|
|
|
|
print("Disk I/O:\n");
|
|
print(" devices: registered="); printUInt64(disk.RegisteredDisks); print("\n");
|
|
print(" reads: requests="); printUInt64(disk.ReadRequests);
|
|
print(" sectors="); printUInt64(disk.ReadSectors);
|
|
print(" max-sectors="); printUInt64(disk.MaxReadSectors); print("\n");
|
|
print(" writes: requests="); printUInt64(disk.WriteRequests);
|
|
print(" sectors="); printUInt64(disk.WriteSectors);
|
|
print(" max-sectors="); printUInt64(disk.MaxWriteSectors); print("\n");
|
|
print(" read-ahead: hits="); printUInt64(disk.ReadaheadHits);
|
|
print(" misses="); printUInt64(disk.ReadaheadMisses);
|
|
print(" stores="); printUInt64(disk.ReadaheadStores);
|
|
print(" sectors="); printUInt64(disk.ReadaheadSectorsLoaded);
|
|
print(" max-sectors="); printUInt64(disk.MaxReadaheadSectors); print("\n");
|
|
print(" read-ahead misc: served-bytes="); printUInt64(disk.ReadaheadBytesServed);
|
|
print(" seq="); printUInt64(disk.ReadaheadSequentialDetections);
|
|
print(" invalidations="); printUInt64(disk.ReadaheadInvalidations);
|
|
print(" suppressed="); printUInt64(disk.ReadaheadSuppressed);
|
|
print(" alloc-fail="); printUInt64(disk.ReadaheadAllocFailures);
|
|
print(" read-fail="); printUInt64(disk.ReadaheadReadFailures); print("\n");
|
|
|
|
print("Surfaces:\n");
|
|
print(" lifetime: created="); printUInt64(surface.CreatedSurfaces);
|
|
print(" destroyed="); printUInt64(surface.DestroyedSurfaces);
|
|
print(" live="); printUInt64(surface.LiveSurfaces); print("\n");
|
|
print(" live memory: pages="); printUInt64(surface.LivePages);
|
|
print(" bytes="); printUInt64(surface.LiveBytes); print("\n");
|
|
print(" maps/presents: maps="); printUInt64(surface.MapCalls);
|
|
print(" presents="); printUInt64(surface.PresentCalls);
|
|
print(" input-rects="); printUInt64(surface.PresentRectInputs); print("\n");
|
|
print(" present work: clipped="); printUInt64(surface.PresentClippedRects);
|
|
print(" empty="); printUInt64(surface.PresentEmptyClips);
|
|
print(" rows="); printUInt64(surface.PresentRowsCopied);
|
|
print(" bytes="); printUInt64(surface.PresentBytesCopied); print("\n");
|
|
|
|
print("Page tables:\n");
|
|
print(" leaf mappings: "); printUInt64(pte.CurrentLeafMappings); print("\n");
|
|
print(" page-table pages: "); printUInt64(pte.PageTablePagesAllocated); print("\n");
|
|
print(" map/unmap: map="); printUInt64(pte.MapCalls);
|
|
print(" failed="); printUInt64(pte.MapFailures);
|
|
print(" unmap="); printUInt64(pte.UnmapCalls);
|
|
print(" failed="); printUInt64(pte.UnmapFailures); print("\n");
|
|
print(" protect: calls="); printUInt64(pte.ProtectCalls);
|
|
print(" failed="); printUInt64(pte.ProtectFailures); print("\n");
|
|
print(" query: total="); printUInt64(pte.QueryCalls);
|
|
print(" failed="); printUInt64(pte.QueryFailures);
|
|
print(" translate="); printUInt64(pte.TranslateCalls); print("\n");
|
|
print(" generic: "); printUInt64(pte.QueryByPurpose[0]);
|
|
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[0]); print("\n");
|
|
print(" user-probe: "); printUInt64(pte.QueryByPurpose[1]);
|
|
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[1]); print("\n");
|
|
print(" heap: "); printUInt64(pte.QueryByPurpose[2]);
|
|
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[2]); print("\n");
|
|
print(" vad: "); printUInt64(pte.QueryByPurpose[3]);
|
|
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[3]); print("\n");
|
|
print(" debug: "); printUInt64(pte.QueryByPurpose[4]);
|
|
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[4]); print("\n");
|
|
print(" self-test: "); printUInt64(pte.QueryByPurpose[5]);
|
|
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[5]); print("\n");
|
|
print(" tlb: local="); printUInt64(pte.LocalFlushes);
|
|
print(" global="); printUInt64(pte.GlobalFlushes);
|
|
print(" new-map-skip="); printUInt64(pte.NewMappingNoFlushes);
|
|
print(" replace-flush="); printUInt64(pte.ReplacedMappingFlushes); print("\n");
|
|
print(" shootdowns: requests="); printUInt64(pte.ShootdownRequests);
|
|
print(" targets="); printUInt64(pte.ShootdownTargetCpus);
|
|
print(" skipped="); printUInt64(pte.ShootdownSkippedNoTargets);
|
|
print(" acks="); printUInt64(pte.ShootdownAcks);
|
|
print(" timeouts="); printUInt64(pte.ShootdownTimeouts); print("\n");
|
|
}
|
|
|
|
|
|
// Implements the cmd time operation.
|
|
static void cmd_time(void) {
|
|
SYS_SYSTEM_TIME t;
|
|
uint32_t status = SysGetSystemTime(&t);
|
|
if (status != 0) {
|
|
print("time: failed: ");
|
|
printUInt(status);
|
|
print("\n");
|
|
return;
|
|
}
|
|
|
|
printUIntZeroWidth(t.Year, 4); print("-");
|
|
printUIntZeroWidth(t.Month, 2); print("-");
|
|
printUIntZeroWidth(t.Day, 2); print(" ");
|
|
printUIntZeroWidth(t.Hour, 2); print(":");
|
|
printUIntZeroWidth(t.Minute, 2); print(":");
|
|
printUIntZeroWidth(t.Second, 2); print(".");
|
|
printUIntZeroWidth(t.Millisecond, 3);
|
|
print(" epoch="); printUInt64(t.EpochSeconds);
|
|
print(" flags=0x"); printUInt(t.Flags);
|
|
if (t.Flags & SYS_TIME_FLAG_TZ_KNOWN) {
|
|
print(" tz="); printInt(t.TimeZoneMinutes);
|
|
} else {
|
|
print(" tz=unspecified");
|
|
}
|
|
print("\n");
|
|
}
|
|
|
|
// Prints display Caps.
|
|
static void printDisplayCaps(uint32_t caps) {
|
|
bool any = false;
|
|
if (caps & DISPLAY_CAP_FRAMEBUFFER) { print("framebuffer"); any = true; }
|
|
if (caps & DISPLAY_CAP_SOFTWARE_CURSOR) { if (any) print(","); print("software-cursor"); any = true; }
|
|
if (caps & DISPLAY_CAP_HARDWARE_CURSOR) { if (any) print(","); print("hardware-cursor"); any = true; }
|
|
if (caps & DISPLAY_CAP_ACCEL_FILL) { if (any) print(","); print("accel-fill"); any = true; }
|
|
if (caps & DISPLAY_CAP_ACCEL_COPY) { if (any) print(","); print("accel-copy"); any = true; }
|
|
if (caps & DISPLAY_CAP_PRESENT_SYNC) { if (any) print(","); print("present-sync"); any = true; }
|
|
if (caps & DISPLAY_CAP_VBLANK) { if (any) print(","); print("vblank"); any = true; }
|
|
if (caps & DISPLAY_CAP_3D) { if (any) print(","); print("3d"); any = true; }
|
|
if (!any) print("none");
|
|
}
|
|
|
|
// Prints display Stats.
|
|
static void printDisplayStats(const char* title, const DISPLAY_STATS* st) {
|
|
print(title); print(":\n");
|
|
print(" blit: "); printUInt64(st->BlitCount); print("\n");
|
|
print(" fill: "); printUInt64(st->FillRectCount); print("\n");
|
|
print(" copy: "); printUInt64(st->CopyRectCount); print("\n");
|
|
print(" read: "); printUInt64(st->ReadRectCount); print("\n");
|
|
print(" present: "); printUInt64(st->PresentCount); print(" rects="); printUInt64(st->PresentRectCount); print("\n");
|
|
print(" dirty: "); printUInt64(st->DirtyRectCount); print(" all="); printUInt64(st->DirtyAllCount); print("\n");
|
|
print(" bytes: "); printUInt64(st->BytesCopied); print("\n");
|
|
print(" sync: "); printUInt64(st->SyncCount); print("\n");
|
|
print(" cursor: move="); printUInt64(st->CursorMoveCount);
|
|
print(" show="); printUInt64(st->CursorShowCount);
|
|
print(" hide="); printUInt64(st->CursorHideCount); print("\n");
|
|
}
|
|
|
|
// Implements the cmd display operation.
|
|
static void cmd_display(void) {
|
|
SYS_DISPLAY_INFO info;
|
|
uint32_t status = SysQueryDisplayInfo(&info);
|
|
if (status != 0) {
|
|
print("display: query failed: ");
|
|
printUInt(status);
|
|
print("\n");
|
|
return;
|
|
}
|
|
|
|
print("Display:\n");
|
|
print(" primary: "); print(info.PrimaryName[0] ? info.PrimaryName : "?"); print("\n");
|
|
print(" devices: "); printUInt(info.DeviceCount); print("\n");
|
|
print(" mode: "); printUInt(info.Mode.Width); print("x"); printUInt(info.Mode.Height);
|
|
print(" stride="); printUInt(info.Mode.Stride); print(" format="); printUInt((uint32_t)info.Mode.Format); print("\n");
|
|
print(" caps: 0x"); printHex32(info.Caps); print(" ["); printDisplayCaps(info.Caps); print("]\n");
|
|
printDisplayStats("Manager counters", &info.ManagerStats);
|
|
printDisplayStats("Provider counters", &info.DeviceStats);
|
|
}
|
|
|
|
// Implements the cmd mem operation.
|
|
static void cmd_mem(void) {
|
|
SYS_MEMORY_STATUS st;
|
|
if (SysGetMemoryStatus(&st) != 0) {
|
|
print("mem: failed\n");
|
|
return;
|
|
}
|
|
|
|
print("Memory Status:\n");
|
|
print(" Free physical: ");
|
|
printSizeKiB(st.FreePhysicalBytes);
|
|
print("\n Reclaimable: ");
|
|
printSizeKiB(st.ReclaimablePhysicalBytes);
|
|
print("\n Available physical: ");
|
|
printSizeKiB(st.AvailablePhysicalBytes);
|
|
print("\n Pinned physical: ");
|
|
printSizeKiB(st.PinnedPhysicalBytes);
|
|
print("\n Non-evictable: ");
|
|
printSizeKiB(st.NonEvictablePhysicalBytes);
|
|
print("\n User frames: ");
|
|
printSizeKiB(st.UserPhysicalBytes);
|
|
print("\n Page tables: ");
|
|
printSizeKiB(st.PageTablePhysicalBytes);
|
|
print("\n Kernel pool: ");
|
|
printSizeKiB(st.KernelPoolBytes);
|
|
print("\n User heap: ");
|
|
printSizeKiB(st.UserHeapBytes);
|
|
print("\n");
|
|
}
|
|
|
|
// Processes state Name.
|
|
static const char* processStateName(uint32_t state) {
|
|
switch (state) {
|
|
case SYS_PROCESS_STATE_RUNNING: return "run";
|
|
case SYS_PROCESS_STATE_TERMINATING: return "term";
|
|
case SYS_PROCESS_STATE_EXITED: return "exit";
|
|
default: return "?";
|
|
}
|
|
}
|
|
|
|
// Names public thread states.
|
|
static const char* threadStateName(uint32_t state) {
|
|
switch (state) {
|
|
case SYS_THREAD_STATE_READY: return "ready";
|
|
case SYS_THREAD_STATE_RUNNING: return "run";
|
|
case SYS_THREAD_STATE_WAITING: return "wait";
|
|
case SYS_THREAD_STATE_DEAD: return "dead";
|
|
default: return "?";
|
|
}
|
|
}
|
|
|
|
// Names public wait object types.
|
|
static const char* threadWaitName(uint32_t waitType) {
|
|
switch (waitType) {
|
|
case SYS_THREAD_WAIT_NONE: return "-";
|
|
case SYS_THREAD_WAIT_PROCESS: return "process";
|
|
case SYS_THREAD_WAIT_THREAD: return "thread";
|
|
case SYS_THREAD_WAIT_MUTEX: return "mutex";
|
|
case SYS_THREAD_WAIT_SEMAPHORE: return "sem";
|
|
case SYS_THREAD_WAIT_EVENT: return "event";
|
|
case SYS_THREAD_WAIT_QUEUE: return "queue";
|
|
case SYS_THREAD_WAIT_FILE: return "file";
|
|
case SYS_THREAD_WAIT_DIR: return "dir";
|
|
case SYS_THREAD_WAIT_ASYNC_IO: return "async";
|
|
case SYS_THREAD_WAIT_SOCKET: return "socket";
|
|
case SYS_THREAD_WAIT_DEVICE: return "device";
|
|
case SYS_THREAD_WAIT_DRIVER: return "driver";
|
|
case SYS_THREAD_WAIT_USB_DEVICE: return "usbdev";
|
|
case SYS_THREAD_WAIT_USB_PIPE: return "usbpipe";
|
|
case SYS_THREAD_WAIT_FILE_MAPPING: return "mapping";
|
|
case SYS_THREAD_WAIT_SURFACE: return "surface";
|
|
default: return "unknown";
|
|
}
|
|
}
|
|
|
|
// Prints thread status flags.
|
|
static void printThreadFlags(uint32_t flags) {
|
|
char buf[8];
|
|
size_t n = 0;
|
|
if (flags & SYS_THREAD_FLAG_CURRENT) buf[n++] = '*';
|
|
if (flags & SYS_THREAD_FLAG_IDLE) buf[n++] = 'I';
|
|
if (flags & SYS_THREAD_FLAG_KERNEL) buf[n++] = 'K';
|
|
if (flags & SYS_THREAD_FLAG_QUEUED) buf[n++] = 'Q';
|
|
if (flags & SYS_THREAD_FLAG_ALERTABLE_WAIT) buf[n++] = 'A';
|
|
if (flags & SYS_THREAD_FLAG_KERNEL_APC_PENDING) buf[n++] = 'k';
|
|
if (flags & SYS_THREAD_FLAG_USER_APC_PENDING) buf[n++] = 'u';
|
|
if (n == 0) buf[n++] = '-';
|
|
buf[n] = '\0';
|
|
printStringWidth(buf, 7);
|
|
}
|
|
|
|
// Implements the cmd ps operation.
|
|
static void cmd_ps(void) {
|
|
SYS_PROCESS_INFO procs[MAX_PROCS];
|
|
size_t count = 0;
|
|
|
|
uint32_t status = SysGetProcessList(procs, MAX_PROCS, &count);
|
|
if (status != 0) {
|
|
print("ps: failed to retrieve process list\n");
|
|
return;
|
|
}
|
|
|
|
print("Processes (Count: ");
|
|
printUInt((uint32_t)count);
|
|
print("):\n");
|
|
print(" PID State Thr Hnd Mod UserKiB ImageKiB Flags Name\n");
|
|
print(" Flags: S=system I=idle *=current T=terminating X=exited.\n");
|
|
|
|
size_t displayCount = count < MAX_PROCS ? count : MAX_PROCS;
|
|
for (size_t i = 0; i < displayCount; i++) {
|
|
SYS_PROCESS_INFO* p = &procs[i];
|
|
char flags[6];
|
|
size_t flagLen = 0;
|
|
|
|
if (p->Flags & SYS_PROCESS_FLAG_SYSTEM) flags[flagLen++] = 'S';
|
|
if (p->Flags & SYS_PROCESS_FLAG_IDLE) flags[flagLen++] = 'I';
|
|
if (p->Flags & SYS_PROCESS_FLAG_CURRENT) flags[flagLen++] = '*';
|
|
if (p->Flags & SYS_PROCESS_FLAG_TERMINATING) flags[flagLen++] = 'T';
|
|
if (p->Flags & SYS_PROCESS_FLAG_EXITED) flags[flagLen++] = 'X';
|
|
if (flagLen == 0) flags[flagLen++] = '-';
|
|
flags[flagLen] = '\0';
|
|
|
|
print(" ");
|
|
printUIntWidth(p->ProcessId, 3);
|
|
print(" ");
|
|
printStringWidth(processStateName(p->State), 5);
|
|
print(" ");
|
|
printUIntWidth(p->ThreadCount, 3);
|
|
print(" ");
|
|
printUIntWidth(p->HandleCount, 3);
|
|
print(" ");
|
|
printUIntWidth(p->ModuleCount, 3);
|
|
print(" ");
|
|
printUInt64Width((uint64_t)p->UserCommittedBytes / 1024u, 7);
|
|
print(" ");
|
|
printUInt64Width((uint64_t)p->ImageSize / 1024u, 8);
|
|
print(" ");
|
|
printStringWidth(flags, 5);
|
|
print(" ");
|
|
print(p->Name);
|
|
if (p->CurrentDirectory[0]) {
|
|
print(" cwd=");
|
|
print(p->CurrentDirectory);
|
|
}
|
|
print("\n");
|
|
}
|
|
|
|
if (count > MAX_PROCS) {
|
|
print(" ... and ");
|
|
printUInt((uint32_t)(count - MAX_PROCS));
|
|
print(" more processes.\n");
|
|
}
|
|
}
|
|
|
|
// Implements the cmd threads operation.
|
|
static void cmd_threads(const char* args) {
|
|
uint32_t pid = 0;
|
|
if (!parseUInt(args, &pid)) {
|
|
print("threads: usage: threads <pid>\n");
|
|
return;
|
|
}
|
|
|
|
SYS_THREAD_INFO threads[MAX_THREADS];
|
|
size_t count = 0;
|
|
uint32_t status = SysGetThreadList(pid, threads, MAX_THREADS, &count);
|
|
if (status != 0) {
|
|
print("threads: query failed status=");
|
|
printUInt(status);
|
|
print("\n");
|
|
return;
|
|
}
|
|
|
|
print("Threads for PID ");
|
|
printUInt(pid);
|
|
print(" (Count: ");
|
|
printUInt((uint32_t)count);
|
|
print("):\n");
|
|
print(" TID State Pri CPU LastCPU QRem QTick Burst Wait WStat Flags\n");
|
|
print(" Flags: *=current I=idle K=kernel Q=queued A=alertable k=kernel-apc u=user-apc.\n");
|
|
|
|
size_t displayCount = count < MAX_THREADS ? count : MAX_THREADS;
|
|
for (size_t i = 0; i < displayCount; i++) {
|
|
SYS_THREAD_INFO* t = &threads[i];
|
|
print(" ");
|
|
printUIntWidth(t->ThreadId, 3);
|
|
print(" ");
|
|
printStringWidth(threadStateName(t->State), 5);
|
|
print(" ");
|
|
printUIntWidth(t->Priority, 3);
|
|
print(" ");
|
|
if (t->RunningCpu == 0xffffffffu) printStringWidth("-", 3);
|
|
else printUIntWidth(t->RunningCpu, 3);
|
|
print(" ");
|
|
if (t->LastCpu == 0xffffffffu) printStringWidth("-", 7);
|
|
else printUIntWidth(t->LastCpu, 7);
|
|
print(" ");
|
|
printUIntWidth(t->QuantumRemaining, 4);
|
|
print(" ");
|
|
printUIntWidth(t->QuantumTicks, 5);
|
|
print(" ");
|
|
printUIntWidth(t->CpuBurstTicks, 5);
|
|
print(" ");
|
|
printStringWidth(threadWaitName(t->WaitObjectType), 8);
|
|
print(" ");
|
|
printUIntWidth(t->WaitStatus, 5);
|
|
print(" ");
|
|
printThreadFlags(t->Flags);
|
|
print("\n");
|
|
}
|
|
|
|
if (count > MAX_THREADS) {
|
|
print(" ... and ");
|
|
printUInt((uint32_t)(count - MAX_THREADS));
|
|
print(" more threads.\n");
|
|
}
|
|
}
|
|
|
|
static void cmd_kill(const char* args) {
|
|
uint32_t pid = 0;
|
|
if (!parseUInt(args, &pid) || pid == 0) { print("kill: usage: kill <pid>\n"); return; }
|
|
uint32_t status = SysKillProcess(pid, (int)STATUS_PROCESS_KILLED);
|
|
if (status != 0) {
|
|
print("shell: failed to kill process ");
|
|
printUInt(pid);
|
|
print(", error code: ");
|
|
printUInt(status);
|
|
print("\n");
|
|
} else {
|
|
print("Process ");
|
|
printUInt(pid);
|
|
print(" terminated successfully.\n");
|
|
}
|
|
}
|
|
|
|
// Implements the cmd write operation.
|
|
static void cmd_write(const char* args) {
|
|
char filename[MAX_CMD]; int i = 0;
|
|
while (args[i] != ' ' && args[i] != '\0' && i < MAX_CMD - 1) {
|
|
filename[i] = args[i]; i++;
|
|
}
|
|
filename[i] = '\0';
|
|
if (args[i] == '\0' || args[i+1] == '\0') { print("write: usage: write <filename> <text>\n"); return; }
|
|
uint32_t hFile;
|
|
if (create_file(filename, &hFile) != 0) { print("write: could not create file\n"); return; }
|
|
size_t written;
|
|
SYS_FILE_IO_REQUEST writeReq = SysMakeFileIoRequest(hFile, args + i + 1, strlen(args + i + 1), &written);
|
|
SysWriteFile(&writeReq);
|
|
SysCloseHandle(hFile);
|
|
print("File written successfully.\n");
|
|
}
|
|
|
|
// Implements the cmd dir operation.
|
|
static void cmd_dir(const char* path) {
|
|
uint32_t hDir;
|
|
const char* target = (path && *path != '\0') ? path : ".";
|
|
|
|
if (open_dir(target, &hDir) != 0) {
|
|
print("dir: failed to open directory\n");
|
|
return;
|
|
}
|
|
|
|
SYS_DIR_ENTRY ke;
|
|
while (SysReadDir(hDir, &ke) == 0) {
|
|
printDirTime(&ke.LastWriteTime);
|
|
|
|
if (ke.Attr & 0x10) {
|
|
print("<DIR> ");
|
|
print(ke.Name);
|
|
} else {
|
|
print(" ");
|
|
print(ke.Name);
|
|
print(" (");
|
|
printUInt64(ke.FileSize);
|
|
print(" bytes)");
|
|
}
|
|
print("\n");
|
|
}
|
|
SysCloseHandle(hDir);
|
|
}
|
|
|
|
// Implements the cmd type operation.
|
|
static void cmd_type(const char* filename) {
|
|
uint32_t hFile;
|
|
if (open_file(filename, &hFile) != 0) { print("type: cannot open file\n"); return; }
|
|
char buffer[512]; size_t bytesRead;
|
|
for (;;) {
|
|
SYS_FILE_IO_REQUEST readReq = SysMakeFileIoRequest(hFile, buffer, sizeof(buffer) - 1, &bytesRead);
|
|
if (SysReadFile(&readReq) != 0 || bytesRead == 0) break;
|
|
buffer[bytesRead] = '\0'; print(buffer);
|
|
}
|
|
SysCloseHandle(hFile); print("\n");
|
|
}
|
|
|
|
|
|
// Implements the extract Program Path operation.
|
|
static bool extractProgramPath(const char* cmdline, char* out, size_t outSize) {
|
|
if (!cmdline || !out || outSize == 0) return false;
|
|
|
|
while (*cmdline == ' ' || *cmdline == '\t') cmdline++;
|
|
if (*cmdline == '\0') return false;
|
|
|
|
size_t pos = 0;
|
|
if (*cmdline == '"') {
|
|
cmdline++;
|
|
while (*cmdline && *cmdline != '"' && pos + 1 < outSize)
|
|
out[pos++] = *cmdline++;
|
|
} else {
|
|
while (*cmdline && *cmdline != ' ' && *cmdline != '\t' && pos + 1 < outSize)
|
|
out[pos++] = *cmdline++;
|
|
}
|
|
|
|
out[pos] = '\0';
|
|
return pos != 0;
|
|
}
|
|
|
|
// Consumes queued Ctrl-C input without taking ordinary foreground stdin.
|
|
static bool shellConsumeQueuedCtrlC(void) {
|
|
for (int i = 0; i < 4; i++) {
|
|
INPUT_KEY_RECORD input;
|
|
if (SysPeekInputEvent(&input) != 0) return false;
|
|
|
|
if ((input.Modifiers & INPUT_MOD_CTRL) && input.Key == INPUT_KEY_C) {
|
|
(void)SysReadInputEvent(&input);
|
|
return true;
|
|
}
|
|
|
|
if (input.Key == INPUT_KEY_LEFT_CTRL || input.Key == INPUT_KEY_RIGHT_CTRL) {
|
|
(void)SysReadInputEvent(&input);
|
|
continue;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Waits for a foreground process while watching Ctrl-C without stealing ordinary stdin.
|
|
static uint32_t waitForegroundProcess(uint32_t hProc) {
|
|
uint32_t pid = 0;
|
|
bool havePid = SysGetProcessPid(hProc, &pid) == 0 && pid != 0;
|
|
bool sentBreak = false;
|
|
|
|
uint32_t inputQueue = 0;
|
|
if (SysOpenInputQueue(0, &inputQueue) != 0)
|
|
return SysWait(hProc, SYS_WAIT_FLAG_NONE);
|
|
|
|
uint32_t handles[2] = { hProc, inputQueue };
|
|
uint32_t index = 0;
|
|
SYS_WAIT_MANY_REQUEST waitReq;
|
|
waitReq.Size = sizeof(waitReq);
|
|
waitReq.Flags = SYS_WAIT_FLAG_NONE;
|
|
waitReq.Handles = handles;
|
|
waitReq.Count = 2;
|
|
waitReq.SignaledIndexOut = &index;
|
|
waitReq.TimeoutMilliseconds = SYS_WAIT_TIMEOUT_INFINITE;
|
|
|
|
for (;;) {
|
|
index = 0;
|
|
uint32_t status = SysWaitMany(&waitReq);
|
|
if (status != SYS_STATUS_SUCCESS) {
|
|
SysCloseHandle(inputQueue);
|
|
return status;
|
|
}
|
|
|
|
if (index == 0) {
|
|
SysCloseHandle(inputQueue);
|
|
return status;
|
|
}
|
|
|
|
uint32_t modifiers = SysGetKeyModifiers();
|
|
if (((modifiers & INPUT_MOD_CTRL) && SysIsKeyDown(INPUT_KEY_C)) || shellConsumeQueuedCtrlC()) {
|
|
if (!sentBreak) {
|
|
print("^C\n");
|
|
if (havePid) (void)SysKillProcess(pid, (int)STATUS_PROCESS_KILLED);
|
|
sentBreak = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Run one command.
|
|
static int runCommand(const char* cmd) {
|
|
if (!cmd || *cmd == '\0') return 0;
|
|
if (strcmp(cmd, "exit") == 0) return 1;
|
|
if (strcmp(cmd, "help") == 0) { cmd_help(); return 0; }
|
|
if (strcmp(cmd, "clear") == 0) { SysClearScreen(0x012456); return 0; }
|
|
if (strncmp(cmd, "crash", 5) == 0) {
|
|
// Check the sub-option trailing "crash " (offset 6).
|
|
if (strcmp(cmd + 6, "void") == 0) {
|
|
// Jumping into the void.
|
|
void (*invalid_func)(void) = NULL;
|
|
invalid_func();
|
|
return 0;
|
|
}
|
|
else if (strcmp(cmd + 6, "noncanonical") == 0) {
|
|
// Non-canonical pointer dereference.
|
|
volatile int *ptr = (volatile int*)0x4242424242424242ULL;
|
|
*ptr = 1337;
|
|
return 0;
|
|
}
|
|
else if (strcmp(cmd + 6, "ud2") == 0) {
|
|
__ud2();
|
|
return 0;
|
|
}
|
|
else if (strcmp(cmd + 6, "nx") == 0) {
|
|
// NX / DEP Violation.
|
|
unsigned char code[] = { 0xC3 };
|
|
void (*execute_stack)(void) = (void (*)(void))code;
|
|
execute_stack();
|
|
return 0;
|
|
}
|
|
else if (strcmp(cmd + 6, "divzero") == 0) {
|
|
// Division by zero.
|
|
volatile int zero = 0;
|
|
volatile int result = 1 / zero;
|
|
(void)result;
|
|
return 0;
|
|
}
|
|
else {
|
|
// Default fallback if just "crash" or an unknown sub-option is passed.
|
|
*(volatile int*)0 = 42;
|
|
return 0;
|
|
}
|
|
}
|
|
if (strcmp(cmd, "mem") == 0) { cmd_mem(); return 0; }
|
|
if (strcmp(cmd, "time") == 0 || strcmp(cmd, "date") == 0) { cmd_time(); return 0; }
|
|
if (strcmp(cmd, "display") == 0 || strcmp(cmd, "gpu") == 0) { cmd_display(); return 0; }
|
|
if (strcmp(cmd, "stats") == 0 || strcmp(cmd, "telemetry") == 0) { cmd_stats(); return 0; }
|
|
if (strcmp(cmd, "ps") == 0) { cmd_ps(); return 0; }
|
|
if (strncmp(cmd, "threads ", 8) == 0) { cmd_threads(cmd + 8); return 0; }
|
|
if (strncmp(cmd, "kill ", 5) == 0) { cmd_kill(cmd + 5); return 0; }
|
|
if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return 0; }
|
|
if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return 0; }
|
|
if (strncmp(cmd, "cd ", 3) == 0) { if (set_cwd(cmd + 3) != 0) print("cd: failed\n"); return 0; }
|
|
if (strncmp(cmd, "del ", 4) == 0) { delete_file(cmd + 4); return 0; }
|
|
if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return 0; }
|
|
if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return 0; }
|
|
|
|
// Make a local copy of the command line so we can mutate it safely if needed
|
|
char modifiedCmd[MAX_CMD];
|
|
strncpy(modifiedCmd, cmd, sizeof(modifiedCmd) - 1);
|
|
modifiedCmd[sizeof(modifiedCmd) - 1] = '\0';
|
|
|
|
// Parse background indicator execution flag (&)
|
|
bool background = false;
|
|
size_t cmdLen = strlen(modifiedCmd);
|
|
if (cmdLen > 0 && modifiedCmd[cmdLen - 1] == '&') {
|
|
background = true;
|
|
modifiedCmd[cmdLen - 1] = '\0'; // Strip the ampersand
|
|
|
|
// Clean up any trailing whitespace left before the ampersand
|
|
cmdLen = strlen(modifiedCmd);
|
|
while (cmdLen > 0 && (modifiedCmd[cmdLen - 1] == ' ' || modifiedCmd[cmdLen - 1] == '\t')) {
|
|
modifiedCmd[cmdLen - 1] = '\0';
|
|
cmdLen--;
|
|
}
|
|
}
|
|
|
|
char program[MAX_CMD];
|
|
if (!extractProgramPath(modifiedCmd, program, sizeof(program))) return 0;
|
|
|
|
uint32_t hProc;
|
|
if (create_process(program, &hProc, modifiedCmd) == 0) {
|
|
if (background) {
|
|
// Background mode: Clean up the handle immediately and skip waiting.
|
|
print("[Launched in background, handle: ");
|
|
printUInt(hProc);
|
|
print("]\n");
|
|
SysCloseHandle(hProc);
|
|
} else {
|
|
// Foreground mode: Wait for completion and flush standard keyboard stream.
|
|
uint32_t waitStatus = waitForegroundProcess(hProc);
|
|
if (waitStatus != SYS_STATUS_SUCCESS) {
|
|
print("shell: wait failed: ");
|
|
printUInt(waitStatus);
|
|
print("\n");
|
|
}
|
|
print("Process exited with code: ");
|
|
int exitCode;
|
|
SysGetProcessExitCode(hProc, &exitCode);
|
|
printInt(exitCode);
|
|
const char* exitName = processExitStatusName((uint32_t)exitCode);
|
|
if (exitName) {
|
|
print(" (0x");
|
|
printHex32((uint32_t)exitCode);
|
|
print(" ");
|
|
print(exitName);
|
|
print(")");
|
|
}
|
|
print("\n");
|
|
SysCloseHandle(hProc);
|
|
while (SysPollKeyboard()) (void)SysReadKey();
|
|
}
|
|
} else {
|
|
print("shell: launch failed\n");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Runs the requested operation.
|
|
int main(void) {
|
|
const char* cmdline = SysGetPeb()->CommandLine.Data;
|
|
char lineBuf[MAX_CMD];
|
|
|
|
print("shell: command line: ");
|
|
print(cmdline);
|
|
print("\n");
|
|
print("shell: ready\n");
|
|
while (true) {
|
|
print(SHELL_PROMPT);
|
|
if (readLine(lineBuf, MAX_CMD)) continue;
|
|
char* cmd = trim(lineBuf);
|
|
shellHistoryAdd(cmd);
|
|
if (runCommand(cmd)) break;
|
|
}
|
|
print("shell: goodbye\n");
|
|
SysExitProcess(0);
|
|
return 0;
|
|
}
|