feat: unify system status codes

This commit is contained in:
daniel committed 2026-09-30 09:43:18 +02:00
1 parent 93894b8fe5
commit 8a3ffb2be1
46 files changed
+1499 -489

No files matched your search

+23
View File
@@ -2,8 +2,31 @@
All syscalls are available through `#include <montauk/syscall.h>` in the `montauk` namespace. The syscall layer provides `syscall0()` through `syscall6()` primitives using AMD64 calling conventions. All raw syscalls return `int64_t`.
## Status Codes
| Code | Value | Meaning |
|------|-------|---------|
| `Success` | 0 | The call succeeded |
| `GenericFailure` | -1 | Unspecified failure |
| `PermissionDenied` | -2 | The object belongs to another process or the handle lacks the right |
| `CapabilityDenied` | -3 | The caller lacks (or was refused consent for) a required capability |
| `SystemIntegrityError` | -4 | The path is protected (ProtectedPaths) and the caller may not modify it |
| `BadPointer` | -5 | A user pointer or buffer range is invalid |
| `InvalidArgument` | -6 | An argument, id, or handle is invalid |
| `UnsupportedOp` | -7 | The operation is not supported here |
| `NoDevice` | -8 | The required device is absent or not initialized |
| `IoFailure` | -9 | Device I/O failed |
| `BadInvocation` | -10 | Unknown syscall number |
| `NoSuchPath` | -11 | The path does not exist |
| `Busy` | -12 | The resource is in use or its queue is full |
| `BufferTooSmall` | -13 | The output buffer is too small; retry with a larger one |
| `OutOfMemory` | -14 | Memory could not be allocated |
The Wi-Fi (`WIFI_ERR_*`) and USB (`USB_ERR_*`) syscalls keep their own error codes.
## Table of Contents
- [Status Codes](#status-codes)
- [Process Management](#process-management)
- [File System](#file-system)
- [Memory](#memory)
+168
View File
@@ -7,6 +7,7 @@
#pragma once
#include <cstdint>
#include <cstddef>
#include <montauk/statuscodes.h>
namespace montauk::abi {
@@ -250,6 +251,122 @@ namespace montauk::abi {
static constexpr uint64_t SYS_LOG_WRITE = 176; // (logMessage) -> 0
static constexpr uint64_t SYS_TERMINAL_ATTACHED = 177; // () -> 1 when connected to a userspace terminal
static constexpr uint64_t SYS_SPAWN_CAPS = 185;
static constexpr uint64_t SYS_SPAWN_REDIR_CAPS = 186;
// As SYS_ALLOC, but commits every page up front instead of faulting them
// in one at a time. For buffers the caller is about to touch in full.
static constexpr uint64_t SYS_ALLOC_EAGER = 187; // (bytes) -> va, 0 on failure
/* Battery.hpp -- ACPI battery status */
static constexpr uint64_t SYS_BATTERYINFO = 188; // (BatteryInfo*) -> 0, -1 no battery device
// The caller's own capability masks.
static constexpr uint64_t SYS_GETCAPS = 189; // (SpawnCapabilities*) -> 0, -1 bad pointer
/* Storage.hpp */
static constexpr uint64_t SYS_VOLINFO = 190; // (VolInfo*, maxCount) -> count, -1 bad pointer
/* Consent.hpp */
static constexpr uint64_t SYS_CONSENT_REGISTER = 191; // () -> 0, -13 lacks CAP_COMPOSITOR, -1 another agent is live
static constexpr uint64_t SYS_CONSENT_NEXT = 192; // (ConsentRequest*) -> 1 delivered, 0 none queued
static constexpr uint64_t SYS_CONSENT_REPLY = 193; // (requestId, CONSENT_ALLOW/DENY) -> 0, -1 stale id
/* Kernel-owned process capabilities. User identities may namespace
per-user resources, but never participate in authorization decisions. */
static constexpr uint64_t CAP_PROCESS_ADMIN = 1ULL << 0;
static constexpr uint64_t CAP_POWER_REQUEST = 1ULL << 1;
static constexpr uint64_t CAP_POWER_CONTROL = 1ULL << 2;
static constexpr uint64_t CAP_SUSPEND = 1ULL << 3;
static constexpr uint64_t CAP_STORAGE_ADMIN = 1ULL << 4;
static constexpr uint64_t CAP_RAW_STORAGE = 1ULL << 5;
static constexpr uint64_t CAP_NETWORK_ADMIN = 1ULL << 6;
static constexpr uint64_t CAP_SET_TIME = 1ULL << 7;
static constexpr uint64_t CAP_USER_ADMIN = 1ULL << 8;
static constexpr uint64_t CAP_DISPLAY_ADMIN = 1ULL << 9;
static constexpr uint64_t CAP_DEVICE_ADMIN = 1ULL << 10;
static constexpr uint64_t CAP_LOG_READ = 1ULL << 11;
/* Write to the program images the system boots and runs (0:/os,
0:/apps). Deliberately separate from CAP_STORAGE_ADMIN: grants are
keyed on binary path, so writing an image is equivalent to acquiring
whatever that image is granted at its next launch. Formatting a data
volume must not carry that authority with it. */
static constexpr uint64_t CAP_SYSTEM_IMAGE = 1ULL << 12;
/* Map other processes' windows and deliver input events to them
(SYS_WINMAP, SYS_WINSENDEVENT): the desktop compositor's authority.
Anything holding it can read every window's pixels and click on any
window, including a consent prompt it is itself waiting on, so it is
never part of a session mask, never part of "all", and never
delegated by the compositor to what it launches. */
static constexpr uint64_t CAP_COMPOSITOR = 1ULL << 13;
static constexpr uint64_t CAP_ALL = (1ULL << 14) - 1;
static constexpr uint64_t CAP_STANDARD_SESSION = CAP_POWER_REQUEST | CAP_SUSPEND;
static constexpr uint64_t CAP_ADMIN_SESSION =
CAP_STANDARD_SESSION | CAP_PROCESS_ADMIN | CAP_STORAGE_ADMIN |
CAP_RAW_STORAGE | CAP_NETWORK_ADMIN | CAP_SET_TIME | CAP_USER_ADMIN |
CAP_DISPLAY_ADMIN | CAP_DEVICE_ADMIN | CAP_LOG_READ;
static_assert((CAP_STANDARD_SESSION & ~CAP_ADMIN_SESSION) == 0);
static_assert((CAP_ADMIN_SESSION & CAP_POWER_CONTROL) == 0,
"final power control belongs only to the session supervisor");
static_assert((CAP_ADMIN_SESSION & CAP_SYSTEM_IMAGE) == 0,
"an admin session must not imply authority to rewrite the "
"programs it launches; grant CAP_SYSTEM_IMAGE per binary");
static_assert((CAP_ADMIN_SESSION & CAP_COMPOSITOR) == 0,
"a session must not delegate the compositor's authority; "
"login grants it to the desktop directly");
// Capability denial for the Wi-Fi and USB syscalls only, which keep their
// own WIFI_ERR_* / USB_ERR_* spaces; everything else returns
// CapabilityDenied from StatusCodes.
static constexpr int SYS_ERR_PERMISSION = -13;
// A process holds two independent sets: what it may use (effective) and
// what it may pass to children (delegable). A launcher can delegate what
// it never uses itself, so neither set bounds the other.
struct SpawnCapabilities {
uint64_t effective;
uint64_t delegable;
};
// Both of the child's sets must come out of the parent's delegable set.
constexpr bool ValidCapabilityDelegation(const SpawnCapabilities& child,
uint64_t parentDelegable) {
return ((child.effective | child.delegable) & ~CAP_ALL) == 0 &&
(child.effective & ~parentDelegable) == 0 &&
(child.delegable & ~parentDelegable) == 0;
}
static_assert(ValidCapabilityDelegation(
{CAP_NETWORK_ADMIN, 0}, CAP_NETWORK_ADMIN));
static_assert(ValidCapabilityDelegation(
{0, CAP_NETWORK_ADMIN}, CAP_NETWORK_ADMIN));
static_assert(!ValidCapabilityDelegation(
{CAP_NETWORK_ADMIN, CAP_NETWORK_ADMIN}, 0));
static_assert(!ValidCapabilityDelegation(
{CAP_NETWORK_ADMIN | CAP_SET_TIME, 0}, CAP_NETWORK_ADMIN));
static_assert(!ValidCapabilityDelegation(
{0, CAP_NETWORK_ADMIN | CAP_SET_TIME}, CAP_NETWORK_ADMIN));
/* Capabilities a user can grant at first use through a consent prompt.
A consent grant never exceeds what the process's parent could have
delegated to it at spawn (its consent ceiling), is never delegable
onwards, and is issued only by the session's consent agent -- so it is
deferred delegation, not amplification. */
static constexpr uint64_t CAP_CONSENT_ELIGIBLE =
CAP_STORAGE_ADMIN | CAP_NETWORK_ADMIN | CAP_SET_TIME |
CAP_DISPLAY_ADMIN | CAP_LOG_READ;
static_assert((CAP_CONSENT_ELIGIBLE & ~CAP_ADMIN_SESSION) == 0,
"consent can only hand out authority an admin session holds");
static constexpr int CONSENT_DENY = 0;
static constexpr int CONSENT_ALLOW = 1;
// A request left unanswered this long is denied (and not remembered).
static constexpr uint64_t CONSENT_TIMEOUT_MS = 120000;
struct ConsentRequest {
uint64_t id;
uint64_t capability; // exactly one CAP_* bit
int32_t pid;
uint32_t imageProtected; // 1 when writing the image needs CAP_SYSTEM_IMAGE
char imagePath[256];
};
static constexpr int USB_ERR_INVALID = -1;
@@ -264,10 +381,17 @@ namespace montauk::abi {
// Graceful power-off request actions (SYS_POWER_REQUEST). The desktop posts
// a pending action and exits; login.elf reads it, runs the shutdown stages,
// then issues the matching SYS_SHUTDOWN / SYS_RESET.
//
// A request can also be posted from inside the session -- the shell's
// shutdown builtin does. login only looks at it once the session leader
// exits, so the leader has to notice and stand down: POWER_REQ_PEEK is the
// non-destructive read it polls with. Only login consumes (QUERY), so a
// leader that peeks cannot swallow the request it is meant to act on.
enum PowerRequestAction : int {
POWER_REQ_QUERY = 0, // read-and-clear the pending action
POWER_REQ_SHUTDOWN = 1,
POWER_REQ_REBOOT = 2,
POWER_REQ_PEEK = 3, // read the pending action without clearing it
};
static constexpr uint32_t CLIPBOARD_MAX_TEXT_BYTES = 256 * 1024;
@@ -530,6 +654,28 @@ namespace montauk::abi {
char typeName[24]; // human-readable type name
};
// One mounted volume (returned by SYS_VOLINFO). A volume is a single
// mounted filesystem, which is what a drive number names: one physical
// disk with three partitions occupies three drive numbers.
//
// A volume with no backing partition -- the boot ramdisk -- reports
// blockDev and partIndex as -1 and leaves the geometry fields zero.
struct VolInfo {
int32_t drive; // VFS drive number ("N:/")
int32_t blockDev; // backing block device index, -1 if none
int32_t partIndex; // global GPT partition index, -1 if none
uint32_t kind; // BlockDeviceKind: 0=none/ramdisk, 1=SATA, 2=SATAPI, 3=NVMe, 4=USB MSC
uint64_t startLba; // partition start LBA, 0 if none
uint64_t sectorCount; // partition length in sectors, 0 if none
uint32_t sectorSize; // logical sector size in bytes, 0 if none
uint32_t _pad0;
char label[36]; // filesystem volume label, "" if unset
char fsType[12]; // "ext2", "FAT32", "ramdisk", "" if unknown
char partName[72]; // GPT partition name, "" if none
char model[41]; // backing disk model, "" if none
char _pad1[3];
};
struct GptAddParams {
int32_t blockDev;
uint32_t _pad0;
@@ -729,6 +875,8 @@ namespace montauk::abi {
char name[64];
uint64_t heapUsed; // Distance from UserHeapBase to high-water mark
uint64_t cpuTimeMs; // accumulated scheduler runtime
uint64_t effectiveCaps;
uint64_t delegableCaps;
};
struct MemStats {
@@ -738,6 +886,26 @@ namespace montauk::abi {
uint64_t pageSize;
};
// Battery status (returned by SYS_BATTERYINFO). A machine with no battery
// device fails the call outright; `present` distinguishes a laptop with the
// pack removed from one that is running on it.
struct BatteryInfo {
uint8_t present; // a battery is installed
uint8_t charging;
uint8_t discharging;
uint8_t critical;
uint8_t acOnline; // AC adapter connected
uint8_t hasAcAdapter; // an AC adapter device exists at all
uint8_t percent; // 0..100, 0 when capacity is unknown
uint8_t powerUnitMilliAmps; // capacities are mAh/mA, not mWh/mW
uint32_t remainingCapacity;
uint32_t fullCapacity; // last full charge capacity
uint32_t designCapacity;
uint32_t presentRate; // mW or mA, 0 when unknown
uint32_t voltageMv;
int32_t minutesRemaining; // -1 when not estimable (e.g. charging)
};
// Crash report (filled by kernel on process fault, returned via SYS_CRASH_REPORT)
struct CrashReportInfo {
int pid;
+10
View File
@@ -217,6 +217,16 @@ struct DesktopState {
uint64_t wifi_status_time;
// Battery indicator. Absent entirely on a machine with no ACPI battery
// device, which is how a desktop keeps a clean panel.
SvgIcon icon_battery;
char battery_icon_name[64]; // which glyph icon_battery holds
bool battery_present; // a battery device exists
bool battery_popup_open;
Rect battery_icon_rect;
montauk::abi::BatteryInfo battery_info;
uint64_t battery_last_poll;
bool vol_popup_open;
Rect vol_icon_rect;
int vol_level; // 0-100
+101 -40
View File
@@ -10,6 +10,7 @@
#include <montauk/syscall.h>
#include <montauk/string.h>
#include <Api/Syscall.hpp>
#include <montauk/capabilities.h>
namespace gui {
@@ -59,32 +60,47 @@ struct TerminalState {
int csi_current_param;
};
// ==== Palette ====
//
// The palette is a mutable global rather than a set of constants because the
// terminal lets the user switch themes at runtime. Cells store resolved colors,
// so a theme switch also has to rewrite the cells that were painted with the
// outgoing palette -- see terminal_remap_palette below.
struct TermPalette {
Color bg;
Color fg;
Color cursor;
Color ansi[16];
};
inline constexpr TermPalette TERM_PALETTE_DEFAULT = {
colors::TERM_BG,
colors::TERM_FG,
colors::TERM_FG,
{
Color::from_hex(0x000000), Color::from_hex(0xCC0000),
Color::from_hex(0x4E9A06), Color::from_hex(0xC4A000),
Color::from_hex(0x3465A4), Color::from_hex(0x75507B),
Color::from_hex(0x06989A), Color::from_hex(0xD3D7CF),
Color::from_hex(0x555753), Color::from_hex(0xEF2929),
Color::from_hex(0x8AE234), Color::from_hex(0xFCE94F),
Color::from_hex(0x729FCF), Color::from_hex(0xAD7FA8),
Color::from_hex(0x34E2E2), Color::from_hex(0xEEEEEC),
}
};
inline TermPalette g_term_palette = TERM_PALETTE_DEFAULT;
// Standard ANSI color palette as ARGB pixels
static inline Color term_ansi_color(int idx) {
switch (idx) {
case 0: return Color::from_hex(0x000000);
case 1: return Color::from_hex(0xCC0000);
case 2: return Color::from_hex(0x4E9A06);
case 3: return Color::from_hex(0xC4A000);
case 4: return Color::from_hex(0x3465A4);
case 5: return Color::from_hex(0x75507B);
case 6: return Color::from_hex(0x06989A);
case 7: return Color::from_hex(0xD3D7CF);
case 8: return Color::from_hex(0x555753);
case 9: return Color::from_hex(0xEF2929);
case 10: return Color::from_hex(0x8AE234);
case 11: return Color::from_hex(0xFCE94F);
case 12: return Color::from_hex(0x729FCF);
case 13: return Color::from_hex(0xAD7FA8);
case 14: return Color::from_hex(0x34E2E2);
case 15: return Color::from_hex(0xEEEEEC);
default: return colors::TERM_FG;
}
if (idx < 0 || idx > 15) return g_term_palette.fg;
return g_term_palette.ansi[idx];
}
// ANSI 256-color palette (0-15 = standard, 16-231 = RGB cube, 232-255 = grayscale)
static inline Color term_ansi_256_color(int idx) {
if (idx < 0) return colors::TERM_FG;
if (idx < 0) return g_term_palette.fg;
if (idx <= 15) return term_ansi_color(idx);
if (idx <= 231) {
// 6x6x6 RGB cube: idx 16 = (0,0,0), idx 231 = (5,5,5)
@@ -118,6 +134,47 @@ static inline void terminal_invalidate_render_cache(TerminalState* t) {
t->render_cursor_visible = false;
}
// Translate one already-resolved cell color from an outgoing palette to the
// incoming one. Colors that are not palette entries -- 256-color cube and
// grayscale ramp values -- are left exactly as the program asked for them.
static inline Color term_remap_color(Color c, const TermPalette& from,
const TermPalette& to) {
if (term_color_equal(c, from.bg)) return to.bg;
if (term_color_equal(c, from.fg)) return to.fg;
for (int i = 0; i < 16; i++) {
if (term_color_equal(c, from.ansi[i])) return to.ansi[i];
}
return c;
}
// Repaint an existing terminal in a new palette. Cells carry resolved colors,
// so switching themes without this leaves all scrollback -- and the shell's
// colored prompt -- in the old theme's colors.
static inline void terminal_remap_palette(TerminalState* t,
const TermPalette& from,
const TermPalette& to) {
if (!t || !t->cells) return;
int total = (t->rows + t->max_scrollback) * t->cols;
for (int i = 0; i < total; i++) {
t->cells[i].fg = term_remap_color(t->cells[i].fg, from, to);
t->cells[i].bg = term_remap_color(t->cells[i].bg, from, to);
}
if (t->alt_cells) {
int screen = t->rows * t->cols;
for (int i = 0; i < screen; i++) {
t->alt_cells[i].fg = term_remap_color(t->alt_cells[i].fg, from, to);
t->alt_cells[i].bg = term_remap_color(t->alt_cells[i].bg, from, to);
}
}
t->current_fg = term_remap_color(t->current_fg, from, to);
t->current_bg = term_remap_color(t->current_bg, from, to);
terminal_invalidate_render_cache(t);
t->dirty = true;
}
static inline Color terminal_erase_bg(TerminalState* t) {
return t->current_bg;
}
@@ -162,8 +219,8 @@ static inline void terminal_init_cells(TerminalState* t, int cols, int rows, int
t->scrollback_lines = 0;
t->max_scrollback = max_sb;
t->view_offset = 0;
t->current_fg = colors::TERM_FG;
t->current_bg = colors::TERM_BG;
t->current_fg = g_term_palette.fg;
t->current_bg = g_term_palette.bg;
t->cursor_visible = false;
t->alt_screen_active = false;
t->reverse_video = false;
@@ -201,10 +258,10 @@ static inline void terminal_init_cells(TerminalState* t, int cols, int rows, int
return;
}
for (int i = 0; i < total_cells; i++) {
t->cells[i] = {' ', colors::TERM_FG, colors::TERM_BG};
t->cells[i] = {' ', g_term_palette.fg, g_term_palette.bg};
}
for (int i = 0; i < screen_cells; i++) {
t->alt_cells[i] = {' ', colors::TERM_FG, colors::TERM_BG};
t->alt_cells[i] = {' ', g_term_palette.fg, g_term_palette.bg};
}
}
@@ -212,7 +269,8 @@ static inline void terminal_init(TerminalState* t, int cols, int rows) {
terminal_init_cells(t, cols, rows, TERM_MAX_SCROLLBACK);
t->cursor_visible = true;
t->child_pid = montauk::spawn_redir("0:/os/shell.elf");
static constexpr const char* kShellPath = "0:/os/shell.elf";
t->child_pid = montauk::caps::launch(kShellPath, {.redirected = true});
if (t->child_pid > 0)
montauk::childio_settermsz(t->child_pid, cols, rows);
}
@@ -245,7 +303,7 @@ static inline void terminal_enter_alt_screen(TerminalState* t) {
TermCell* screen = term_screen_row(t, 0);
for (int i = 0; i < total; i++) {
t->alt_cells[i] = screen[i];
screen[i] = {' ', colors::TERM_FG, colors::TERM_BG};
screen[i] = {' ', g_term_palette.fg, g_term_palette.bg};
}
t->view_offset = 0;
t->cursor_x = 0;
@@ -424,8 +482,8 @@ static inline void terminal_process_csi(TerminalState* t, char cmd) {
}
if (code == 0) {
t->current_fg = colors::TERM_FG;
t->current_bg = colors::TERM_BG;
t->current_fg = g_term_palette.fg;
t->current_bg = g_term_palette.bg;
t->reverse_video = false;
} else if (code == 1) {
// Bold: map to bright version of current color
@@ -473,15 +531,15 @@ static inline void terminal_process_csi(TerminalState* t, char cmd) {
} else if (code >= 100 && code <= 107) {
t->current_bg = term_ansi_color(code - 100 + 8);
} else if (code == 39) {
t->current_fg = colors::TERM_FG;
t->current_fg = g_term_palette.fg;
} else if (code == 49) {
t->current_bg = colors::TERM_BG;
t->current_bg = g_term_palette.bg;
}
}
if (t->csi_param_count == 0) {
// ESC[m with no params = reset
t->current_fg = colors::TERM_FG;
t->current_bg = colors::TERM_BG;
t->current_fg = g_term_palette.fg;
t->current_bg = g_term_palette.bg;
t->reverse_video = false;
}
break;
@@ -532,8 +590,8 @@ static inline void terminal_feed(TerminalState* t, const char* data, int len) {
for (int j = 0; j < 8; j++) t->csi_params[j] = 0;
} else if (ch == 'c') {
// Reset terminal
t->current_fg = colors::TERM_FG;
t->current_bg = colors::TERM_BG;
t->current_fg = g_term_palette.fg;
t->current_bg = g_term_palette.bg;
t->cursor_x = 0;
t->cursor_y = 0;
t->parse_state = TerminalState::STATE_NORMAL;
@@ -627,18 +685,21 @@ static inline void terminal_draw_cursor_pixels(const TermCell& cell,
uint32_t* pixels, int pw, int ph,
int px, int py, int cell_w, int cell_h,
bool use_ttf, GlyphCache* gc) {
// The block cursor takes its color from the palette and punches the glyph
// out in the background color, so it stays legible on light themes too --
// a hardcoded white block disappears on a white background.
terminal_fill_pixel_rect(pixels, pw, ph, px, py, cell_w, cell_h,
colors::WHITE.to_pixel());
g_term_palette.cursor.to_pixel());
if (cell.ch <= 32 && cell.ch >= 0) return;
if (use_ttf) {
int baseline = py + gc->ascent;
fonts::mono->draw_char_to_buffer(pixels, pw, ph,
px, baseline, (unsigned char)cell.ch, colors::BLACK, gc);
px, baseline, (unsigned char)cell.ch, g_term_palette.bg, gc);
} else {
terminal_draw_bitmap_char(pixels, pw, ph, px, py, (unsigned char)cell.ch,
colors::BLACK.to_pixel());
g_term_palette.bg.to_pixel());
}
}
@@ -700,7 +761,7 @@ static inline void terminal_render(TerminalState* t, uint32_t* pixels, int pw, i
t->render_base_row != base_row ||
t->render_term_cols != t->cols || t->render_term_rows != t->rows;
uint32_t bg_px = colors::TERM_BG.to_pixel();
uint32_t bg_px = g_term_palette.bg.to_pixel();
if (full_redraw) {
int row_bytes = pw * sizeof(uint32_t);
for (int i = 0; i < pw; i++) pixels[i] = bg_px;
@@ -779,9 +840,9 @@ static inline void terminal_resize(TerminalState* t, int new_cols, int new_rows)
// Clear new buffers
for (int i = 0; i < new_total; i++)
new_cells[i] = {' ', colors::TERM_FG, colors::TERM_BG};
new_cells[i] = {' ', g_term_palette.fg, g_term_palette.bg};
for (int i = 0; i < new_rows * new_cols; i++)
new_alt[i] = {' ', colors::TERM_FG, colors::TERM_BG};
new_alt[i] = {' ', g_term_palette.fg, g_term_palette.bg};
// Copy content: scrollback + visible screen
int old_content = t->scrollback_lines + t->rows;
+4 -4
View File
@@ -483,10 +483,10 @@ namespace fonts {
return f;
};
system_font = load("0:/fonts/Roboto-Medium.ttf");
system_bold = load("0:/fonts/Roboto-Bold.ttf");
mono = load("0:/fonts/JetBrainsMono-Regular.ttf");
mono_bold = load("0:/fonts/JetBrainsMono-Bold.ttf");
system_font = load("0:/os/fonts/Roboto-Medium.ttf");
system_bold = load("0:/os/fonts/Roboto-Bold.ttf");
mono = load("0:/os/fonts/JetBrainsMono-Regular.ttf");
mono_bold = load("0:/os/fonts/JetBrainsMono-Bold.ttf");
return system_font != nullptr;
}
+36 -6
View File
@@ -208,8 +208,38 @@ extern "C" {
#define MTK_SYS_USB_BULK_IN_START 182
#define MTK_SYS_USB_BULK_IN_STOP 183
#define MTK_SYS_USB_BULK_IN_READ 184
#define MTK_SYS_SPAWN_CAPS 185
#define MTK_SYS_SPAWN_REDIR_CAPS 186
#define MTK_SYS_ALLOC_EAGER 187
#define MTK_SYS_BATTERYINFO 188
#define MTK_SYS_GETCAPS 189
#define MTK_SYS_VOLINFO 190
#define MTK_SYS_CONSENT_REGISTER 191
#define MTK_SYS_CONSENT_NEXT 192
#define MTK_SYS_CONSENT_REPLY 193
/* @SYSCALLS-END */
/* Syscall status codes (montauk/statuscodes.h for C++). */
/* @STATUS-BEGIN
Generated from Common/StatusCodes.hpp by scripts/montauk-syscalls.py.
Do not edit by hand; run 'make gen-syscalls' to refresh. */
#define MTK_STATUS_SUCCESS (0)
#define MTK_STATUS_GENERIC_FAILURE (-1)
#define MTK_STATUS_PERMISSION_DENIED (-2)
#define MTK_STATUS_CAPABILITY_DENIED (-3)
#define MTK_STATUS_SYSTEM_INTEGRITY_ERROR (-4)
#define MTK_STATUS_BAD_POINTER (-5)
#define MTK_STATUS_INVALID_ARGUMENT (-6)
#define MTK_STATUS_UNSUPPORTED_OP (-7)
#define MTK_STATUS_NO_DEVICE (-8)
#define MTK_STATUS_IO_FAILURE (-9)
#define MTK_STATUS_BAD_INVOCATION (-10)
#define MTK_STATUS_NO_SUCH_PATH (-11)
#define MTK_STATUS_BUSY (-12)
#define MTK_STATUS_BUFFER_TOO_SMALL (-13)
#define MTK_STATUS_OUT_OF_MEMORY (-14)
/* @STATUS-END */
#define MTK_SOCK_TCP 1
#define MTK_SOCK_UDP 2
#define MTK_IPC_SIGNAL_READABLE (1u << 0)
@@ -683,8 +713,8 @@ static inline int mtk_set_unix_time(int64_t unix_seconds) {
return (int)_mtk_syscall1(MTK_SYS_SETUNIXTIME, (long)unix_seconds);
}
static inline void mtk_settz(int offset_minutes) {
_mtk_syscall1(MTK_SYS_SETTZ, (long)offset_minutes);
static inline int mtk_settz(int offset_minutes) {
return (int)_mtk_syscall1(MTK_SYS_SETTZ, (long)offset_minutes);
}
static inline int mtk_gettz(void) {
@@ -883,12 +913,12 @@ static inline int mtk_audio_ctl(int handle, int cmd, int value) {
Power management
==================================================================== */
static inline void mtk_reset(void) {
_mtk_syscall0(MTK_SYS_RESET);
static inline int mtk_reset(void) {
return (int)_mtk_syscall0(MTK_SYS_RESET);
}
static inline void mtk_shutdown(void) {
_mtk_syscall0(MTK_SYS_SHUTDOWN);
static inline int mtk_shutdown(void) {
return (int)_mtk_syscall0(MTK_SYS_SHUTDOWN);
}
/* ====================================================================
+1
View File
@@ -21,6 +21,7 @@ typedef struct {
/* Byte-oriented C locale only: one byte, one character. */
size_t mbstowcs(wchar_t *dst, const char *src, size_t n);
int mblen(const char *s, size_t n);
size_t mbrtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *ps);
#ifdef __cplusplus
}
@@ -0,0 +1,216 @@
/*
* capabilities.h
* Shared reader for the capability grant table (0:/config/capabilities.toml)
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <montauk/syscall.h>
#include <montauk/toml.h>
#include <montauk/config.h>
#include <montauk/heap.h>
namespace montauk {
namespace caps {
inline constexpr const char* GRANT_CONFIG = "capabilities";
inline constexpr const char* GRANT_PREFIX = "grant.";
struct CapName {
const char* name;
uint64_t bit;
};
// Names as they appear in the config file. Kept in the same order as the
// CAP_* bit definitions in Api/Syscall.hpp.
inline constexpr CapName NAMES[] = {
{"process_admin", montauk::abi::CAP_PROCESS_ADMIN},
{"power_request", montauk::abi::CAP_POWER_REQUEST},
{"power_control", montauk::abi::CAP_POWER_CONTROL},
{"suspend", montauk::abi::CAP_SUSPEND},
{"storage_admin", montauk::abi::CAP_STORAGE_ADMIN},
{"raw_storage", montauk::abi::CAP_RAW_STORAGE},
{"network_admin", montauk::abi::CAP_NETWORK_ADMIN},
{"set_time", montauk::abi::CAP_SET_TIME},
{"user_admin", montauk::abi::CAP_USER_ADMIN},
{"display_admin", montauk::abi::CAP_DISPLAY_ADMIN},
{"device_admin", montauk::abi::CAP_DEVICE_ADMIN},
{"log_read", montauk::abi::CAP_LOG_READ},
{"system_image", montauk::abi::CAP_SYSTEM_IMAGE},
{"compositor", montauk::abi::CAP_COMPOSITOR},
};
// Never implied by "all": each must be named explicitly in a grant.
inline constexpr uint64_t EXPLICIT_ONLY =
montauk::abi::CAP_SYSTEM_IMAGE | montauk::abi::CAP_COMPOSITOR;
inline uint64_t bit_for_name(const char* name) {
if (name == nullptr || name[0] == '\0') return 0;
// "all" means "everything this launcher may pass on".
if (montauk::streq(name, "all"))
return montauk::abi::CAP_ALL & ~EXPLICIT_ONLY;
for (const auto& entry : NAMES) {
if (montauk::streq(entry.name, name)) return entry.bit;
}
return 0;
}
// True when a grant for `path` can apply: the program is installed under
// 0:/os or 0:/apps, which cannot be rewritten without system_image. The
// kernel enforces this (Fs::IsSystemImage) by starting any other program
// without capabilities; this copy of the rule is only for display.
inline bool is_installed_path(const char* path) {
if (path == nullptr) return false;
const char* p = path;
uint64_t drive = 0;
if (*p < '0' || *p > '9') return false;
while (*p >= '0' && *p <= '9') drive = drive * 10 + (uint64_t)(*p++ - '0');
if (*p != ':' || drive != 0) return false;
p++;
static const char* const roots[] = {"/os", "/apps"};
for (const char* root : roots) {
int i = 0;
while (root[i]) {
char c = p[i];
if (c >= 'A' && c <= 'Z') c = (char)(c + ('a' - 'A'));
if (c != root[i]) break;
i++;
}
if (root[i] == '\0' && (p[i] == '\0' || p[i] == '/')) return true;
}
return false;
}
// Read an array-of-strings key into a capability mask.
inline uint64_t mask_from_key(const montauk::toml::Doc& doc, const char* key) {
montauk::toml::Value* arr = doc.get_array(key);
if (arr == nullptr) return 0;
uint64_t mask = 0;
for (int i = 0; i < arr->array.count; i++) {
montauk::toml::Value* item = arr->array.items[i];
if (item == nullptr || item->type != montauk::toml::Type::String) continue;
mask |= bit_for_name(item->str);
}
return mask;
}
// Append `suffix` to the "grant.<id>." stem of `path_key`.
// Returns false if the key is not of that shape or does not fit.
inline bool build_sibling_key(const char* path_key, const char* suffix,
char* out, int outSz) {
int prefixLen = 0;
for (; GRANT_PREFIX[prefixLen]; prefixLen++) {
if (path_key[prefixLen] != GRANT_PREFIX[prefixLen]) return false;
}
// Copy through the final '.' so "grant.foo.path" yields "grant.foo.".
int lastDot = -1;
for (int i = 0; path_key[i]; i++) {
if (path_key[i] == '.') lastDot = i;
}
if (lastDot < prefixLen) return false;
int n = 0;
for (; n <= lastDot && n < outSz - 1; n++) out[n] = path_key[n];
for (int i = 0; suffix[i] && n < outSz - 1; i++) out[n++] = suffix[i];
out[n] = '\0';
return true;
}
// Look up the grant declared for `binary_path`. Returns false when the
// path has no entry, which is the common case and means "no authority".
inline bool lookup(const char* binary_path,
montauk::abi::SpawnCapabilities& out) {
out = {0, 0};
if (binary_path == nullptr || binary_path[0] == '\0') return false;
montauk::toml::Doc doc = montauk::config::load(GRANT_CONFIG);
bool found = false;
for (int i = 0; i < doc.entries.count && !found; i++) {
montauk::toml::Value* entry = doc.entries.items[i];
if (entry == nullptr || entry->key == nullptr) continue;
if (entry->type != montauk::toml::Type::String) continue;
char sibling[128];
if (!build_sibling_key(entry->key, "path", sibling, sizeof(sibling))) continue;
if (!montauk::streq(sibling, entry->key)) continue;
if (!montauk::streq(entry->str, binary_path)) continue;
build_sibling_key(entry->key, "effective", sibling, sizeof(sibling));
uint64_t effective = mask_from_key(doc, sibling);
build_sibling_key(entry->key, "delegable", sibling, sizeof(sibling));
uint64_t delegable = mask_from_key(doc, sibling);
out.effective = effective;
out.delegable = delegable;
found = true;
}
doc.destroy();
return found;
}
// The calling process's own capability masks.
inline bool self(montauk::abi::SpawnCapabilities& out) {
out = {0, 0};
return montauk::getcaps(&out) == 0;
}
inline uint64_t self_delegable() {
montauk::abi::SpawnCapabilities mine;
return self(mine) ? mine.delegable : 0;
}
// Build a spawn request for `binary_path`, clamped to what the caller may
// actually delegate.
inline montauk::abi::SpawnCapabilities for_binary(const char* binary_path,
uint64_t caller_delegable) {
montauk::abi::SpawnCapabilities caps{0, 0};
if (caller_delegable == 0) return caps;
if (!lookup(binary_path, caps)) return caps;
caps.effective &= caller_delegable;
caps.delegable &= caller_delegable;
return caps;
}
// The calling process's delegable set, cached.
inline uint64_t my_delegable() {
static uint64_t cached = 0;
static bool loaded = false;
if (!loaded) {
cached = self_delegable();
loaded = true;
}
return cached;
}
struct LaunchOptions {
const char* args = nullptr;
// Owner identity for the child. Requires CAP_USER_ADMIN and is
// reserved for session creators (init, login);
const char* user = nullptr;
// True for a console host that reads and writes the child's I/O
// through the childio calls rather than sharing its own.
bool redirected = false;
};
// Unified launch helper.
inline int launch(const char* path, const LaunchOptions& opt = {}) {
if (path == nullptr || path[0] == '\0') return -1;
if (opt.redirected && opt.user != nullptr) return -1;
montauk::abi::SpawnCapabilities caps = for_binary(path, my_delegable());
return opt.redirected
? spawn_redir_with_caps(path, opt.args, caps)
: spawn_with_caps(path, opt.args, opt.user, caps);
}
} // namespace caps
} // namespace montauk
+209
View File
@@ -0,0 +1,209 @@
/*
* consent.h
* First-use consent: prompt wording and the stored-decision table
* (0:/config/consent.toml).
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <montauk/syscall.h>
#include <montauk/string.h>
#include <montauk/toml.h>
#include <montauk/config.h>
#include <montauk/capabilities.h>
namespace montauk {
namespace consent {
inline constexpr const char* STORE_CONFIG = "consent";
inline constexpr const char* STORE_PREFIX = "decision.";
struct CapText {
uint64_t bit;
const char* action; // completes "<App> wants to ..."
const char* icon; // shown in the prompt
};
inline constexpr CapText TEXTS[] = {
{montauk::abi::CAP_STORAGE_ADMIN, "format, partition and mount disks",
"0:/os/icons/drive-harddisk.svg"},
{montauk::abi::CAP_NETWORK_ADMIN, "change network settings and join Wi-Fi networks",
"0:/os/icons/network-wired.svg"},
{montauk::abi::CAP_SET_TIME, "change the system clock and time zone",
"0:/os/icons/preferences-system-time.svg"},
{montauk::abi::CAP_DISPLAY_ADMIN, "change the screen resolution and brightness",
"0:/os/icons/preferences-desktop-display.svg"},
{montauk::abi::CAP_LOG_READ, "read the system log, which can reveal what other apps and devices are doing",
"0:/os/icons/utilities-log-viewer.svg"},
};
// Fallback for a capability without its own icon.
inline constexpr const char* DEFAULT_ICON = "0:/os/icons/lock.svg";
inline const char* action_for(uint64_t bit) {
for (const auto& t : TEXTS) {
if (t.bit == bit) return t.action;
}
return "use a protected system capability";
}
inline const char* icon_for(uint64_t bit) {
for (const auto& t : TEXTS) {
if (t.bit == bit) return t.icon;
}
return DEFAULT_ICON;
}
inline const char* name_for(uint64_t bit) {
for (const auto& n : montauk::caps::NAMES) {
if (n.bit == bit) return n.name;
}
return "";
}
// One stored decision, as read from the table. Only programs installed
// under 0:/os or 0:/apps are ever stored: those paths cannot be rewritten
// without CAP_SYSTEM_IMAGE, so the path alone identifies the program.
// The kernel reports this as ConsentRequest::imageProtected.
struct Decision {
char id[16];
char path[256];
uint64_t capability;
bool allow;
};
namespace detail {
inline void key(char* out, int cap, const char* id, const char* field) {
int n = 0;
for (const char* p = STORE_PREFIX; *p && n < cap - 1; p++) out[n++] = *p;
for (const char* p = id; *p && n < cap - 1; p++) out[n++] = *p;
if (n < cap - 1) out[n++] = '.';
for (const char* p = field; *p && n < cap - 1; p++) out[n++] = *p;
out[n] = '\0';
}
// Ids of "decision.<id>.path" entries, in file order.
inline int collect_ids(const montauk::toml::Doc& doc, char (*ids)[16], int max) {
int count = 0;
int prefixLen = montauk::slen(STORE_PREFIX);
for (int i = 0; i < doc.entries.count && count < max; i++) {
montauk::toml::Value* v = doc.entries.items[i];
if (v == nullptr || v->key == nullptr) continue;
if (!montauk::starts_with(v->key, STORE_PREFIX)) continue;
const char* rest = v->key + prefixLen;
int dot = 0;
while (rest[dot] && rest[dot] != '.') dot++;
if (rest[dot] != '.' || !montauk::streq(rest + dot + 1, "path")) continue;
if (dot <= 0 || dot >= 16) continue;
montauk::memcpy(ids[count], rest, dot);
ids[count][dot] = '\0';
count++;
}
return count;
}
inline bool read(const montauk::toml::Doc& doc, const char* id, Decision& out) {
char k[64];
montauk::memset(&out, 0, sizeof(out));
montauk::strncpy(out.id, id, sizeof(out.id));
key(k, sizeof(k), id, "path");
montauk::strncpy(out.path, doc.get_string(k, ""), sizeof(out.path));
key(k, sizeof(k), id, "capability");
out.capability = montauk::caps::bit_for_name(doc.get_string(k, ""));
key(k, sizeof(k), id, "decision");
out.allow = montauk::streq(doc.get_string(k, ""), "allow");
// "all" or an unknown name is never a valid stored decision.
return out.path[0] &&
(out.capability & montauk::abi::CAP_CONSENT_ELIGIBLE) != 0 &&
(out.capability & (out.capability - 1)) == 0;
}
inline void remove_id(montauk::toml::Doc* doc, const char* id) {
static const char* fields[] = {"path", "capability", "decision"};
char k[64];
for (const char* f : fields) {
key(k, sizeof(k), id, f);
montauk::config::unset(doc, k);
}
}
} // namespace detail
// Read up to `max` stored decisions. Returns the count.
inline int load_all(Decision* out, int max) {
montauk::toml::Doc doc = montauk::config::load(STORE_CONFIG);
char ids[128][16];
int idCount = detail::collect_ids(doc, ids, 128);
int count = 0;
for (int i = 0; i < idCount && count < max; i++) {
if (detail::read(doc, ids[i], out[count])) count++;
}
doc.destroy();
return count;
}
// Stored answer for (path, capability): 1 allow, 0 deny, -1 none.
inline int lookup(const char* path, uint64_t capability) {
montauk::toml::Doc doc = montauk::config::load(STORE_CONFIG);
char ids[128][16];
int idCount = detail::collect_ids(doc, ids, 128);
int result = -1;
for (int i = 0; i < idCount && result < 0; i++) {
Decision d;
if (!detail::read(doc, ids[i], d)) continue;
if (d.capability == capability && montauk::streq(d.path, path)) {
result = d.allow ? 1 : 0;
}
}
doc.destroy();
return result;
}
// Store an answer, replacing any earlier one for the same path and
// capability. Returns 0 on success, negative when the table cannot be
// written.
inline int record(const char* path, uint64_t capability, bool allow) {
montauk::toml::Doc doc = montauk::config::load(STORE_CONFIG);
char ids[128][16];
int idCount = detail::collect_ids(doc, ids, 128);
int nextId = 1;
for (int i = 0; i < idCount; i++) {
Decision d;
bool valid = detail::read(doc, ids[i], d);
if (valid && d.capability == capability && montauk::streq(d.path, path))
detail::remove_id(&doc, ids[i]);
int n = 0;
for (const char* p = ids[i]; *p >= '0' && *p <= '9'; p++) n = n * 10 + (*p - '0');
if (n >= nextId) nextId = n + 1;
}
char id[16];
int len = 0;
char rev[16];
for (int n = nextId; n > 0 && len < 15; n /= 10) rev[len++] = (char)('0' + n % 10);
for (int i = 0; i < len; i++) id[i] = rev[len - 1 - i];
id[len] = '\0';
char k[64];
detail::key(k, sizeof(k), id, "path");
montauk::config::set_string(&doc, k, path);
detail::key(k, sizeof(k), id, "capability");
montauk::config::set_string(&doc, k, name_for(capability));
detail::key(k, sizeof(k), id, "decision");
montauk::config::set_string(&doc, k, allow ? "allow" : "deny");
int rc = montauk::config::save(STORE_CONFIG, &doc);
doc.destroy();
return rc;
}
// Drop one stored decision by id; the program is asked again next time.
inline int forget(const char* id) {
montauk::toml::Doc doc = montauk::config::load(STORE_CONFIG);
detail::remove_id(&doc, id);
int rc = montauk::config::save(STORE_CONFIG, &doc);
doc.destroy();
return rc;
}
} // namespace consent
} // namespace montauk
@@ -0,0 +1,35 @@
/*
* statuscodes.h
* MontaukOS status codes
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
// Userspace mirror of kernel/src/Common/StatusCodes.hpp. Also usable from C
// (the libc includes it), where the enumerators are unqualified.
#ifdef __cplusplus
namespace montauk {
#endif
enum StatusCodes {
Success = 0,
GenericFailure = -1,
PermissionDenied = -2,
CapabilityDenied = -3,
SystemIntegrityError = -4,
BadPointer = -5,
InvalidArgument = -6,
UnsupportedOp = -7,
NoDevice = -8,
IoFailure = -9,
BadInvocation = -10,
NoSuchPath = -11,
Busy = -12,
BufferTooSmall = -13,
OutOfMemory = -14
};
#ifdef __cplusplus
}
#endif
+60 -7
View File
@@ -6,6 +6,7 @@
#pragma once
#include <Api/Syscall.hpp>
#include <montauk/statuscodes.h>
namespace montauk {
@@ -120,6 +121,28 @@ namespace montauk {
inline int spawn(const char* path, const char* args = nullptr) {
return (int)syscall2(montauk::abi::SYS_SPAWN, (uint64_t)path, (uint64_t)args);
}
inline int spawn_with_caps(const char* path, const char* args,
const char* user,
const montauk::abi::SpawnCapabilities& capabilities) {
return (int)syscall4(montauk::abi::SYS_SPAWN_CAPS, (uint64_t)path,
(uint64_t)args, (uint64_t)user,
(uint64_t)&capabilities);
}
// The calling process's own capability masks. Returns 0 on success.
inline int getcaps(montauk::abi::SpawnCapabilities* out) {
return (int)syscall1(montauk::abi::SYS_GETCAPS, (uint64_t)out);
}
// Consent agent.
inline int consent_register() {
return (int)syscall0(montauk::abi::SYS_CONSENT_REGISTER);
}
inline int consent_next(montauk::abi::ConsentRequest* out) {
return (int)syscall1(montauk::abi::SYS_CONSENT_NEXT, (uint64_t)out);
}
inline int consent_reply(uint64_t id, int decision) {
return (int)syscall2(montauk::abi::SYS_CONSENT_REPLY, id, (uint64_t)decision);
}
inline int chdir(const char* path) {
return (int)syscall1(montauk::abi::SYS_CHDIR, (uint64_t)path);
}
@@ -184,6 +207,10 @@ namespace montauk {
// Memory
inline void* alloc(uint64_t size) { return (void*)syscall1(montauk::abi::SYS_ALLOC, size); }
// As alloc(), but the kernel commits the whole range immediately. Use it
// for a buffer that is about to be written end to end: the lazy path costs
// one page fault, one mutex acquire and one VMA walk per 4 KiB.
inline void* alloc_eager(uint64_t size) { return (void*)syscall1(montauk::abi::SYS_ALLOC_EAGER, size); }
inline void free(void* ptr) { syscall1(montauk::abi::SYS_FREE, (uint64_t)ptr); }
// Timekeeping
@@ -391,7 +418,10 @@ namespace montauk {
}
// Timezone offset (total minutes from UTC)
inline void settz(int offset_minutes) { syscall1(montauk::abi::SYS_SETTZ, (uint64_t)(int64_t)offset_minutes); }
inline int settz(int offset_minutes) {
return (int)syscall1(montauk::abi::SYS_SETTZ,
(uint64_t)(int64_t)offset_minutes);
}
inline int gettz() { return (int)syscall0(montauk::abi::SYS_GETTZ); }
// Random number generation
@@ -400,14 +430,12 @@ namespace montauk {
}
// Power management
[[noreturn]] inline void reset() {
syscall0(montauk::abi::SYS_RESET);
__builtin_unreachable();
inline int reset() {
return (int)syscall0(montauk::abi::SYS_RESET);
}
[[noreturn]] inline void shutdown() {
syscall0(montauk::abi::SYS_SHUTDOWN);
__builtin_unreachable();
inline int shutdown() {
return (int)syscall0(montauk::abi::SYS_SHUTDOWN);
}
inline int suspend() {
@@ -423,6 +451,13 @@ namespace montauk {
return (int)syscall1(montauk::abi::SYS_POWER_REQUEST, (uint64_t)(int64_t)action);
}
// Non-destructive read of the pending request, for a session leader that
// must stand down when something inside its session (the shell's shutdown
// builtin, say) asked for power-off. Returns POWER_REQ_QUERY when idle.
inline int power_request_pending() {
return power_request(montauk::abi::POWER_REQ_PEEK);
}
// Mouse
inline void mouse_state(montauk::abi::MouseState* out) { syscall1(montauk::abi::SYS_MOUSESTATE, (uint64_t)out); }
inline void set_mouse_bounds(int32_t maxX, int32_t maxY) {
@@ -447,6 +482,13 @@ namespace montauk {
inline int spawn_redir(const char* path, const char* args = nullptr) {
return (int)syscall2(montauk::abi::SYS_SPAWN_REDIR, (uint64_t)path, (uint64_t)args);
}
inline int spawn_redir_with_caps(
const char* path, const char* args,
const montauk::abi::SpawnCapabilities& capabilities) {
return (int)syscall3(montauk::abi::SYS_SPAWN_REDIR_CAPS,
(uint64_t)path, (uint64_t)args,
(uint64_t)&capabilities);
}
inline int childio_read(int childPid, char* buf, int maxLen) {
return (int)syscall3(montauk::abi::SYS_CHILDIO_READ, (uint64_t)childPid, (uint64_t)buf, (uint64_t)maxLen);
}
@@ -484,6 +526,10 @@ namespace montauk {
inline int partlist(montauk::abi::PartInfo* buf, int max) {
return (int)syscall2(montauk::abi::SYS_PARTLIST, (uint64_t)buf, (uint64_t)max);
}
// One entry per mounted volume (one filesystem, i.e. one drive number).
inline int volinfo(montauk::abi::VolInfo* buf, int max) {
return (int)syscall2(montauk::abi::SYS_VOLINFO, (uint64_t)buf, (uint64_t)max);
}
// Raw block device I/O (driver-agnostic)
inline int64_t disk_read(int blockDev, uint64_t lba, uint32_t sectorCount, void* buf) {
@@ -613,6 +659,13 @@ namespace montauk {
return (int)syscall1(montauk::abi::SYS_BTINFO, (uint64_t)buf);
}
// ACPI battery status. Returns 0 on success, or -1 on a machine with no
// battery device at all -- which is the signal not to draw an indicator.
// A laptop with the pack removed still returns 0, with present == 0.
inline int battery_info(montauk::abi::BatteryInfo* out) {
return (int)syscall1(montauk::abi::SYS_BATTERYINFO, (uint64_t)out);
}
// Wi-Fi. scan() runs a full channel sweep and blocks until it finishes or
// timeoutMs elapses, then fills buf with the networks seen; it returns the
// number of entries written, or -1 when no adapter is ready.
+10
View File
@@ -219,6 +219,16 @@ namespace user {
return false;
}
inline bool is_admin(const char* username) {
UserInfo users[MAX_USERS];
int count = load_users(users, MAX_USERS);
for (int i = 0; i < count; i++) {
if (montauk::streq(users[i].username, username))
return montauk::streq(users[i].role, "admin");
}
return false;
}
// ---- User management ----
inline bool create_user(const char* username, const char* display_name,
Binary file not shown.