feat: extend ulib
This commit is contained in:
1 parent
2ee63600a4
commit
b3b01cc38f
1 file changed
+202
+202
@@ -3,6 +3,19 @@
|
||||
|
||||
#include "syscall.h"
|
||||
#include "fs.h"
|
||||
#include "signalnum.h"
|
||||
#include "poll.h"
|
||||
|
||||
typedef void (*SigHandler)(int signum);
|
||||
|
||||
/* Real sigaction()'s sa_sigaction shape: signum (same as SigHandler's own
|
||||
argument, kept for parity -- see signalnum.h's SigInfo.siSignum comment),
|
||||
a pointer to the real SigInfo describing THIS delivery, and a context
|
||||
pointer this kernel always hands back as NULL (see signalnum.h's
|
||||
sigFlagSigInfo comment for why). Only ever meaningful for a handler
|
||||
installed through userSigActionInfo below, WITH sigFlagSigInfo actually
|
||||
set in its flags -- see that function's own doc comment. */
|
||||
typedef void (*SigInfoHandler)(int signum, SigInfo* info, void* context);
|
||||
|
||||
long userWrite(int fd, const char* buffer, unsigned long length);
|
||||
long userRead(int fd, char* buffer, unsigned long length);
|
||||
@@ -12,6 +25,22 @@ long userGetpid(void);
|
||||
void* userSbrk(long increment);
|
||||
void userSleep(unsigned long milliseconds);
|
||||
|
||||
/* Real alarm(2): schedules sigAlrm against the calling process `seconds`
|
||||
real seconds from now, replacing any alarm already pending; 0 cancels
|
||||
instead of scheduling. Returns the whole seconds remaining on whatever
|
||||
alarm was pending before this call (rounded up), or 0 if none was. See
|
||||
sched.h's threadSetAlarm for the full contract. */
|
||||
unsigned long userAlarm(unsigned long seconds);
|
||||
|
||||
/* Real poll(2): waits for any of `nfds` PollFd entries (poll.h) to become
|
||||
ready, filling each one's revents in place. timeoutMs < 0 waits forever,
|
||||
0 reports current readiness without ever blocking, > 0 blocks at most
|
||||
that many milliseconds (rounded up to this kernel's ~10ms PIT tick
|
||||
granularity -- see sched.c's threadSetAlarm for the same rounding on a
|
||||
different timer). Returns the number of entries with a nonzero revents
|
||||
(0 on timeout, never negative except -1 for a bad nfds/pointer). */
|
||||
long userPoll(PollFd* fds, unsigned long nfds, long timeoutMs);
|
||||
|
||||
long userOpen(const char* path, unsigned int flags);
|
||||
long userClose(int fd);
|
||||
long userLseek(int fd, long offset, int whence);
|
||||
@@ -20,6 +49,179 @@ long userGetdents(int fd, FbDirent* out, unsigned long length);
|
||||
long userMkdir(const char* path);
|
||||
long userUnlink(const char* path);
|
||||
|
||||
/* Real rmdir(2): the counterpart userUnlink above refuses a directory
|
||||
(EISDIR); this refuses anything that is NOT an empty directory
|
||||
(ENOTDIR/ENOTEMPTY, both collapsed to -1 here, matching userUnlink's own
|
||||
"just -1" error shape). */
|
||||
long userRmdir(const char* path);
|
||||
|
||||
/* Real chmod(2): overwrites `path`'s whole owner-rwx mode (fs.h's
|
||||
fsModeOwner*, e.g. 0644) with `mode` -- no symbolic +x-style merge,
|
||||
matching real chmod's own numeric-mode form. 0 on success, -1 for a
|
||||
missing path or (tmpfs only) a node under a non-writable mount, same
|
||||
restriction userUnlink/userRmdir already have on the ustar-loaded
|
||||
backing store. Takes effect immediately for any FUTURE userOpen/
|
||||
userSpawn/userExec on this path -- an fd already open, or a process
|
||||
already running, is never affected (real Unix only checks permission at
|
||||
open/exec time). */
|
||||
long userChmod(const char* path, unsigned int mode);
|
||||
|
||||
/* Real chown(2) (vfs.h's vfsChown): reassigns `path`'s owner uid outright.
|
||||
Root-only (this process's own userGetuid() must be 0), full stop, unlike
|
||||
userChmod -- real Unix's _POSIX_CHOWN_RESTRICTED default means not even a
|
||||
file's own owner may give it away. -1 for a missing path, a node under a
|
||||
non-writable mount, or a non-root caller; the target's uid is left
|
||||
completely unchanged on any failure. */
|
||||
long userChown(const char* path, unsigned int uid);
|
||||
|
||||
/* Real getuid(2)/setuid(2) (sched.h's Thread.uid, currentThreadUid/
|
||||
currentThreadSetUid): userGetuid always succeeds, returning THIS
|
||||
process's own current uid. userSetuid enforces the real privilege rule
|
||||
itself (no additional check happens at the syscall layer) -- 0 ("root")
|
||||
may become any uid; any other uid may only pass its own current value
|
||||
back in (a no-op that still returns 0, matching real setuid(getuid())),
|
||||
and any other target returns -1 with the credential unchanged. There is
|
||||
no way back to uid 0 once dropped short of already being uid 0 at the
|
||||
time of the call -- a real, permanent privilege drop for the lifetime of
|
||||
this process, exactly like a daemon that setuid()s away from root once
|
||||
at startup. */
|
||||
long userGetuid(void);
|
||||
long userSetuid(unsigned int uid);
|
||||
|
||||
/* Real getgid(2)/setgid(2) (sched.h's Thread.gid, currentThreadGid/
|
||||
currentThreadSetGid): userGetgid always succeeds, returning THIS
|
||||
process's own current gid. userSetgid's privilege gate is THIS process's
|
||||
own userGetuid() (== 0), not its gid -- matching real setgid(2)'s
|
||||
CAP_SETGID-shaped rule -- any non-root caller may only pass its own
|
||||
current gid back in (a no-op that still returns 0), any other target
|
||||
returns -1 unchanged. */
|
||||
long userGetgid(void);
|
||||
long userSetgid(unsigned int gid);
|
||||
|
||||
/* Real chgrp(2) (vfs.h's vfsChgrp): reassigns `path`'s group gid. Unlike
|
||||
userChown (root-only, full stop), the file's own owner may also call
|
||||
this -- but only to set the group to THIS process's own current gid
|
||||
(userGetgid()), never an arbitrary one, since this kernel tracks exactly
|
||||
one gid per process (no supplementary-groups list). Root may set any
|
||||
gid. -1 for a missing path, a node under a non-writable mount, a
|
||||
non-owner non-root caller, or a non-root caller naming any gid other
|
||||
than its own. */
|
||||
long userChgrp(const char* path, unsigned int gid);
|
||||
|
||||
/* Real chdir(2)/getcwd(2) (sched.h's Thread.cwd, vfs.h's vfsChdir/
|
||||
vfsGetcwd): userChdir resolves a relative `path` against THIS process's
|
||||
own current cwd, requires the result to already be a real directory, and
|
||||
makes it this process's new cwd -- 0 on success, -1 (nothing changed) if
|
||||
it does not name a directory. A child spawned by userSpawn/userExec
|
||||
AFTER this call inherits the new value; this process's parent, and any
|
||||
already-spawned sibling, are never affected (each process's cwd is an
|
||||
independent copy, not shared state). userGetcwd copies the current
|
||||
value into `buffer` (nul-terminated) and returns its length, or -1
|
||||
(buffer untouched) if it does not fit `cap` -- real getcwd(2)'s own
|
||||
ERANGE case. */
|
||||
long userChdir(const char* path);
|
||||
long userGetcwd(char* buffer, unsigned long cap);
|
||||
|
||||
/* Real getenv(3)/setenv(3) (sched.h's Thread.env, syscall.c's sysGetEnv/
|
||||
sysSetEnv): userGetEnv copies THIS process's own value for `name` into
|
||||
`buffer` (nul-terminated) and returns its length, or -1 (buffer
|
||||
untouched) if `name` has no entry or the value does not fit `cap`.
|
||||
userSetEnv overwrites an existing entry with a matching `name`, or
|
||||
creates a new one, in THIS process's own table only -- 0 on success, -1
|
||||
if the combined "NAME=VALUE" would not fit the kernel's fixed entry size
|
||||
or the table is already full. A child spawned by userSpawn/userExec
|
||||
AFTER a userSetEnv call inherits the new value (a real, independent
|
||||
copy, same as userChdir above); this process's parent, and any
|
||||
already-spawned sibling, are never affected. */
|
||||
long userGetEnv(const char* name, char* buffer, unsigned long cap);
|
||||
long userSetEnv(const char* name, const char* value);
|
||||
|
||||
/* Real unsetenv(3): removes THIS process's own entry named `name` from its
|
||||
table, if any, so a following userGetEnv on the same name returns -1
|
||||
again -- 0 whether or not an entry actually existed (matching real
|
||||
unsetenv(3), a no-op success either way), never touches the parent's or
|
||||
any sibling's own table (same independent-copy shape as userSetEnv). */
|
||||
long userUnsetEnv(const char* name);
|
||||
|
||||
long userPipe(int fds[2]);
|
||||
|
||||
/* Shared pipes: unlike userPipe (both ends always land in the caller's own
|
||||
fd table), userPipeCreate returns a raw handle number that means nothing
|
||||
until an fd table actually attaches to it (userPipeAttach) -- meant to be
|
||||
handed to a different, independently spawned process (e.g. as an argv
|
||||
string) so the two can share a pipe without needing fork(). Once
|
||||
attached, userRead/userWrite on that fd really block (park the whole
|
||||
process, not busy-retry) instead of returning -1 for "try again" the way
|
||||
an unshared userPipe end does. */
|
||||
long userPipeCreate(void);
|
||||
long userPipeAttach(long handle, int wantWrite);
|
||||
long userSymlink(const char* target, const char* path);
|
||||
long userReadlink(const char* path, char* buffer, unsigned long cap);
|
||||
long userLink(const char* existingPath, const char* newPath);
|
||||
|
||||
/* Named pipe: creates a fifo at `path` (see vfsMkfifo, vfs.h). Unlike
|
||||
userPipeCreate/userPipeAttach, which need a raw handle number passed to
|
||||
both ends (e.g. as an argv string, see fifomaster.c), any two processes
|
||||
that simply agree on a path can rendezvous by both calling userOpen on it
|
||||
-- oWriteOnly for a blocking write end, oReadOnly for a blocking read
|
||||
end. 0 on success, -1 if the path already exists or its parent is not a
|
||||
writable directory. */
|
||||
long userMkfifo(const char* path);
|
||||
/* argv is a NULL-terminated array of C strings, exactly like execv(3)'s --
|
||||
pass nullptr for "no arguments" (argc 0 on the other side). */
|
||||
long userExec(const char* path, char* const argv[]);
|
||||
long userSpawn(const char* path, char* const argv[]);
|
||||
long userWait(long pid);
|
||||
|
||||
/* waitpid(-1)-style "wait for ANY child, not one specific pid": reaps
|
||||
whichever child of the caller happens to already be dead (or blocks, the
|
||||
same real blocking semantics as userWait(pid), until one dies) and
|
||||
returns its exit code. Writes the reaped child's own pid to *outPid
|
||||
(ignored if NULL). Returns -1 immediately, without blocking, if the
|
||||
caller currently has no children at all, dead or alive -- the real
|
||||
waitpid(-1)'s ECHILD case. See user/waitanytest.c for a live demo. */
|
||||
long userWaitAny(long* outPid);
|
||||
|
||||
/* Unconditionally terminates another process by pid -- the only way to
|
||||
recover a process genuinely stuck forever (e.g. blocked in userRead on a
|
||||
shared pipe whose writer never shows up, see userPipeAttach). 0 on
|
||||
success, -1 for an invalid pid, an already-dead one, or your own. The
|
||||
killed process gets no chance to run any cleanup -- its parent's
|
||||
userWait still reports its exit status, which will be exactly 9. Unlike
|
||||
userSigSend below, this can never be caught, blocked, or ignored --
|
||||
internally it goes straight to threadKill (sched.c), the same as
|
||||
userSigSend(pid, sigKill) does (signal.h).
|
||||
|
||||
Real kill(2)'s own pid<=0 convention (sched.h's Thread.pgid) also
|
||||
applies here: pid==0 kills every OTHER member of the caller's own
|
||||
process group (never the caller itself -- a Running thread is never one
|
||||
of threadKill's accepted states, the same reason plain self-targeting
|
||||
already does nothing special); pid<0 kills every member of group -pid.
|
||||
0 is still returned for success as long as AT LEAST ONE target thread
|
||||
was actually killed (real kill(2)'s own ESRCH-if-nobody-matched
|
||||
semantics for the group form). */
|
||||
long userKill(long pid);
|
||||
|
||||
/* Real, catchable signal delivery (signal.h) -- see that header for the
|
||||
full design (a shared trampoline page, delivery on return-to-ring3,
|
||||
sysSigReturn to resume exactly where a handler interrupted). */
|
||||
|
||||
/* Raises signal `signum` (signal.h's sigHup..sigTstp) against `pid`. 0 on
|
||||
success, -1 for a bad signum or an invalid/dead/unused target. sigKill
|
||||
is delegated straight to the same unconditional path userKill uses --
|
||||
it is never caught, blocked, or ignored, matching real kill(2). Any
|
||||
other signal is only actually delivered once the target next returns to
|
||||
user code on its own (this kernel does not wake a sleeping/blocked
|
||||
target early -- see signal.h's file comment).
|
||||
|
||||
Same pid<=0 group convention as userKill above: pid==0 targets the
|
||||
caller's own process group (INCLUDING itself, unlike userKill's group
|
||||
form -- sigRaiseInfo has no "never self" exclusion the way threadKill
|
||||
does), pid<0 targets group -pid. See sched.h's Thread.pgid and
|
||||
signal.h's sigRaiseGroup for the full design, and user/pgroupfg.c/
|
||||
user/pgroupbg.c for a live demo of the SAME scoping rule applied by
|
||||
keyboard.c's own real Ctrl-C. */
|
||||
long userSigSend(long pid, int signum);
|
||||
void print(const char* text);
|
||||
void printDec(long value);
|
||||
unsigned long stringLength(const char* text);
|
||||
|
||||
Reference in new issue
Block a user