Pseudo-terminals, and what tmux and htop needed around them
- /dev/ptmx and /dev/pts/N: Unix98 pairs with a full line discipline, window sizes, job control and hangup (kernel/dev/pty.c). - The console speaks scroll regions, insert/delete line, SU/SD, RI/IND/NEL, DECSC/DECRC; swallows charset designations and private CSI sequences; and LF only returns the carriage under ONLCR. - setsid() drops the controlling terminal, so a pane's pty becomes its ctty. - pipe2() honours O_NONBLOCK and O_CLOEXEC (libevent's signal pipe). - File modes are stored and enforced: chmod, fchmod(at), umask, creation modes; /tmp is 1777. - /proc/stat has real user/system/idle; /proc/<pid>/task; kthread flag. - SO_PEERCRED, IOV_MAX 1024. - /proc/strace: name a program and its syscalls go to the kernel log. - World gains htop, btop, tmux, mc, ncdu, fastfetch, games, X toys, NetSurf. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
parent
c65bfa3fa3
commit
fd9d392416
28 changed files with 1577 additions and 47 deletions
|
|
@ -50,6 +50,26 @@ fetchstr(addr_t addr, char **pp)
|
|||
return len;
|
||||
}
|
||||
|
||||
// /proc/strace. Which argument of a Linux syscall is a path, by number, so
|
||||
// the trace can show the name rather than an address: that is most of what
|
||||
// one wants from a trace of a program that will not start.
|
||||
static int
|
||||
path_arg(uint64 nr)
|
||||
{
|
||||
switch (nr) {
|
||||
case 2: case 4: case 6: case 21: case 59: case 76: case 80: case 82:
|
||||
case 83: case 84: case 85: case 86: case 87: case 88: case 89: case 90:
|
||||
case 92: case 137: case 161:
|
||||
return 0;
|
||||
case 257: case 258: case 260: case 262: case 263: case 264: case 267:
|
||||
case 268: case 269: case 280: case 316: case 332: case 437: case 439:
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void strace_call(struct trapframe *tf);
|
||||
|
||||
static addr_t
|
||||
fetcharg(int n)
|
||||
{
|
||||
|
|
@ -117,6 +137,10 @@ static addr_t (*syscalls[NSYSCALLS])(void) = {
|
|||
void
|
||||
syscall(struct trapframe *tf)
|
||||
{
|
||||
if (myproc()->strace) {
|
||||
strace_call(tf);
|
||||
return;
|
||||
}
|
||||
kstack_check_canary("syscall entry");
|
||||
myproc()->tf = tf;
|
||||
uint64 orig = myproc()->tf->rax;
|
||||
|
|
@ -139,3 +163,63 @@ syscall(struct trapframe *tf)
|
|||
|
||||
kstack_check_canary("syscall exit");
|
||||
}
|
||||
|
||||
// syscall() for a traced process: the same dispatch, with a line in the log
|
||||
// for each call -- the Linux number, four arguments, a path where there is
|
||||
// one, and what came back. The path is copied before the call runs, since
|
||||
// execve replaces the memory it is in.
|
||||
static void
|
||||
strace_call(struct trapframe *tf)
|
||||
{
|
||||
struct proc *p = myproc();
|
||||
uint64 orig = tf->rax, num = orig;
|
||||
uint64 a0 = tf->rdi, a1 = tf->rsi, a2 = tf->rdx, a3 = tf->r10;
|
||||
char path[64];
|
||||
int pa = p->syscall_map ? path_arg(orig) : -1;
|
||||
|
||||
path[0] = 0;
|
||||
if (pa >= 0) {
|
||||
addr_t u = pa == 0 ? a0 : a1;
|
||||
int i;
|
||||
for (i = 0; i < (int)sizeof(path) - 1; i++) {
|
||||
char c;
|
||||
if (copy_from_user(&c, u + i, 1) < 0 || c == 0)
|
||||
break;
|
||||
path[i] = c;
|
||||
}
|
||||
path[i] = 0;
|
||||
}
|
||||
|
||||
// The result line is written when the call returns, so a call that never
|
||||
// does would leave no trace at all. Everything that can wait gets a line
|
||||
// on the way in as well.
|
||||
switch (orig) {
|
||||
case 0: case 2: case 7: case 23: case 35: case 43: case 45: case 47:
|
||||
case 59: case 61: case 202: case 232: case 257: case 270: case 271:
|
||||
case 281: case 288:
|
||||
if (p->syscall_map)
|
||||
kinfo("strace %d %s: %d(%lx, %lx, %lx, %lx)%s%s%s ...\n",
|
||||
p->pid, p->name, (int)orig, a0, a1, a2, a3,
|
||||
pa >= 0 ? " \"" : "", path, pa >= 0 ? "\"" : "");
|
||||
}
|
||||
|
||||
kstack_check_canary("syscall entry");
|
||||
p->tf = tf;
|
||||
if (p->syscall_map)
|
||||
num = (num < (uint64)p->map_size) ? p->syscall_map[num] : 0;
|
||||
if (num > 0 && num < NSYSCALLS && syscalls[num])
|
||||
tf->rax = syscalls[num]();
|
||||
else
|
||||
tf->rax = -ENOSYS;
|
||||
|
||||
// A successful execve has replaced the program: its name is the new one.
|
||||
kinfo("strace %d %s: %s%d(%lx, %lx, %lx, %lx)%s%s%s = %ld\n",
|
||||
p->pid, p->name, p->syscall_map ? "" : "native ", (int)orig,
|
||||
a0, a1, a2, a3, pa >= 0 ? " \"" : "", path, pa >= 0 ? "\"" : "",
|
||||
(long)tf->rax);
|
||||
|
||||
if (p->killed)
|
||||
exit(W_EXITCODE(0, p->killsig ? p->killsig : SIGKILL));
|
||||
deliver_signals(tf);
|
||||
kstack_check_canary("syscall exit");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue