Skip to content

Commit f18379d

Browse files
committed
Added syscall stuff
Updated errors
1 parent f318abf commit f18379d

File tree

3 files changed

+58
-147
lines changed

3 files changed

+58
-147
lines changed

src/credentials.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
/**
55
* Credentials used for various operations.
66
* Similar to Linux's cred struct.
7-
* @category Internals
8-
* @see https://github.com/torvalds/linux/blob/master/include/linux/cred.h
7+
* @see Linux include/linux/cred.h
98
*/
109
export interface Credentials {
1110
uid: number;
@@ -22,15 +21,13 @@ export interface Credentials {
2221

2322
/**
2423
* Initialization for a set of credentials
25-
* @category Internals
2624
*/
2725
export interface CredentialsInit extends Partial<Credentials> {
2826
uid: number;
2927
gid: number;
3028
}
3129

3230
/**
33-
* @category Internals
3431
*/
3532
export function createCredentials(source: CredentialsInit): Credentials {
3633
return {
@@ -46,7 +43,6 @@ export function createCredentials(source: CredentialsInit): Credentials {
4643
/**
4744
* Returns true if the credentials can be used for an operation that requires root privileges.
4845
* @internal
49-
* @category Internals
5046
*/
5147
export function credentialsAllowRoot(cred?: Credentials): boolean {
5248
if (!cred) return false;

src/error.ts

Lines changed: 7 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// SPDX-License-Identifier: GPL-3.0-or-later WITH EXCEPTIONS
22
// Copyright (c) 2025 James Prevett
33

4-
import { omit } from 'utilium';
5-
64
/**
75
* Standard POSIX error codes.
86
* @see https://en.wikipedia.org/wiki/Errno.h
@@ -407,152 +405,19 @@ const errnoMessages = {
407405
[Errno.EHWPOISON]: 'Memory page has hardware error',
408406
} as const satisfies Record<Errno, string>;
409407

408+
type ErrName<T extends Errno> = T extends (typeof Errno)[infer K extends keyof typeof Errno] ? K : keyof typeof Errno;
409+
410+
export function errname<const T extends Errno>(err: T): ErrName<T> {
411+
return Errno[err] as ErrName<T>;
412+
}
413+
410414
type ErrnoMessage<T extends Errno | keyof typeof Errno> = (typeof errnoMessages)[T extends Errno
411415
? T
412416
: T extends keyof typeof Errno
413417
? (typeof Errno)[T]
414-
: ''];
418+
: string];
415419

416420
export function strerror<const T extends Errno | keyof typeof Errno>(errno: T): ErrnoMessage<T> {
417421
const _errno: Errno = typeof errno == 'string' ? Errno[errno] : errno;
418422
return (_errno in errnoMessages ? errnoMessages[_errno] : '') as ErrnoMessage<T>;
419423
}
420-
421-
export interface ExceptionExtra {
422-
path?: string;
423-
dest?: string;
424-
syscall?: string;
425-
[k: string]: any;
426-
}
427-
428-
/**
429-
* JSON representation of an error.
430-
*/
431-
export interface ExceptionJSON {
432-
errno: Errno;
433-
message: string;
434-
code: keyof typeof Errno;
435-
stack: string;
436-
path?: string;
437-
dest?: string;
438-
syscall?: string;
439-
}
440-
441-
/**
442-
* Set the message of an exception to the UV-style message.
443-
*/
444-
export function setUVMessage<T extends ExceptionJSON>(ex: T): T {
445-
let message = `${ex.code}: ${errnoMessages[ex.errno]}, ${ex.syscall}`;
446-
447-
if (ex.path) message += ` '${ex.path}'`;
448-
if (ex.dest) message += ` -> '${ex.dest}'`;
449-
if (ex.message && !ex.message.startsWith(errnoMessages[ex.errno])) message += ` (${ex.message})`;
450-
451-
ex.message = message;
452-
return ex;
453-
}
454-
455-
/**
456-
* An error with additional information about what happened.
457-
*
458-
* @privateRemarks
459-
*
460-
* This is modeled after Node.js's `ErrnoException` and `UVException`.
461-
*
462-
* `Error.captureStackTrace` is used when available to hide irrelevant stack frames.
463-
* This is being standardized, however it is not available in Deno and behind a flag in Firefox.
464-
* See https://github.com/tc39/proposal-error-capturestacktrace for more details.
465-
*/
466-
export class Exception extends Error implements ExceptionJSON {
467-
declare public stack: string;
468-
469-
public code: keyof typeof Errno;
470-
471-
public path?: string;
472-
public dest?: string;
473-
public syscall?: string;
474-
475-
public constructor(errno: Errno, message: false, context: ExceptionExtra);
476-
public constructor(errno: Errno, message?: string);
477-
public constructor(
478-
public errno: Errno,
479-
message?: string | false,
480-
ctx: ExceptionExtra = {}
481-
) {
482-
const code = Errno[errno] as keyof typeof Errno;
483-
484-
super(message || '');
485-
486-
this.code = code;
487-
Object.assign(this, omit(ctx, 'message'));
488-
if (!message) setUVMessage(this);
489-
490-
Error.captureStackTrace?.(this, this.constructor);
491-
}
492-
493-
public toString(): string {
494-
return this.message;
495-
}
496-
497-
public toJSON(): ExceptionJSON {
498-
const json: ExceptionJSON = {
499-
errno: this.errno,
500-
code: this.code,
501-
stack: this.stack,
502-
message: this.message,
503-
};
504-
505-
if (this.path) json.path = this.path;
506-
if (this.dest) json.dest = this.dest;
507-
if (this.syscall) json.syscall = this.syscall;
508-
509-
return json;
510-
}
511-
512-
public static fromJSON(this: void, json: ExceptionJSON): Exception {
513-
const err = json.syscall ? new Exception(json.errno, false, json) : new Exception(json.errno, json.message);
514-
err.stack = json.stack;
515-
return err;
516-
}
517-
}
518-
519-
/**
520-
* Shortcut for UV-style exceptions.
521-
*/
522-
export function UV(this: void, code: keyof typeof Errno, syscall: string, path?: string, dest?: string): Exception;
523-
export function UV(this: void, code: keyof typeof Errno, context?: ExceptionExtra): Exception;
524-
export function UV(
525-
this: void,
526-
code: keyof typeof Errno,
527-
context?: ExceptionExtra | string,
528-
path?: string,
529-
dest?: string
530-
): Exception {
531-
if (typeof context === 'string') context = { syscall: context, path, dest };
532-
const err = new Exception(Errno[code], false, context ?? {});
533-
Error.captureStackTrace?.(err, UV);
534-
return err;
535-
}
536-
537-
/**
538-
* Shortcut to easily create an `Exception` with a specific error code.
539-
*/
540-
export function withErrno(this: void, code: keyof typeof Errno, message?: string): Exception {
541-
const err = new Exception(Errno[code], message ?? errnoMessages[Errno[code]]);
542-
Error.captureStackTrace?.(err, withErrno);
543-
return err;
544-
}
545-
546-
export function rethrow(syscall: string, path?: string, dest?: string): (e: Exception) => never;
547-
export function rethrow(extra: ExceptionExtra): (e: Exception) => never;
548-
export function rethrow(extra: ExceptionExtra | string, path?: string, dest?: string): (e: Exception) => never {
549-
const ctx = typeof extra === 'string' ? { syscall: extra } : extra;
550-
if (path) ctx.path = path;
551-
if (dest) ctx.dest = dest;
552-
553-
return function (e: Exception) {
554-
Object.assign(e, ctx);
555-
setUVMessage(e);
556-
throw e;
557-
};
558-
}

src/syscalls.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { Errno as E, strerror, type Errno } from './error.js';
2+
import { warn } from './log.js';
3+
4+
export type Syscall = (...args: any[]) => any;
5+
6+
/**
7+
* Augment this when defining syscalls.
8+
*/
9+
export interface Syscalls extends Record<string, Syscall> {}
10+
11+
export const syscalls = Object.create(null) as Syscalls;
12+
13+
const expected_name = /[\w_]+/i;
14+
15+
export function define_syscall<const Name extends keyof Syscalls>(handler: Syscalls[Name]): void {
16+
if (!expected_name.test(handler.name)) warn('Syscall name is not a normal identifier: ' + handler.name);
17+
if (handler.name in syscalls) throw new Error('Syscall is already defined: ' + handler.name);
18+
Object.defineProperty(syscalls, handler.name, {
19+
value: handler,
20+
enumerable: true,
21+
writable: false,
22+
configurable: false,
23+
});
24+
}
25+
26+
class SyscallError extends Error {
27+
readonly name = 'SyscallError';
28+
29+
constructor(public readonly code: Errno) {
30+
const message = strerror(code);
31+
super(message);
32+
Error.captureStackTrace?.(this, do_syscall);
33+
}
34+
}
35+
36+
export type { SyscallError };
37+
38+
export function do_syscall<const Name extends keyof Syscalls>(
39+
name: Name,
40+
...args: Parameters<Syscalls[Name]>
41+
): ReturnType<Syscalls[Name]> {
42+
const syscall = syscalls[name];
43+
if (!syscall) throw new SyscallError(E.ENOSYS);
44+
45+
try {
46+
return syscall(...args);
47+
} catch (ex: unknown) {
48+
throw new SyscallError(ex as Errno);
49+
}
50+
}

0 commit comments

Comments
 (0)