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
2
Makefile
2
Makefile
|
|
@ -26,7 +26,7 @@ KERN_CORE = $(addprefix $(BUILD)/kernel/core/, \
|
||||||
KERN_DEV = $(addprefix $(BUILD)/kernel/dev/, \
|
KERN_DEV = $(addprefix $(BUILD)/kernel/dev/, \
|
||||||
bio.o blkdev.o cdev.o device.o disk.o fb.o ide.o input.o irq.o kbd.o keymap.o mouse.o \
|
bio.o blkdev.o cdev.o device.o disk.o fb.o ide.o input.o irq.o kbd.o keymap.o mouse.o \
|
||||||
pci.o pciuser.o text.o ttf.o uart.o gfx.o vt.o ramdisk.o procfs.o \
|
pci.o pciuser.o text.o ttf.o uart.o gfx.o vt.o ramdisk.o procfs.o \
|
||||||
dbg.o netuser.o audio.o dmi.o)
|
dbg.o netuser.o audio.o dmi.o pty.o)
|
||||||
|
|
||||||
# Drivers for hardware that has to be *found* rather than assumed. Anything
|
# Drivers for hardware that has to be *found* rather than assumed. Anything
|
||||||
# discovered by walking a bus belongs here; the fixed-address legacy devices
|
# discovered by walking a bus belongs here; the fixed-address legacy devices
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,10 @@ int pciuser_mmio_ok(uint64 phys, uint64 len);
|
||||||
void pciuser_release_all(struct proc*);
|
void pciuser_release_all(struct proc*);
|
||||||
int pciuser_owner_pid(int idx);
|
int pciuser_owner_pid(int idx);
|
||||||
int proc_reincarnate(int status);
|
int proc_reincarnate(int status);
|
||||||
|
void proc_cputime(uint64 *user, uint64 *sys);
|
||||||
|
// kernel/dev/procfs.c: /proc/strace
|
||||||
|
int strace_wanted(const char *name);
|
||||||
|
int proc_write_strace(int user_src, uint64 src, uint n);
|
||||||
void proc_kill_init(void);
|
void proc_kill_init(void);
|
||||||
extern uint init_generation;
|
extern uint init_generation;
|
||||||
void cprintf(char*, ...);
|
void cprintf(char*, ...);
|
||||||
|
|
@ -512,6 +516,7 @@ int socksendto(struct socket*, char *buf, int n,
|
||||||
int sockbind_in(struct socket*, uint addr, ushort port);
|
int sockbind_in(struct socket*, uint addr, ushort port);
|
||||||
int sockconnect_in(struct socket*, uint addr, ushort port);
|
int sockconnect_in(struct socket*, uint addr, ushort port);
|
||||||
int sock_domain(struct socket*);
|
int sock_domain(struct socket*);
|
||||||
|
int sock_peercred(struct socket*, int*, uint*, uint*);
|
||||||
// Socket options. The syscall layer does the user-memory marshalling and these
|
// Socket options. The syscall layer does the user-memory marshalling and these
|
||||||
// do the work, because struct socket's fields live in kernel/net/unix.c and the
|
// do the work, because struct socket's fields live in kernel/net/unix.c and the
|
||||||
// syscall layer deliberately cannot see them.
|
// syscall layer deliberately cannot see them.
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
#define NOFILE 128 // open files per process
|
#define NOFILE 128 // open files per process
|
||||||
#define NFILE 512 // open files per system
|
#define NFILE 512 // open files per system
|
||||||
#define NINODE 200 // maximum number of active i-nodes
|
#define NINODE 200 // maximum number of active i-nodes
|
||||||
#define NDEV 13 // maximum major device number
|
#define NDEV 16 // maximum major device number
|
||||||
#define RAMDISK_DEV 2 // memory-backed block device (kernel/dev/ramdisk.c)
|
#define RAMDISK_DEV 2 // memory-backed block device (kernel/dev/ramdisk.c)
|
||||||
#define USBDISK_DEV 3 // USB mass storage (kernel/drivers/usb_storage.c)
|
#define USBDISK_DEV 3 // USB mass storage (kernel/drivers/usb_storage.c)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -174,6 +174,7 @@ struct procinfo {
|
||||||
uint64 nvcsw, nivcsw; // voluntary / involuntary context switches
|
uint64 nvcsw, nivcsw; // voluntary / involuntary context switches
|
||||||
char name[16];
|
char name[16];
|
||||||
char exe[64]; // full path, for /proc/<pid>/exe
|
char exe[64]; // full path, for /proc/<pid>/exe
|
||||||
|
int kthread; // a kernel thread, not a program
|
||||||
};
|
};
|
||||||
|
|
||||||
struct proc {
|
struct proc {
|
||||||
|
|
@ -321,6 +322,14 @@ struct proc {
|
||||||
// chroot(2): where an absolute path starts, and where ".." stops. 0 is the
|
// chroot(2): where an absolute path starts, and where ".." stops. 0 is the
|
||||||
// system's root. Inherited across fork, held as a reference like cwd.
|
// system's root. Inherited across fork, held as a reference like cwd.
|
||||||
struct inode *root;
|
struct inode *root;
|
||||||
|
|
||||||
|
// Syscall tracing: every call and its result go to the log. Set by exec
|
||||||
|
// when the program's name is the one written to /proc/strace, and kept
|
||||||
|
// across fork so that what a traced program starts is traced too.
|
||||||
|
int strace;
|
||||||
|
|
||||||
|
// umask(2). Inherited across fork; 022 for the first process.
|
||||||
|
uint umask;
|
||||||
};
|
};
|
||||||
#define FD_CLOEXEC 1
|
#define FD_CLOEXEC 1
|
||||||
|
|
||||||
|
|
|
||||||
41
include/dev/pty.h
Normal file
41
include/dev/pty.h
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Pseudo-terminals, Unix98 style: open /dev/ptmx and get the master side of a
|
||||||
|
// new pair; the slave is /dev/pts/N. What sits between the two is the same
|
||||||
|
// line discipline a console has. See kernel/dev/pty.c.
|
||||||
|
|
||||||
|
#include "core/types.h"
|
||||||
|
|
||||||
|
#define PTMX_MAJOR 13 // /dev/ptmx: opening it allocates a pair
|
||||||
|
#define PTM_MAJOR 14 // a master, minor = pair number; never in a listing
|
||||||
|
#define PTS_MAJOR 15 // /dev/pts/N
|
||||||
|
#define NPTY 32
|
||||||
|
|
||||||
|
// A controlling terminal that is a pty, in proc->ctty: PTY_CTTY + N. The
|
||||||
|
// console's are 1..NVT.
|
||||||
|
#define PTY_CTTY 64
|
||||||
|
|
||||||
|
struct inode;
|
||||||
|
struct file;
|
||||||
|
|
||||||
|
int pty_alloc(void); // the new pair's number, or -1
|
||||||
|
int pty_used(int n);
|
||||||
|
int pty_ioctl(struct inode *ip, uint request, addr_t arg);
|
||||||
|
int pty_poll(struct inode *ip, int events, int readable, int writable);
|
||||||
|
struct inode *ptmx_open(struct inode *ip); // ip locked; returns the master, locked
|
||||||
|
void pty_init(void);
|
||||||
|
|
||||||
|
// Lines the devsw table needs.
|
||||||
|
int ptm_read(struct inode*, uint64, char*, int);
|
||||||
|
int ptm_write(struct inode*, uint64, char*, int);
|
||||||
|
int ptm_ready(struct inode*);
|
||||||
|
void ptm_open(struct inode*);
|
||||||
|
void ptm_close(struct inode*);
|
||||||
|
int pts_read(struct inode*, uint64, char*, int);
|
||||||
|
int pts_write(struct inode*, uint64, char*, int);
|
||||||
|
int pts_ready(struct inode*);
|
||||||
|
void pts_open(struct inode*);
|
||||||
|
void pts_close(struct inode*);
|
||||||
|
|
||||||
|
// From kernel/fs/devfs.c: the inode of master N.
|
||||||
|
struct inode *devfs_pty_master(int n);
|
||||||
|
|
@ -82,7 +82,7 @@ struct vtcell {
|
||||||
// Escape parser states. E_OSC and E_OSC_ESC exist for hyperlinks: a terminal
|
// Escape parser states. E_OSC and E_OSC_ESC exist for hyperlinks: a terminal
|
||||||
// that does not follow OSC 8 must still consume it, or the URL is printed as
|
// that does not follow OSC 8 must still consume it, or the URL is printed as
|
||||||
// text. See the comment in kernel/core/console.c that this came from.
|
// text. See the comment in kernel/core/console.c that this came from.
|
||||||
enum { VE_NORMAL, VE_ESC, VE_CSI, VE_OSC, VE_OSC_ESC };
|
enum { VE_NORMAL, VE_ESC, VE_CSI, VE_OSC, VE_OSC_ESC, VE_CHARSET };
|
||||||
|
|
||||||
struct vt {
|
struct vt {
|
||||||
int index; // 0-based; /dev/tty<index+1>
|
int index; // 0-based; /dev/tty<index+1>
|
||||||
|
|
@ -98,6 +98,10 @@ struct vt {
|
||||||
// CR, LF, backspace, an escape sequence -- clears it.
|
// CR, LF, backspace, an escape sequence -- clears it.
|
||||||
int wrap_pending;
|
int wrap_pending;
|
||||||
int saved_cx, saved_cy; // DECSC/DECRC
|
int saved_cx, saved_cy; // DECSC/DECRC
|
||||||
|
// The scrolling region, DECSTBM: rows scroll_top..scroll_bot inclusive.
|
||||||
|
// scroll_bot 0 means unset, the whole screen -- which keeps a grid resized
|
||||||
|
// underneath it from holding a stale bottom.
|
||||||
|
int scroll_top, scroll_bot;
|
||||||
|
|
||||||
uint8 fg, bg; // current SGR colours
|
uint8 fg, bg; // current SGR colours
|
||||||
int bold;
|
int bold;
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,10 @@ struct inode {
|
||||||
uint64 size;
|
uint64 size;
|
||||||
|
|
||||||
uint rwmode;
|
uint rwmode;
|
||||||
|
// POSIX permission bits from a filesystem that keeps them, or 0 for one
|
||||||
|
// that does not, in which case stat makes some up from the type. Bit 18 set
|
||||||
|
// alone means a mode of 0000, which chmod can ask for.
|
||||||
|
uint mode;
|
||||||
uint supermode;
|
uint supermode;
|
||||||
uint showmode;
|
uint showmode;
|
||||||
uint atime;
|
uint atime;
|
||||||
|
|
|
||||||
|
|
@ -355,6 +355,12 @@ struct socket {
|
||||||
// the only side that can say when they have been delivered.
|
// the only side that can say when they have been delivered.
|
||||||
struct scm_fds scm[NSCM];
|
struct scm_fds scm[NSCM];
|
||||||
int nscm;
|
int nscm;
|
||||||
|
|
||||||
|
// Who is at this end, for SO_PEERCRED on the other: the process that made
|
||||||
|
// the socket, or for the server's end of a connection, the one that made
|
||||||
|
// the listener -- which is what Linux records too.
|
||||||
|
int cred_pid;
|
||||||
|
uint cred_uid, cred_gid;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ int boot_debug;
|
||||||
// the VBE info block, not 0x7000 (startothers() below uses that address for
|
// the VBE info block, not 0x7000 (startothers() below uses that address for
|
||||||
// the AP boot trampoline).
|
// the AP boot trampoline).
|
||||||
#include "arch/schboot.h"
|
#include "arch/schboot.h"
|
||||||
|
#include "dev/pty.h"
|
||||||
|
|
||||||
// Bootstrap processor starts running C code here.
|
// Bootstrap processor starts running C code here.
|
||||||
// Allocate a real stack and switch to it, first
|
// Allocate a real stack and switch to it, first
|
||||||
|
|
@ -269,6 +270,7 @@ kmain(uint64 fb_addr)
|
||||||
// Before the keyboard and mouse, which start producing events the moment
|
// Before the keyboard and mouse, which start producing events the moment
|
||||||
// their handlers are registered.
|
// their handlers are registered.
|
||||||
inputinit();
|
inputinit();
|
||||||
|
pty_init();
|
||||||
if (boot_verbose) cprintf("verbose boot: inputinit done\n");
|
if (boot_verbose) cprintf("verbose boot: inputinit done\n");
|
||||||
// Before consoleinit, because the console writes to a terminal and there
|
// Before consoleinit, because the console writes to a terminal and there
|
||||||
// has to be one. vt_init reads the framebuffer geometry, so it also has to
|
// has to be one. vt_init reads the framebuffer geometry, so it also has to
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
#include "fs/file.h"
|
#include "fs/file.h"
|
||||||
#include "arch/x86.h"
|
#include "arch/x86.h"
|
||||||
#include "uapi/errno.h"
|
#include "uapi/errno.h"
|
||||||
|
#include "dev/pty.h"
|
||||||
|
|
||||||
extern int consoleread(struct inode*, uint64, char*, int);
|
extern int consoleread(struct inode*, uint64, char*, int);
|
||||||
extern int consolewrite(struct inode*, uint64, char*, int);
|
extern int consolewrite(struct inode*, uint64, char*, int);
|
||||||
|
|
@ -216,4 +217,10 @@ struct devsw devsw[] = {
|
||||||
// The close hook plays out what is still queued: a program that writes a
|
// The close hook plays out what is still queued: a program that writes a
|
||||||
// sound and exits expects to hear the end of it.
|
// sound and exits expects to hear the end of it.
|
||||||
[12] = { dspread, dspwrite, 0, dspclose },
|
[12] = { dspread, dspwrite, 0, dspclose },
|
||||||
|
// Pseudo-terminals, kernel/dev/pty.c. /dev/ptmx (13) is never read or
|
||||||
|
// written: open() swaps it for a new master (14) before it gets the chance.
|
||||||
|
// The slaves (15) are /dev/pts/N.
|
||||||
|
[PTMX_MAJOR] = { 0, 0 },
|
||||||
|
[PTM_MAJOR] = { ptm_read, ptm_write, ptm_ready, ptm_close, ptm_open },
|
||||||
|
[PTS_MAJOR] = { pts_read, pts_write, pts_ready, pts_close, pts_open },
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -194,16 +194,36 @@ static void
|
||||||
gen_stat(struct sbuf *sb)
|
gen_stat(struct sbuf *sb)
|
||||||
{
|
{
|
||||||
// USER_HZ, like times() -- everything that parses /proc/stat divides by 100.
|
// USER_HZ, like times() -- everything that parses /proc/stat divides by 100.
|
||||||
uint t = (uint)((uint64)uptime() * USER_HZ / HZ);
|
//
|
||||||
|
// Busy is what processes were charged; idle is the rest of every online
|
||||||
|
// processor's time. Calling it all user time, as this did, is a machine at
|
||||||
|
// 100% forever in htop and top. The charge is not kept per processor, so
|
||||||
|
// each cpuN line is an equal share of the total -- right on average, which
|
||||||
|
// is all a meter can show anyway.
|
||||||
|
uint64 n = ncpu_online > 0 ? ncpu_online : 1;
|
||||||
|
uint64 all = (uint64)uptime() * USER_HZ / HZ * n;
|
||||||
|
uint64 user, sys;
|
||||||
|
proc_cputime(&user, &sys);
|
||||||
|
user = user * USER_HZ / HZ;
|
||||||
|
sys = sys * USER_HZ / HZ;
|
||||||
|
if (user + sys > all)
|
||||||
|
all = user + sys;
|
||||||
|
uint64 idle = all - user - sys;
|
||||||
|
|
||||||
|
// user nice system idle iowait irq softirq steal guest guest_nice
|
||||||
sb_puts(sb, "cpu ");
|
sb_puts(sb, "cpu ");
|
||||||
sb_putu(sb, t); sb_puts(sb, " 0 0 0 0 0 0 0 0 0\n");
|
sb_putu(sb, user); sb_puts(sb, " 0 ");
|
||||||
|
sb_putu(sb, sys); sb_putc(sb, ' ');
|
||||||
|
sb_putu(sb, idle); sb_puts(sb, " 0 0 0 0 0 0\n");
|
||||||
// Online, for the same reason as cpuinfo: top and friends count the cpuN
|
// Online, for the same reason as cpuinfo: top and friends count the cpuN
|
||||||
// lines here to decide how many cores to draw.
|
// lines here to decide how many cores to draw.
|
||||||
for (int i = 0; i < ncpu_online; i++) {
|
for (int i = 0; i < ncpu_online; i++) {
|
||||||
sb_puts(sb, "cpu");
|
sb_puts(sb, "cpu");
|
||||||
sb_putu(sb, i);
|
sb_putu(sb, i);
|
||||||
sb_putc(sb, ' ');
|
sb_putc(sb, ' ');
|
||||||
sb_putu(sb, t); sb_puts(sb, " 0 0 0 0 0 0 0 0 0\n");
|
sb_putu(sb, user / n); sb_puts(sb, " 0 ");
|
||||||
|
sb_putu(sb, sys / n); sb_putc(sb, ' ');
|
||||||
|
sb_putu(sb, idle / n); sb_puts(sb, " 0 0 0 0 0 0\n");
|
||||||
}
|
}
|
||||||
sb_kv(sb, "processes ", (uint64)proc_num(), 0);
|
sb_kv(sb, "processes ", (uint64)proc_num(), 0);
|
||||||
sb_kv(sb, "procs_running ", 1, 0);
|
sb_kv(sb, "procs_running ", 1, 0);
|
||||||
|
|
@ -388,12 +408,42 @@ gen_filesystems(struct sbuf *sb)
|
||||||
#define PROC_BOOTDEBUG 30
|
#define PROC_BOOTDEBUG 30
|
||||||
#define PROC_SLAB 20
|
#define PROC_SLAB 20
|
||||||
#define PROC_PARTITIONS 21
|
#define PROC_PARTITIONS 21
|
||||||
|
#define PROC_STRACE 31
|
||||||
|
|
||||||
// Render global file `id` (a PROC_* constant) into `buf`. Returns its length.
|
// Render global file `id` (a PROC_* constant) into `buf`. Returns its length.
|
||||||
//
|
//
|
||||||
// Split out of procread() so the /proc *filesystem* (kernel/fs/procfs.c) can
|
// Split out of procread() so the /proc *filesystem* (kernel/fs/procfs.c) can
|
||||||
// use the same generators: that file owns the namespace, this one owns the
|
// use the same generators: that file owns the namespace, this one owns the
|
||||||
// content, and they have no other business with each other.
|
// content, and they have no other business with each other.
|
||||||
|
// /proc/strace: the name of the program to trace, or empty. Read by exec, so
|
||||||
|
// a change takes effect at the next program started, not in running ones.
|
||||||
|
static char strace_name[16];
|
||||||
|
|
||||||
|
int
|
||||||
|
strace_wanted(const char *name)
|
||||||
|
{
|
||||||
|
return strace_name[0] && strncmp(name, strace_name, sizeof(strace_name)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
proc_write_strace(int user_src, uint64 src, uint n)
|
||||||
|
{
|
||||||
|
char tmp[16];
|
||||||
|
uint len = n < sizeof(tmp) - 1 ? n : sizeof(tmp) - 1;
|
||||||
|
|
||||||
|
if (user_src) {
|
||||||
|
if (copy_from_user(tmp, src, len) < 0)
|
||||||
|
return -EFAULT;
|
||||||
|
} else {
|
||||||
|
memmove(tmp, (char *)src, len);
|
||||||
|
}
|
||||||
|
while (len > 0 && (tmp[len - 1] == '\n' || tmp[len - 1] == ' '))
|
||||||
|
len--;
|
||||||
|
tmp[len] = 0;
|
||||||
|
memmove(strace_name, tmp, sizeof(strace_name));
|
||||||
|
return (int)n;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
proc_gen_global(int id, char *buf, int cap)
|
proc_gen_global(int id, char *buf, int cap)
|
||||||
{
|
{
|
||||||
|
|
@ -442,6 +492,10 @@ proc_gen_global(int id, char *buf, int cap)
|
||||||
sb_puts(&sb, boot_debug ? "on\n" : "off\n");
|
sb_puts(&sb, boot_debug ? "on\n" : "off\n");
|
||||||
break;
|
break;
|
||||||
case PROC_DEVICES: gen_devices(&sb); break;
|
case PROC_DEVICES: gen_devices(&sb); break;
|
||||||
|
case PROC_STRACE:
|
||||||
|
sb_puts(&sb, strace_name);
|
||||||
|
sb_putc(&sb, '\n');
|
||||||
|
break;
|
||||||
// The block layer renders its own table, like PCI and SchFS above.
|
// The block layer renders its own table, like PCI and SchFS above.
|
||||||
case PROC_PARTITIONS: sb.len = blkdev_info(buf, cap); break;
|
case PROC_PARTITIONS: sb.len = blkdev_info(buf, cap); break;
|
||||||
default: return 0;
|
default: return 0;
|
||||||
|
|
|
||||||
709
kernel/dev/pty.c
Normal file
709
kernel/dev/pty.c
Normal file
|
|
@ -0,0 +1,709 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
//
|
||||||
|
// Pseudo-terminals.
|
||||||
|
//
|
||||||
|
// A terminal emulator, tmux, sshd and script(1) all want the same thing: a
|
||||||
|
// terminal with a program on one side and *themselves* on the other, instead of
|
||||||
|
// a keyboard and a screen. Unix98 spells that /dev/ptmx -- each open makes a new
|
||||||
|
// pair and hands back its master -- and /dev/pts/N, the slave, which the program
|
||||||
|
// gets as its stdin, stdout and controlling terminal. There were none of these,
|
||||||
|
// so xterm had nothing to run a shell in and tmux could not start a server.
|
||||||
|
//
|
||||||
|
// Between the two sides sits a line discipline, the console's in miniature
|
||||||
|
// (kernel/core/console.c): what the master writes is the slave's *input* --
|
||||||
|
// canonical editing, echo, ^C as SIGINT to the foreground group -- and what the
|
||||||
|
// slave writes is its *output*, NL turned into CR NL, read back by the master.
|
||||||
|
//
|
||||||
|
// Every pair shares one lock. The paths are short and a second lock per pair
|
||||||
|
// would only be one more thing to take in the wrong order.
|
||||||
|
|
||||||
|
#include "core/types.h"
|
||||||
|
#include "core/defs.h"
|
||||||
|
#include "core/param.h"
|
||||||
|
#include "core/spinlock.h"
|
||||||
|
#include "core/sleeplock.h"
|
||||||
|
#include "core/proc.h"
|
||||||
|
#include "fs/fs.h"
|
||||||
|
#include "fs/file.h"
|
||||||
|
#include "core/uaccess.h"
|
||||||
|
#include "uapi/posix.h"
|
||||||
|
#include "uapi/errno.h"
|
||||||
|
#include "dev/pty.h"
|
||||||
|
|
||||||
|
#define PTY_BUF 4096
|
||||||
|
|
||||||
|
#define TIOCGPTN 0x80045430
|
||||||
|
#define TIOCSPTLCK 0x40045431
|
||||||
|
#define TIOCGPTPEER 0x5441
|
||||||
|
#define TIOCNOTTY 0x5422
|
||||||
|
#define TIOCGSID 0x5429
|
||||||
|
#define TIOCOUTQ 0x5411
|
||||||
|
#define TIOCPKT 0x5420
|
||||||
|
|
||||||
|
#define POLLIN_ 0x001
|
||||||
|
#define POLLOUT_ 0x004
|
||||||
|
#define POLLHUP_ 0x010
|
||||||
|
|
||||||
|
struct pty {
|
||||||
|
int used;
|
||||||
|
int mopen, sopen; // open file descriptions on each side
|
||||||
|
int slave_seen; // the slave has been opened at least once
|
||||||
|
struct termios tio;
|
||||||
|
struct winsize ws;
|
||||||
|
int fg_pgrp, sid;
|
||||||
|
|
||||||
|
// The slave's input, after the line discipline. [r, w) is ready to read;
|
||||||
|
// [w, e) is the line still being edited in canonical mode.
|
||||||
|
char in[PTY_BUF];
|
||||||
|
uint ir, iw, ie;
|
||||||
|
int eof; // ^D on an empty line: the next read returns 0
|
||||||
|
int abort_read; // ^C: wake the reader with EINTR
|
||||||
|
|
||||||
|
// The slave's output, for the master to read.
|
||||||
|
char out[PTY_BUF];
|
||||||
|
uint or, ow;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct spinlock ptylock;
|
||||||
|
static struct pty ptys[NPTY];
|
||||||
|
|
||||||
|
#define CTRL(x) ((x) & 0x1f)
|
||||||
|
|
||||||
|
static void
|
||||||
|
termios_defaults(struct termios *t)
|
||||||
|
{
|
||||||
|
memset(t, 0, sizeof(*t));
|
||||||
|
t->c_iflag = ICRNL | IUTF8;
|
||||||
|
t->c_oflag = OPOST | ONLCR;
|
||||||
|
t->c_cflag = CS8 | CREAD;
|
||||||
|
t->c_lflag = ISIG | ICANON | ECHO | ECHOE | IEXTEN;
|
||||||
|
t->c_cc[VINTR] = CTRL('C');
|
||||||
|
t->c_cc[VQUIT] = CTRL('\\');
|
||||||
|
t->c_cc[VERASE] = 0x7f;
|
||||||
|
t->c_cc[VKILL] = CTRL('U');
|
||||||
|
t->c_cc[VEOF] = CTRL('D');
|
||||||
|
t->c_cc[VSUSP] = CTRL('Z');
|
||||||
|
t->c_cc[VWERASE]= CTRL('W');
|
||||||
|
t->c_cc[VMIN] = 1;
|
||||||
|
t->c_ispeed = 15;
|
||||||
|
t->c_ospeed = 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pty_init(void)
|
||||||
|
{
|
||||||
|
initlock(&ptylock, "pty");
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
pty_alloc(void)
|
||||||
|
{
|
||||||
|
acquire(&ptylock);
|
||||||
|
for (int i = 0; i < NPTY; i++) {
|
||||||
|
struct pty *p = &ptys[i];
|
||||||
|
if (p->used)
|
||||||
|
continue;
|
||||||
|
memset(p, 0, sizeof(*p));
|
||||||
|
p->used = 1;
|
||||||
|
termios_defaults(&p->tio);
|
||||||
|
p->ws.ws_row = 24;
|
||||||
|
p->ws.ws_col = 80;
|
||||||
|
release(&ptylock);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
release(&ptylock);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
pty_used(int n)
|
||||||
|
{
|
||||||
|
return n >= 0 && n < NPTY && ptys[n].used;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct pty *
|
||||||
|
pty_of(struct inode *ip)
|
||||||
|
{
|
||||||
|
if (ip->minor < 0 || ip->minor >= NPTY)
|
||||||
|
return 0;
|
||||||
|
return &ptys[ip->minor];
|
||||||
|
}
|
||||||
|
|
||||||
|
// /dev/ptmx, opened: a new pair, and the master in place of the ptmx inode.
|
||||||
|
// Called with ip locked, as open() holds it; hands back the master the same way.
|
||||||
|
struct inode *
|
||||||
|
ptmx_open(struct inode *ip)
|
||||||
|
{
|
||||||
|
int n = pty_alloc();
|
||||||
|
struct inode *m;
|
||||||
|
|
||||||
|
if (n < 0)
|
||||||
|
return 0;
|
||||||
|
m = devfs_pty_master(n);
|
||||||
|
if (m == 0) {
|
||||||
|
ptys[n].used = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
iunlockput(ip);
|
||||||
|
ilock(m);
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
maybe_free(struct pty *p)
|
||||||
|
{
|
||||||
|
if (p->mopen == 0 && p->sopen == 0)
|
||||||
|
p->used = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- output: the slave writes, the master reads ------------------------------
|
||||||
|
|
||||||
|
static int
|
||||||
|
out_room(struct pty *p)
|
||||||
|
{
|
||||||
|
return PTY_BUF - (int)(p->ow - p->or);
|
||||||
|
}
|
||||||
|
|
||||||
|
// One byte of output, with OPOST processing. Caller holds the lock and has made
|
||||||
|
// sure there is room for two.
|
||||||
|
static void
|
||||||
|
out_put(struct pty *p, int c)
|
||||||
|
{
|
||||||
|
if ((p->tio.c_oflag & OPOST) && (p->tio.c_oflag & ONLCR) && c == '\n')
|
||||||
|
p->out[p->ow++ % PTY_BUF] = '\r';
|
||||||
|
p->out[p->ow++ % PTY_BUF] = (char)c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Echo goes into the same queue, and is dropped rather than waited for when
|
||||||
|
// the master has stopped reading: the writer here is whoever typed, and
|
||||||
|
// blocking it on its own echo would deadlock a master that writes and reads
|
||||||
|
// from one thread.
|
||||||
|
static void
|
||||||
|
echo_put(struct pty *p, int c)
|
||||||
|
{
|
||||||
|
if (out_room(p) < 2)
|
||||||
|
return;
|
||||||
|
out_put(p, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
echo(struct pty *p, int c)
|
||||||
|
{
|
||||||
|
if (c == '\n') {
|
||||||
|
if (p->tio.c_lflag & (ECHO | ECHONL))
|
||||||
|
echo_put(p, '\n');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!(p->tio.c_lflag & ECHO))
|
||||||
|
return;
|
||||||
|
if (c < 32 && c != '\t' && c != '\r') {
|
||||||
|
echo_put(p, '^');
|
||||||
|
echo_put(p, c + '@');
|
||||||
|
} else {
|
||||||
|
echo_put(p, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
erase_one(struct pty *p)
|
||||||
|
{
|
||||||
|
if (p->ie == p->iw)
|
||||||
|
return;
|
||||||
|
p->ie--;
|
||||||
|
if ((p->tio.c_lflag & ECHO) && (p->tio.c_lflag & ECHOE)) {
|
||||||
|
echo_put(p, '\b');
|
||||||
|
echo_put(p, ' ');
|
||||||
|
echo_put(p, '\b');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- input: the master writes, the slave reads -------------------------------
|
||||||
|
|
||||||
|
// One byte through the line discipline. Returns a signal to send to the
|
||||||
|
// foreground group, or 0. Caller holds the lock.
|
||||||
|
static int
|
||||||
|
input_byte(struct pty *p, int c)
|
||||||
|
{
|
||||||
|
struct termios *t = &p->tio;
|
||||||
|
|
||||||
|
if (t->c_iflag & ISTRIP)
|
||||||
|
c &= 0x7f;
|
||||||
|
if (c == '\r') {
|
||||||
|
if (t->c_iflag & IGNCR)
|
||||||
|
return 0;
|
||||||
|
if (t->c_iflag & ICRNL)
|
||||||
|
c = '\n';
|
||||||
|
} else if (c == '\n' && (t->c_iflag & INLCR)) {
|
||||||
|
c = '\r';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t->c_lflag & ISIG) {
|
||||||
|
int sig = 0;
|
||||||
|
if (c == t->c_cc[VINTR]) sig = SIGINT;
|
||||||
|
else if (c == t->c_cc[VQUIT]) sig = SIGQUIT;
|
||||||
|
else if (c == t->c_cc[VSUSP]) sig = SIGTSTP;
|
||||||
|
if (sig && c != 0) {
|
||||||
|
echo(p, c);
|
||||||
|
if (!(t->c_lflag & NOFLSH))
|
||||||
|
p->ie = p->iw = p->ir;
|
||||||
|
p->abort_read = 1;
|
||||||
|
return sig;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t->c_lflag & ICANON) {
|
||||||
|
if (c == t->c_cc[VERASE] || c == CTRL('H')) {
|
||||||
|
erase_one(p);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (c == t->c_cc[VKILL]) {
|
||||||
|
while (p->ie != p->iw && p->in[(p->ie - 1) % PTY_BUF] != '\n')
|
||||||
|
erase_one(p);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (c == t->c_cc[VWERASE] && (t->c_lflag & IEXTEN)) {
|
||||||
|
while (p->ie != p->iw && p->in[(p->ie - 1) % PTY_BUF] == ' ')
|
||||||
|
erase_one(p);
|
||||||
|
while (p->ie != p->iw && p->in[(p->ie - 1) % PTY_BUF] != ' ')
|
||||||
|
erase_one(p);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (c == t->c_cc[VEOF]) {
|
||||||
|
if (p->ie == p->iw)
|
||||||
|
p->eof = 1;
|
||||||
|
p->iw = p->ie;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p->ie - p->ir >= PTY_BUF)
|
||||||
|
return 0; // full: dropped, as a console does
|
||||||
|
p->in[p->ie++ % PTY_BUF] = (char)c;
|
||||||
|
echo(p, c);
|
||||||
|
if (!(t->c_lflag & ICANON) || c == '\n' || p->ie - p->ir >= PTY_BUF)
|
||||||
|
p->iw = p->ie;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ptm_write(struct inode *ip, uint64 off, char *src, int n)
|
||||||
|
{
|
||||||
|
struct pty *p = pty_of(ip);
|
||||||
|
char kbuf[128];
|
||||||
|
int done = 0;
|
||||||
|
|
||||||
|
(void)off;
|
||||||
|
if (p == 0 || !p->used)
|
||||||
|
return -EIO;
|
||||||
|
iunlock(ip);
|
||||||
|
while (done < n) {
|
||||||
|
int chunk = n - done < (int)sizeof(kbuf) ? n - done : (int)sizeof(kbuf);
|
||||||
|
if (copy_from_user(kbuf, (addr_t)(src + done), chunk) < 0) {
|
||||||
|
ilock(ip);
|
||||||
|
return done ? done : -EFAULT;
|
||||||
|
}
|
||||||
|
acquire(&ptylock);
|
||||||
|
for (int i = 0; i < chunk; i++) {
|
||||||
|
// Raw-mode input waits for room rather than vanishing: a paste into an
|
||||||
|
// editor is a burst of thousands of bytes, and dropping the tail of it
|
||||||
|
// is worse than making the writer wait for the reader.
|
||||||
|
while (!(p->tio.c_lflag & ICANON) && p->ie - p->ir >= PTY_BUF &&
|
||||||
|
p->sopen > 0) {
|
||||||
|
if (myproc()->killed) {
|
||||||
|
release(&ptylock);
|
||||||
|
ilock(ip);
|
||||||
|
return done + i ? done + i : -EINTR;
|
||||||
|
}
|
||||||
|
sleep(&p->ie, &ptylock);
|
||||||
|
}
|
||||||
|
int sig = input_byte(p, kbuf[i] & 0xff);
|
||||||
|
if (sig) {
|
||||||
|
int pg = p->fg_pgrp;
|
||||||
|
wakeup(&p->ir);
|
||||||
|
release(&ptylock);
|
||||||
|
if (pg > 0)
|
||||||
|
sigsend_pgrp(pg, sig);
|
||||||
|
acquire(&ptylock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wakeup(&p->ir);
|
||||||
|
wakeup(&p->or); // the echo
|
||||||
|
release(&ptylock);
|
||||||
|
done += chunk;
|
||||||
|
}
|
||||||
|
poll_kick();
|
||||||
|
ilock(ip);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ptm_read(struct inode *ip, uint64 off, char *dst, int n)
|
||||||
|
{
|
||||||
|
struct pty *p = pty_of(ip);
|
||||||
|
char kbuf[256];
|
||||||
|
int got = 0;
|
||||||
|
|
||||||
|
(void)off;
|
||||||
|
if (p == 0 || !p->used)
|
||||||
|
return -EIO;
|
||||||
|
iunlock(ip);
|
||||||
|
acquire(&ptylock);
|
||||||
|
while (p->or == p->ow) {
|
||||||
|
// Every slave descriptor closed, after there was one: the program on the
|
||||||
|
// other side has gone. Linux says EIO, and that is what terminal
|
||||||
|
// emulators and tmux take as "the shell exited".
|
||||||
|
if (p->slave_seen && p->sopen == 0) {
|
||||||
|
release(&ptylock);
|
||||||
|
ilock(ip);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
if (myproc()->killed) {
|
||||||
|
release(&ptylock);
|
||||||
|
ilock(ip);
|
||||||
|
return -EINTR;
|
||||||
|
}
|
||||||
|
sleep(&p->or, &ptylock);
|
||||||
|
}
|
||||||
|
while (got < n && got < (int)sizeof(kbuf) && p->or != p->ow)
|
||||||
|
kbuf[got++] = p->out[p->or++ % PTY_BUF];
|
||||||
|
wakeup(&p->ow);
|
||||||
|
release(&ptylock);
|
||||||
|
poll_kick();
|
||||||
|
ilock(ip);
|
||||||
|
if (copy_to_user((addr_t)dst, kbuf, got) < 0)
|
||||||
|
return -EFAULT;
|
||||||
|
return got;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ptm_ready(struct inode *ip)
|
||||||
|
{
|
||||||
|
struct pty *p = pty_of(ip);
|
||||||
|
return p && (p->or != p->ow || (p->slave_seen && p->sopen == 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ptm_open(struct inode *ip)
|
||||||
|
{
|
||||||
|
struct pty *p = pty_of(ip);
|
||||||
|
if (p) {
|
||||||
|
acquire(&ptylock);
|
||||||
|
p->mopen++;
|
||||||
|
release(&ptylock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The master is gone: the slave side has been hung up on. Its readers see end
|
||||||
|
// of file, its writers EIO, and its session SIGHUP -- which is how closing an
|
||||||
|
// xterm window ends the shell inside it.
|
||||||
|
void
|
||||||
|
ptm_close(struct inode *ip)
|
||||||
|
{
|
||||||
|
struct pty *p = pty_of(ip);
|
||||||
|
int sid = 0, pg = 0;
|
||||||
|
|
||||||
|
if (p == 0)
|
||||||
|
return;
|
||||||
|
acquire(&ptylock);
|
||||||
|
if (p->mopen > 0)
|
||||||
|
p->mopen--;
|
||||||
|
if (p->mopen == 0) {
|
||||||
|
sid = p->sid;
|
||||||
|
pg = p->fg_pgrp;
|
||||||
|
wakeup(&p->ir);
|
||||||
|
wakeup(&p->ow);
|
||||||
|
wakeup(&p->ie);
|
||||||
|
}
|
||||||
|
maybe_free(p);
|
||||||
|
release(&ptylock);
|
||||||
|
if (sid > 0)
|
||||||
|
sigsend_pgrp(sid, SIGHUP);
|
||||||
|
if (pg > 0 && pg != sid)
|
||||||
|
sigsend_pgrp(pg, SIGHUP);
|
||||||
|
poll_kick();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- the slave ---------------------------------------------------------------
|
||||||
|
|
||||||
|
int
|
||||||
|
pts_read(struct inode *ip, uint64 off, char *dst, int n)
|
||||||
|
{
|
||||||
|
struct pty *p = pty_of(ip);
|
||||||
|
char kbuf[256];
|
||||||
|
int got = 0, c;
|
||||||
|
|
||||||
|
(void)off;
|
||||||
|
if (p == 0 || !p->used)
|
||||||
|
return -EIO;
|
||||||
|
if (n > (int)sizeof(kbuf))
|
||||||
|
n = sizeof(kbuf);
|
||||||
|
iunlock(ip);
|
||||||
|
acquire(&ptylock);
|
||||||
|
while (got < n) {
|
||||||
|
while (p->ir == p->iw) {
|
||||||
|
if (p->eof) {
|
||||||
|
p->eof = 0;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (p->mopen == 0)
|
||||||
|
goto done; // hung up: end of file
|
||||||
|
// VMIN 0 is a poll, and a program that asked for one must not block.
|
||||||
|
if (!(p->tio.c_lflag & ICANON) && p->tio.c_cc[VMIN] == 0)
|
||||||
|
goto done;
|
||||||
|
if (got > 0 && !(p->tio.c_lflag & ICANON))
|
||||||
|
goto done;
|
||||||
|
if (myproc()->killed || p->abort_read) {
|
||||||
|
p->abort_read = 0;
|
||||||
|
release(&ptylock);
|
||||||
|
ilock(ip);
|
||||||
|
return got ? got : -EINTR;
|
||||||
|
}
|
||||||
|
sleep(&p->ir, &ptylock);
|
||||||
|
}
|
||||||
|
c = p->in[p->ir++ % PTY_BUF];
|
||||||
|
kbuf[got++] = (char)c;
|
||||||
|
if ((p->tio.c_lflag & ICANON) && c == '\n')
|
||||||
|
break;
|
||||||
|
if (!(p->tio.c_lflag & ICANON) &&
|
||||||
|
got >= (p->tio.c_cc[VMIN] ? p->tio.c_cc[VMIN] : 1) && p->ir == p->iw)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
done:
|
||||||
|
wakeup(&p->ie);
|
||||||
|
release(&ptylock);
|
||||||
|
poll_kick();
|
||||||
|
ilock(ip);
|
||||||
|
if (got && copy_to_user((addr_t)dst, kbuf, got) < 0)
|
||||||
|
return -EFAULT;
|
||||||
|
return got;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
pts_write(struct inode *ip, uint64 off, char *src, int n)
|
||||||
|
{
|
||||||
|
struct pty *p = pty_of(ip);
|
||||||
|
char kbuf[128];
|
||||||
|
int done = 0;
|
||||||
|
|
||||||
|
(void)off;
|
||||||
|
if (p == 0 || !p->used)
|
||||||
|
return -EIO;
|
||||||
|
iunlock(ip);
|
||||||
|
while (done < n) {
|
||||||
|
int chunk = n - done < (int)sizeof(kbuf) ? n - done : (int)sizeof(kbuf);
|
||||||
|
if (copy_from_user(kbuf, (addr_t)(src + done), chunk) < 0) {
|
||||||
|
ilock(ip);
|
||||||
|
return done ? done : -EFAULT;
|
||||||
|
}
|
||||||
|
acquire(&ptylock);
|
||||||
|
for (int i = 0; i < chunk; i++) {
|
||||||
|
while (out_room(p) < 2) {
|
||||||
|
if (p->mopen == 0 || myproc()->killed) {
|
||||||
|
release(&ptylock);
|
||||||
|
ilock(ip);
|
||||||
|
return done + i ? done + i : (p->mopen == 0 ? -EIO : -EINTR);
|
||||||
|
}
|
||||||
|
wakeup(&p->or);
|
||||||
|
sleep(&p->ow, &ptylock);
|
||||||
|
}
|
||||||
|
if (p->mopen == 0) {
|
||||||
|
release(&ptylock);
|
||||||
|
ilock(ip);
|
||||||
|
return done + i ? done + i : -EIO;
|
||||||
|
}
|
||||||
|
out_put(p, kbuf[i] & 0xff);
|
||||||
|
}
|
||||||
|
wakeup(&p->or);
|
||||||
|
release(&ptylock);
|
||||||
|
done += chunk;
|
||||||
|
}
|
||||||
|
poll_kick();
|
||||||
|
ilock(ip);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
pts_ready(struct inode *ip)
|
||||||
|
{
|
||||||
|
struct pty *p = pty_of(ip);
|
||||||
|
return p && (p->ir != p->iw || p->eof || p->mopen == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Opening a terminal makes it the caller's controlling terminal when the
|
||||||
|
// caller leads a session that has none -- the System V rule, which Linux keeps
|
||||||
|
// and which xterm relies on: its child calls setsid() and opens the slave, and
|
||||||
|
// expects ^C to work from then on without a TIOCSCTTY.
|
||||||
|
void
|
||||||
|
pts_open(struct inode *ip)
|
||||||
|
{
|
||||||
|
struct pty *p = pty_of(ip);
|
||||||
|
struct proc *me = myproc();
|
||||||
|
|
||||||
|
if (p == 0)
|
||||||
|
return;
|
||||||
|
acquire(&ptylock);
|
||||||
|
p->sopen++;
|
||||||
|
p->slave_seen = 1;
|
||||||
|
if (me && me->sid == me->pid && me->ctty == 0 && p->sid == 0) {
|
||||||
|
me->ctty = PTY_CTTY + ip->minor;
|
||||||
|
p->sid = me->sid;
|
||||||
|
p->fg_pgrp = me->pgid;
|
||||||
|
}
|
||||||
|
release(&ptylock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pts_close(struct inode *ip)
|
||||||
|
{
|
||||||
|
struct pty *p = pty_of(ip);
|
||||||
|
|
||||||
|
if (p == 0)
|
||||||
|
return;
|
||||||
|
acquire(&ptylock);
|
||||||
|
if (p->sopen > 0)
|
||||||
|
p->sopen--;
|
||||||
|
if (p->sopen == 0) {
|
||||||
|
p->sid = 0;
|
||||||
|
p->fg_pgrp = 0;
|
||||||
|
wakeup(&p->or); // the master reads EIO now
|
||||||
|
}
|
||||||
|
maybe_free(p);
|
||||||
|
release(&ptylock);
|
||||||
|
poll_kick();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- ioctl and poll ----------------------------------------------------------
|
||||||
|
|
||||||
|
int
|
||||||
|
pty_ioctl(struct inode *ip, uint request, addr_t arg)
|
||||||
|
{
|
||||||
|
struct pty *p = pty_of(ip);
|
||||||
|
int master = ip->major == PTM_MAJOR;
|
||||||
|
int v;
|
||||||
|
|
||||||
|
if (p == 0 || !p->used)
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
switch (request) {
|
||||||
|
case TIOCGPTN:
|
||||||
|
if (!master)
|
||||||
|
return -ENOTTY;
|
||||||
|
v = ip->minor;
|
||||||
|
return put_user(v, arg) < 0 ? -EFAULT : 0;
|
||||||
|
case TIOCSPTLCK:
|
||||||
|
case TIOCPKT:
|
||||||
|
// Slaves are never locked here, so unlockpt() has nothing to undo; packet
|
||||||
|
// mode is not offered, and nothing in the image asks for it.
|
||||||
|
return master ? 0 : -ENOTTY;
|
||||||
|
case TCGETS: {
|
||||||
|
struct termios t;
|
||||||
|
acquire(&ptylock);
|
||||||
|
t = p->tio;
|
||||||
|
release(&ptylock);
|
||||||
|
return copy_to_user(arg, &t, sizeof(t)) < 0 ? -EFAULT : 0;
|
||||||
|
}
|
||||||
|
case TCSETS: case TCSETSW: case TCSETSF: {
|
||||||
|
struct termios t;
|
||||||
|
if (copy_from_user(&t, arg, sizeof(t)) < 0)
|
||||||
|
return -EFAULT;
|
||||||
|
acquire(&ptylock);
|
||||||
|
p->tio = t;
|
||||||
|
if (request == TCSETSF)
|
||||||
|
p->ie = p->iw = p->ir;
|
||||||
|
// Leaving canonical mode hands over the line being edited, as on the
|
||||||
|
// console: a shell that switches to raw mid-line must not lose it.
|
||||||
|
if (!(t.c_lflag & ICANON))
|
||||||
|
p->iw = p->ie;
|
||||||
|
wakeup(&p->ir);
|
||||||
|
release(&ptylock);
|
||||||
|
poll_kick();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case TCFLSH:
|
||||||
|
acquire(&ptylock);
|
||||||
|
if (arg == TCIFLUSH || arg == TCIOFLUSH) {
|
||||||
|
p->ie = p->iw = p->ir;
|
||||||
|
p->eof = 0;
|
||||||
|
}
|
||||||
|
if (arg == TCOFLUSH || arg == TCIOFLUSH)
|
||||||
|
p->or = p->ow;
|
||||||
|
wakeup(&p->ie);
|
||||||
|
wakeup(&p->ow);
|
||||||
|
release(&ptylock);
|
||||||
|
return 0;
|
||||||
|
case TIOCGWINSZ:
|
||||||
|
return copy_to_user(arg, &p->ws, sizeof(p->ws)) < 0 ? -EFAULT : 0;
|
||||||
|
case TIOCSWINSZ: {
|
||||||
|
struct winsize ws;
|
||||||
|
int changed, pg;
|
||||||
|
if (copy_from_user(&ws, arg, sizeof(ws)) < 0)
|
||||||
|
return -EFAULT;
|
||||||
|
acquire(&ptylock);
|
||||||
|
changed = ws.ws_row != p->ws.ws_row || ws.ws_col != p->ws.ws_col;
|
||||||
|
p->ws = ws;
|
||||||
|
pg = p->fg_pgrp;
|
||||||
|
release(&ptylock);
|
||||||
|
// The program inside learns of a resize the way it would on Linux.
|
||||||
|
if (changed && pg > 0)
|
||||||
|
sigsend_pgrp(pg, SIGWINCH);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case FIONREAD:
|
||||||
|
v = master ? (int)(p->ow - p->or) : (int)(p->iw - p->ir);
|
||||||
|
return put_user(v, arg) < 0 ? -EFAULT : 0;
|
||||||
|
case TIOCOUTQ:
|
||||||
|
v = master ? 0 : (int)(p->ow - p->or);
|
||||||
|
return put_user(v, arg) < 0 ? -EFAULT : 0;
|
||||||
|
case TIOCGPGRP:
|
||||||
|
v = p->fg_pgrp;
|
||||||
|
return put_user(v, arg) < 0 ? -EFAULT : 0;
|
||||||
|
case TIOCSPGRP:
|
||||||
|
if (copy_from_user(&v, arg, sizeof(v)) < 0)
|
||||||
|
return -EFAULT;
|
||||||
|
if (v <= 0)
|
||||||
|
return -EINVAL;
|
||||||
|
p->fg_pgrp = v;
|
||||||
|
return 0;
|
||||||
|
case TIOCGSID:
|
||||||
|
if (p->sid == 0)
|
||||||
|
return -ENOTTY;
|
||||||
|
v = p->sid;
|
||||||
|
return put_user(v, arg) < 0 ? -EFAULT : 0;
|
||||||
|
case TIOCSCTTY:
|
||||||
|
if (master)
|
||||||
|
return -EINVAL;
|
||||||
|
myproc()->ctty = PTY_CTTY + ip->minor;
|
||||||
|
p->sid = myproc()->sid;
|
||||||
|
p->fg_pgrp = myproc()->pgid;
|
||||||
|
return 0;
|
||||||
|
case TIOCNOTTY:
|
||||||
|
if (myproc()->ctty == PTY_CTTY + ip->minor)
|
||||||
|
myproc()->ctty = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -ENOTTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
pty_poll(struct inode *ip, int events, int readable, int writable)
|
||||||
|
{
|
||||||
|
struct pty *p = pty_of(ip);
|
||||||
|
int rev = 0;
|
||||||
|
|
||||||
|
if (p == 0 || !p->used)
|
||||||
|
return POLLHUP_;
|
||||||
|
if (ip->major == PTM_MAJOR) {
|
||||||
|
if (readable && (events & POLLIN_) && p->or != p->ow)
|
||||||
|
rev |= POLLIN_;
|
||||||
|
if (p->slave_seen && p->sopen == 0)
|
||||||
|
rev |= POLLHUP_ | (events & POLLIN_);
|
||||||
|
} else {
|
||||||
|
if (readable && (events & POLLIN_) && (p->ir != p->iw || p->eof))
|
||||||
|
rev |= POLLIN_;
|
||||||
|
if (p->mopen == 0)
|
||||||
|
rev |= POLLHUP_ | (events & POLLIN_);
|
||||||
|
}
|
||||||
|
if (writable && (events & POLLOUT_))
|
||||||
|
rev |= POLLOUT_;
|
||||||
|
return rev;
|
||||||
|
}
|
||||||
175
kernel/dev/vt.c
175
kernel/dev/vt.c
|
|
@ -286,6 +286,99 @@ vt_scroll(struct vt *v)
|
||||||
v->cy--;
|
v->cy--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- the scrolling region ----------------------------------------------------
|
||||||
|
//
|
||||||
|
// tmux, htop, less and every curses program on TERM=linux scroll part of the
|
||||||
|
// screen: a status line stays put while the rest moves. Without a region the
|
||||||
|
// whole screen scrolled instead, and a pane's output smeared into its
|
||||||
|
// neighbours and over the status bar.
|
||||||
|
|
||||||
|
static int
|
||||||
|
region_bot(struct vt *v)
|
||||||
|
{
|
||||||
|
if (v->scroll_bot > v->scroll_top && v->scroll_bot < v->rows)
|
||||||
|
return v->scroll_bot;
|
||||||
|
return v->rows - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
region_top(struct vt *v)
|
||||||
|
{
|
||||||
|
return v->scroll_top < region_bot(v) ? v->scroll_top : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
draw_rows(struct vt *v, int r0, int r1)
|
||||||
|
{
|
||||||
|
if (!is_active(v))
|
||||||
|
return;
|
||||||
|
for (int r = r0; r <= r1; r++)
|
||||||
|
for (int c = 0; c < v->cols; c++)
|
||||||
|
show(v, c, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rows top..bot move up n; the n exposed at the bottom are blank.
|
||||||
|
static void
|
||||||
|
region_up(struct vt *v, int top, int bot, int n)
|
||||||
|
{
|
||||||
|
if (n <= 0 || top > bot)
|
||||||
|
return;
|
||||||
|
if (n > bot - top + 1)
|
||||||
|
n = bot - top + 1;
|
||||||
|
if (top == 0 && bot == v->rows - 1) {
|
||||||
|
// The whole screen: the framebuffer can be moved instead of redrawn.
|
||||||
|
while (n-- > 0) {
|
||||||
|
memmove(v->cells, cell_at(v, 0, 1),
|
||||||
|
(uint64)(v->rows - 1) * v->cols * sizeof(struct vtcell));
|
||||||
|
blank_row(v, v->rows - 1);
|
||||||
|
if (is_active(v))
|
||||||
|
gfx_scroll_up(vt_rgb(v->bg));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memmove(cell_at(v, 0, top), cell_at(v, 0, top + n),
|
||||||
|
(uint64)(bot - top + 1 - n) * v->cols * sizeof(struct vtcell));
|
||||||
|
for (int r = bot - n + 1; r <= bot; r++)
|
||||||
|
blank_row(v, r);
|
||||||
|
draw_rows(v, top, bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rows top..bot move down n; the n exposed at the top are blank.
|
||||||
|
static void
|
||||||
|
region_down(struct vt *v, int top, int bot, int n)
|
||||||
|
{
|
||||||
|
if (n <= 0 || top > bot)
|
||||||
|
return;
|
||||||
|
if (n > bot - top + 1)
|
||||||
|
n = bot - top + 1;
|
||||||
|
memmove(cell_at(v, 0, top + n), cell_at(v, 0, top),
|
||||||
|
(uint64)(bot - top + 1 - n) * v->cols * sizeof(struct vtcell));
|
||||||
|
for (int r = top; r < top + n; r++)
|
||||||
|
blank_row(v, r);
|
||||||
|
draw_rows(v, top, bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Down a line, scrolling the region when the cursor is on its last row. Below
|
||||||
|
// the region the cursor only moves, and stops at the bottom of the screen.
|
||||||
|
static void
|
||||||
|
linefeed(struct vt *v)
|
||||||
|
{
|
||||||
|
if (v->cy == region_bot(v))
|
||||||
|
region_up(v, region_top(v), region_bot(v), 1);
|
||||||
|
else if (v->cy < v->rows - 1)
|
||||||
|
v->cy++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Up a line, the reverse: the region scrolls down from its first row.
|
||||||
|
static void
|
||||||
|
reverse_index(struct vt *v)
|
||||||
|
{
|
||||||
|
if (v->cy == region_top(v))
|
||||||
|
region_down(v, region_top(v), region_bot(v), 1);
|
||||||
|
else if (v->cy > 0)
|
||||||
|
v->cy--;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
vt_cursor_set(struct vt *v, int col, int row)
|
vt_cursor_set(struct vt *v, int col, int row)
|
||||||
{
|
{
|
||||||
|
|
@ -328,19 +421,23 @@ wrap_now(struct vt *v)
|
||||||
return;
|
return;
|
||||||
v->wrap_pending = 0;
|
v->wrap_pending = 0;
|
||||||
v->cx = 0;
|
v->cx = 0;
|
||||||
v->cy++;
|
linefeed(v);
|
||||||
while (v->cy >= v->rows)
|
|
||||||
vt_scroll(v);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
vt_putc(struct vt *v, int glyph)
|
vt_putc(struct vt *v, int glyph)
|
||||||
{
|
{
|
||||||
switch (glyph) {
|
switch (glyph) {
|
||||||
|
// LF is a line feed: down one row, same column. The carriage return a
|
||||||
|
// shell expects with it is ONLCR's, so it happens only while the terminal
|
||||||
|
// asks for it. Doing it always broke every program that turns OPOST off and
|
||||||
|
// moves down with a bare LF -- tmux does, row after row, and each row of a
|
||||||
|
// right-hand pane landed in column 0, where its ESC [ K erased the left one.
|
||||||
case '\n':
|
case '\n':
|
||||||
v->wrap_pending = 0;
|
v->wrap_pending = 0;
|
||||||
|
if ((v->tio.c_oflag & (OPOST | ONLCR)) == (OPOST | ONLCR))
|
||||||
v->cx = 0;
|
v->cx = 0;
|
||||||
v->cy++;
|
linefeed(v);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '\r':
|
case '\r':
|
||||||
|
|
@ -759,6 +856,38 @@ vt_csi(struct vt *v, int final)
|
||||||
case 's': v->saved_cx = col; v->saved_cy = row; break;
|
case 's': v->saved_cx = col; v->saved_cy = row; break;
|
||||||
case 'u': vt_cursor_set(v, v->saved_cx, v->saved_cy); break;
|
case 'u': vt_cursor_set(v, v->saved_cx, v->saved_cy); break;
|
||||||
|
|
||||||
|
// DECSTBM: the scrolling region, 1-based and inclusive, and the cursor home.
|
||||||
|
case 'r': {
|
||||||
|
int t = v->esc_nparm > 0 && v->esc_parm[0] ? v->esc_parm[0] : 1;
|
||||||
|
int b = v->esc_nparm > 1 && v->esc_parm[1] ? v->esc_parm[1] : v->rows;
|
||||||
|
if (b > v->rows) b = v->rows;
|
||||||
|
if (t < b) {
|
||||||
|
v->scroll_top = t - 1;
|
||||||
|
v->scroll_bot = b - 1;
|
||||||
|
} else {
|
||||||
|
v->scroll_top = v->scroll_bot = 0;
|
||||||
|
}
|
||||||
|
vt_cursor_set(v, 0, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert and delete lines at the cursor, within the region: what a curses
|
||||||
|
// program sends to open or close a gap rather than repaint everything below.
|
||||||
|
case 'L':
|
||||||
|
if (row >= region_top(v) && row <= region_bot(v))
|
||||||
|
region_down(v, row, region_bot(v), n1);
|
||||||
|
v->wrap_pending = 0;
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
if (row >= region_top(v) && row <= region_bot(v))
|
||||||
|
region_up(v, row, region_bot(v), n1);
|
||||||
|
v->wrap_pending = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Scroll the region without moving the cursor.
|
||||||
|
case 'S': region_up(v, region_top(v), region_bot(v), n1); break;
|
||||||
|
case 'T': region_down(v, region_top(v), region_bot(v), n1); break;
|
||||||
|
|
||||||
// Cursor visibility (DECTCEM) and the other private modes. There is no
|
// Cursor visibility (DECTCEM) and the other private modes. There is no
|
||||||
// hardware cursor being drawn, so there is nothing to show or hide --
|
// hardware cursor being drawn, so there is nothing to show or hide --
|
||||||
// swallowing them silently is correct, and far better than printing
|
// swallowing them silently is correct, and far better than printing
|
||||||
|
|
@ -781,6 +910,37 @@ vt_feed_1(struct vt *v, int c)
|
||||||
for (int i = 0; i < VT_MAXPARM; i++) v->esc_parm[i] = 0;
|
for (int i = 0; i < VT_MAXPARM; i++) v->esc_parm[i] = 0;
|
||||||
return; }
|
return; }
|
||||||
if (c == ']') { v->esc_state = VE_OSC; return; }
|
if (c == ']') { v->esc_state = VE_OSC; return; }
|
||||||
|
v->esc_state = VE_NORMAL;
|
||||||
|
switch (c) {
|
||||||
|
case '7': // DECSC
|
||||||
|
v->saved_cx = v->cx;
|
||||||
|
v->saved_cy = v->cy;
|
||||||
|
break;
|
||||||
|
case '8': // DECRC
|
||||||
|
vt_cursor_set(v, v->saved_cx, v->saved_cy);
|
||||||
|
break;
|
||||||
|
case 'M': // RI: up, scrolling back
|
||||||
|
v->wrap_pending = 0;
|
||||||
|
reverse_index(v);
|
||||||
|
break;
|
||||||
|
case 'D': // IND: down, scrolling
|
||||||
|
v->wrap_pending = 0;
|
||||||
|
linefeed(v);
|
||||||
|
break;
|
||||||
|
case 'E': // NEL: CR and IND
|
||||||
|
v->wrap_pending = 0;
|
||||||
|
v->cx = 0;
|
||||||
|
linefeed(v);
|
||||||
|
break;
|
||||||
|
case '(': case ')': case '*': case '+':
|
||||||
|
// A character set designation. The set itself is not offered, but the
|
||||||
|
// byte naming it must be swallowed: ESC ( B printed a stray "B".
|
||||||
|
v->esc_state = VE_CHARSET;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
case VE_CHARSET:
|
||||||
v->esc_state = VE_NORMAL;
|
v->esc_state = VE_NORMAL;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -798,7 +958,12 @@ vt_feed_1(struct vt *v, int c)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case VE_CSI:
|
case VE_CSI:
|
||||||
if (c == '?') { v->esc_private = 1; return; }
|
// Private markers (< = > ?) and intermediates (space to /): sequences
|
||||||
|
// this terminal does not speak, but must still consume to the final byte.
|
||||||
|
// Ending at the marker instead printed the rest -- tmux asking for
|
||||||
|
// extended keys as ESC [ > 4 ; 1 m left "4;1m" on the screen.
|
||||||
|
if (c >= '<' && c <= '?') { v->esc_private = 1; return; }
|
||||||
|
if (c >= 0x20 && c <= 0x2F) { v->esc_private = 1; return; }
|
||||||
if (c >= '0' && c <= '9') {
|
if (c >= '0' && c <= '9') {
|
||||||
if (v->esc_nparm == 0) v->esc_nparm = 1;
|
if (v->esc_nparm == 0) v->esc_nparm = 1;
|
||||||
if (v->esc_nparm <= VT_MAXPARM)
|
if (v->esc_nparm <= VT_MAXPARM)
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@
|
||||||
#include "fs/file.h"
|
#include "fs/file.h"
|
||||||
#include "uapi/errno.h"
|
#include "uapi/errno.h"
|
||||||
#include "fs/blkdev.h"
|
#include "fs/blkdev.h"
|
||||||
|
#include "dev/pty.h"
|
||||||
|
|
||||||
// Inode numbering. Two directories and a flat table of nodes, so the number is
|
// Inode numbering. Two directories and a flat table of nodes, so the number is
|
||||||
// either a directory id or an index into devnodes[] offset past them.
|
// either a directory id or an index into devnodes[] offset past them.
|
||||||
|
|
@ -49,6 +50,12 @@
|
||||||
// device number, and inode DEVFS_BLK0 plus that number. Not in devnodes[],
|
// device number, and inode DEVFS_BLK0 plus that number. Not in devnodes[],
|
||||||
// because they come and go: a stick plugged in appears, pulled out vanishes.
|
// because they come and go: a stick plugged in appears, pulled out vanishes.
|
||||||
#define DEVFS_BLK0 4096ULL
|
#define DEVFS_BLK0 4096ULL
|
||||||
|
// Pseudo-terminals: the /dev/pts directory, its slaves by pair number, and the
|
||||||
|
// masters, which have inodes so that an open file can hold one but appear in no
|
||||||
|
// directory -- the only way to one is opening /dev/ptmx.
|
||||||
|
#define DEVFS_PTS 3ULL
|
||||||
|
#define DEVFS_PTS0 8192ULL
|
||||||
|
#define DEVFS_PTM0 12288ULL
|
||||||
|
|
||||||
// Which directory a node lives in. Only two exist and a third would want a real
|
// Which directory a node lives in. Only two exist and a third would want a real
|
||||||
// tree rather than a flag, which is the point at which this file should grow
|
// tree rather than a flag, which is the point at which this file should grow
|
||||||
|
|
@ -110,6 +117,8 @@ static const struct devnode devnodes[] = {
|
||||||
// Audio, spelled the way UNIX spells it. See include/dev/audio.h.
|
// Audio, spelled the way UNIX spells it. See include/dev/audio.h.
|
||||||
{ "dsp", 12, 0, D_ROOT },
|
{ "dsp", 12, 0, D_ROOT },
|
||||||
{ "mouse", 3, 1, D_ROOT },
|
{ "mouse", 3, 1, D_ROOT },
|
||||||
|
// Opening it makes a new pseudo-terminal pair; see kernel/dev/pty.c.
|
||||||
|
{ "ptmx", PTMX_MAJOR, 0, D_ROOT },
|
||||||
|
|
||||||
// Raw disks. Created whether or not a drive is present: opening one with no
|
// Raw disks. Created whether or not a drive is present: opening one with no
|
||||||
// disk behind it fails with ENXIO, which is a better answer than a missing
|
// disk behind it fails with ENXIO, which is a better answer than a missing
|
||||||
|
|
@ -143,9 +152,22 @@ devfs_iload(struct inode *ip)
|
||||||
ip->major = ip->minor = 0;
|
ip->major = ip->minor = 0;
|
||||||
ip->size = 0;
|
ip->size = 0;
|
||||||
|
|
||||||
if (ip->inum == DEVFS_ROOT || ip->inum == DEVFS_INPUT) {
|
if (ip->inum == DEVFS_ROOT || ip->inum == DEVFS_INPUT ||
|
||||||
|
ip->inum == DEVFS_PTS) {
|
||||||
ip->type = T_DIR;
|
ip->type = T_DIR;
|
||||||
ip->nlink = 2;
|
ip->nlink = 2;
|
||||||
|
} else if (ip->inum >= DEVFS_PTS0 && ip->inum < DEVFS_PTS0 + NPTY &&
|
||||||
|
pty_used((int)(ip->inum - DEVFS_PTS0))) {
|
||||||
|
ip->type = T_DEV;
|
||||||
|
ip->nlink = 1;
|
||||||
|
ip->major = PTS_MAJOR;
|
||||||
|
ip->minor = (short)(ip->inum - DEVFS_PTS0);
|
||||||
|
ip->mode = 0620;
|
||||||
|
} else if (ip->inum >= DEVFS_PTM0 && ip->inum < DEVFS_PTM0 + NPTY) {
|
||||||
|
ip->type = T_DEV;
|
||||||
|
ip->nlink = 1;
|
||||||
|
ip->major = PTM_MAJOR;
|
||||||
|
ip->minor = (short)(ip->inum - DEVFS_PTM0);
|
||||||
} else if (i >= 0) {
|
} else if (i >= 0) {
|
||||||
ip->type = T_DEV;
|
ip->type = T_DEV;
|
||||||
ip->nlink = 1;
|
ip->nlink = 1;
|
||||||
|
|
@ -196,6 +218,32 @@ devfs_dirlookup(struct inode *dp, char *name, uint *poff)
|
||||||
|
|
||||||
if (dp->inum == DEVFS_ROOT && streq(name, "input"))
|
if (dp->inum == DEVFS_ROOT && streq(name, "input"))
|
||||||
return iget(DEVFS_DEV, DEVFS_INPUT);
|
return iget(DEVFS_DEV, DEVFS_INPUT);
|
||||||
|
if (dp->inum == DEVFS_ROOT && streq(name, "pts"))
|
||||||
|
return iget(DEVFS_DEV, DEVFS_PTS);
|
||||||
|
|
||||||
|
// /dev/pts/N: a pair that exists.
|
||||||
|
if (dp->inum == DEVFS_PTS) {
|
||||||
|
int n = 0;
|
||||||
|
const char *s = name;
|
||||||
|
if (!*s)
|
||||||
|
return 0;
|
||||||
|
for (; *s; s++) {
|
||||||
|
if (*s < '0' || *s > '9')
|
||||||
|
return 0;
|
||||||
|
n = n * 10 + (*s - '0');
|
||||||
|
if (n >= NPTY)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return pty_used(n) ? iget(DEVFS_DEV, DEVFS_PTS0 + (uint64)n) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// /dev/tty is the caller's controlling terminal. When that is a pty, it is
|
||||||
|
// the slave itself -- what a program inside xterm or tmux opens to talk to
|
||||||
|
// its terminal must be that terminal, not the console.
|
||||||
|
if (dp->inum == DEVFS_ROOT && streq(name, "tty") && myproc() &&
|
||||||
|
myproc()->ctty >= PTY_CTTY && myproc()->ctty < PTY_CTTY + NPTY &&
|
||||||
|
pty_used(myproc()->ctty - PTY_CTTY))
|
||||||
|
return iget(DEVFS_DEV, DEVFS_PTS0 + (uint64)(myproc()->ctty - PTY_CTTY));
|
||||||
|
|
||||||
short want = dp->inum == DEVFS_INPUT ? D_INPUT : D_ROOT;
|
short want = dp->inum == DEVFS_INPUT ? D_INPUT : D_ROOT;
|
||||||
for (int i = 0; i < NDEVNODES; i++)
|
for (int i = 0; i < NDEVNODES; i++)
|
||||||
|
|
@ -258,7 +306,8 @@ devfs_getdents(struct inode *dp, char *out, int cap, uint64 *poff)
|
||||||
int used = 0;
|
int used = 0;
|
||||||
uint64 n = *poff;
|
uint64 n = *poff;
|
||||||
|
|
||||||
if (dp->inum != DEVFS_ROOT && dp->inum != DEVFS_INPUT)
|
if (dp->inum != DEVFS_ROOT && dp->inum != DEVFS_INPUT &&
|
||||||
|
dp->inum != DEVFS_PTS)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (n == 0) {
|
if (n == 0) {
|
||||||
|
|
@ -271,15 +320,36 @@ devfs_getdents(struct inode *dp, char *out, int cap, uint64 *poff)
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// /dev/input is a directory entry of the root, and it is emitted between the
|
// /dev/pts: the dots, then the pairs that exist. Ordinal 2 + N is pair N,
|
||||||
// dots and the nodes so that its ordinal is stable.
|
// whether or not the ones before it exist, so a pair opened between two
|
||||||
|
// calls does not shift the rest.
|
||||||
|
if (dp->inum == DEVFS_PTS) {
|
||||||
|
for (; n >= 2 && n < 2 + NPTY; n++) {
|
||||||
|
int k = (int)(n - 2);
|
||||||
|
char nm[4];
|
||||||
|
if (!pty_used(k))
|
||||||
|
continue;
|
||||||
|
nm[0] = k >= 10 ? '0' + k / 10 : '0' + k;
|
||||||
|
nm[1] = k >= 10 ? '0' + k % 10 : 0;
|
||||||
|
nm[2] = 0;
|
||||||
|
if (emit(out, cap, &used, DEVFS_PTS0 + (uint64)k, 2, nm) < 0) goto out;
|
||||||
|
}
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// /dev/input and /dev/pts are directory entries of the root, emitted
|
||||||
|
// between the dots and the nodes so that their ordinals are stable.
|
||||||
if (dp->inum == DEVFS_ROOT && n == 2) {
|
if (dp->inum == DEVFS_ROOT && n == 2) {
|
||||||
if (emit(out, cap, &used, DEVFS_INPUT, 4, "input") < 0) goto out;
|
if (emit(out, cap, &used, DEVFS_INPUT, 4, "input") < 0) goto out;
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
|
if (dp->inum == DEVFS_ROOT && n == 3) {
|
||||||
|
if (emit(out, cap, &used, DEVFS_PTS, 4, "pts") < 0) goto out;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
short want = dp->inum == DEVFS_INPUT ? D_INPUT : D_ROOT;
|
short want = dp->inum == DEVFS_INPUT ? D_INPUT : D_ROOT;
|
||||||
uint64 base = dp->inum == DEVFS_ROOT ? 3 : 2;
|
uint64 base = dp->inum == DEVFS_ROOT ? 4 : 2;
|
||||||
|
|
||||||
for (int i = 0; i < NDEVNODES; i++) {
|
for (int i = 0; i < NDEVNODES; i++) {
|
||||||
if (devnodes[i].dir != want)
|
if (devnodes[i].dir != want)
|
||||||
|
|
@ -339,3 +409,12 @@ devfs_mount(const char *target)
|
||||||
iput(dp);
|
iput(dp);
|
||||||
return r < 0 ? -EBUSY : 0;
|
return r < 0 ? -EBUSY : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The inode of pty master N, for ptmx_open() to hand to the file it opens.
|
||||||
|
struct inode *
|
||||||
|
devfs_pty_master(int n)
|
||||||
|
{
|
||||||
|
if (n < 0 || n >= NPTY)
|
||||||
|
return 0;
|
||||||
|
return iget(DEVFS_DEV, DEVFS_PTM0 + (uint64)n);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -535,6 +535,8 @@ exec_elf(char *path, struct argsrc *av, struct argsrc *ev)
|
||||||
if(*s == '/')
|
if(*s == '/')
|
||||||
last = s+1;
|
last = s+1;
|
||||||
safestrcpy(myproc()->name, last, sizeof(myproc()->name));
|
safestrcpy(myproc()->name, last, sizeof(myproc()->name));
|
||||||
|
if (strace_wanted(myproc()->name))
|
||||||
|
myproc()->strace = 1;
|
||||||
safestrcpy(myproc()->exe, path, sizeof(myproc()->exe));
|
safestrcpy(myproc()->exe, path, sizeof(myproc()->exe));
|
||||||
|
|
||||||
for(int i = 0; i < NOFILE; i++){
|
for(int i = 0; i < NOFILE; i++){
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#include "uapi/posix.h"
|
#include "uapi/posix.h"
|
||||||
#include "uapi/errno.h"
|
#include "uapi/errno.h"
|
||||||
#include "core/kmem.h"
|
#include "core/kmem.h"
|
||||||
|
#include "dev/pty.h"
|
||||||
|
|
||||||
extern struct devsw devsw[];
|
extern struct devsw devsw[];
|
||||||
|
|
||||||
|
|
@ -422,6 +423,10 @@ file_poll(struct file *f, int events)
|
||||||
return rev;
|
return rev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(f->ip && f->ip->type == T_DEV &&
|
||||||
|
(f->ip->major == PTM_MAJOR || f->ip->major == PTS_MAJOR))
|
||||||
|
return pty_poll(f->ip, events, f->readable, f->writable);
|
||||||
|
|
||||||
if(f->ip && f->ip->type == T_DEV && f->ip->major == CONSOLE){
|
if(f->ip && f->ip->type == T_DEV && f->ip->major == CONSOLE){
|
||||||
int rev = 0;
|
int rev = 0;
|
||||||
if((events & POLLIN) && f->readable && console_input_ready(f->ip ? f->ip->minor : 1))
|
if((events & POLLIN) && f->readable && console_input_ready(f->ip ? f->ip->minor : 1))
|
||||||
|
|
|
||||||
|
|
@ -624,8 +624,10 @@ ilock(struct inode *ip)
|
||||||
|
|
||||||
acquiresleep(&ip->lock);
|
acquiresleep(&ip->lock);
|
||||||
|
|
||||||
if (ip->valid == 0)
|
if (ip->valid == 0) {
|
||||||
|
ip->mode = 0;
|
||||||
ip->ops->iload(ip);
|
ip->ops->iload(ip);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@
|
||||||
#include "uapi/stat.h"
|
#include "uapi/stat.h"
|
||||||
#include "core/uaccess.h"
|
#include "core/uaccess.h"
|
||||||
#include "uapi/errno.h"
|
#include "uapi/errno.h"
|
||||||
|
#include "dev/pty.h"
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Inode numbering
|
// Inode numbering
|
||||||
|
|
@ -68,7 +69,8 @@
|
||||||
#define PF_EXE 5
|
#define PF_EXE 5
|
||||||
#define PF_SCHEDSTAT 6
|
#define PF_SCHEDSTAT 6
|
||||||
#define PF_FD 7 // the directory /proc/<pid>/fd
|
#define PF_FD 7 // the directory /proc/<pid>/fd
|
||||||
#define PF_MAX 7
|
#define PF_TASK 8 // the directory /proc/<pid>/task
|
||||||
|
#define PF_MAX 8
|
||||||
#define PF_FDENT 0x80 // | fd: one link in it
|
#define PF_FDENT 0x80 // | fd: one link in it
|
||||||
|
|
||||||
#define INO_PID(ino) ((int)((ino) >> 8))
|
#define INO_PID(ino) ((int)((ino) >> 8))
|
||||||
|
|
@ -109,6 +111,9 @@ static const struct global_file globals[] = {
|
||||||
// Disks and partitions, in Linux's format. See blkdev_info().
|
// Disks and partitions, in Linux's format. See blkdev_info().
|
||||||
{ "partitions", 21 },
|
{ "partitions", 21 },
|
||||||
{ "kmsg", 17 },
|
{ "kmsg", 17 },
|
||||||
|
// A program's name: from its next exec on, it and everything it starts log
|
||||||
|
// every syscall. Empty to stop. See strace_call() in kernel/sys/syscall.c.
|
||||||
|
{ "strace", 31 },
|
||||||
};
|
};
|
||||||
#define NGLOBALS ((int)(sizeof(globals) / sizeof(globals[0])))
|
#define NGLOBALS ((int)(sizeof(globals) / sizeof(globals[0])))
|
||||||
|
|
||||||
|
|
@ -132,6 +137,11 @@ static const struct global_file pidfiles[] = {
|
||||||
// One symlink per open descriptor, named by its number and pointing at the
|
// One symlink per open descriptor, named by its number and pointing at the
|
||||||
// path it was opened by, or at "socket:[ino]" and the like.
|
// path it was opened by, or at "socket:[ino]" and the like.
|
||||||
{ "fd", PF_FD },
|
{ "fd", PF_FD },
|
||||||
|
// The process's threads, one directory each, named by thread id. A process
|
||||||
|
// here is one thread, so task/ holds a single entry, the pid, and it *is*
|
||||||
|
// /proc/<pid> -- the same inode. htop reads every process through it and
|
||||||
|
// shows nothing at all without it.
|
||||||
|
{ "task", PF_TASK },
|
||||||
};
|
};
|
||||||
#define NPIDFILES ((int)(sizeof(pidfiles) / sizeof(pidfiles[0])))
|
#define NPIDFILES ((int)(sizeof(pidfiles) / sizeof(pidfiles[0])))
|
||||||
|
|
||||||
|
|
@ -201,8 +211,16 @@ gen_pid_stat(struct sb *s, const struct procinfo *pi)
|
||||||
// header and nothing else: ps with no arguments shows only the processes on
|
// header and nothing else: ps with no arguments shows only the processes on
|
||||||
// *this* terminal, and nothing could ever match. `ps -e` always worked.
|
// *this* terminal, and nothing could ever match. `ps -e` always worked.
|
||||||
sbputc(s, ' ');
|
sbputc(s, ' ');
|
||||||
|
// A pty is Linux's (136, N), the same number stat() gives /dev/pts/N.
|
||||||
|
if (pi->ctty >= PTY_CTTY)
|
||||||
|
sbputi(s, (136 << 8) | (pi->ctty - PTY_CTTY));
|
||||||
|
else
|
||||||
sbputi(s, pi->ctty ? ((1 << 8) | (10 + pi->ctty)) : 0);
|
sbputi(s, pi->ctty ? ((1 << 8) | (10 + pi->ctty)) : 0);
|
||||||
sbputs(s, " -1 0"); // 8 tpgid, 9 flags
|
// 8 tpgid, 9 flags. PF_KTHREAD (0x00200000) on a kernel thread is how htop
|
||||||
|
// and ps tell one from a process and hide it; without it acpi and dbgd
|
||||||
|
// listed as programs of four bytes each.
|
||||||
|
sbputs(s, " -1 ");
|
||||||
|
sbputi(s, pi->kthread ? 0x00200000 : 0);
|
||||||
|
|
||||||
// 10..13: minflt cminflt majflt cmajflt. There is no fault accounting, and
|
// 10..13: minflt cminflt majflt cmajflt. There is no fault accounting, and
|
||||||
// a fabricated number is worse than none -- top would draw a plausible graph
|
// a fabricated number is worse than none -- top would draw a plausible graph
|
||||||
|
|
@ -320,18 +338,20 @@ procfs_iload(struct inode *ip)
|
||||||
ip->valid = 1;
|
ip->valid = 1;
|
||||||
ip->nlink = 1;
|
ip->nlink = 1;
|
||||||
ip->major = ip->minor = 0;
|
ip->major = ip->minor = 0;
|
||||||
// Read-only, with two exceptions: /proc/sysrq is the deliberate write target
|
// Read-only, with three exceptions: /proc/sysrq is the deliberate write
|
||||||
// (`echo c > /proc/sysrq` to crash on purpose), and /proc/schinit is where
|
// target (`echo c > /proc/sysrq` to crash on purpose), /proc/schinit is where
|
||||||
// PID 1 publishes its job table. Marking everything 2 made filewrite()
|
// PID 1 publishes its job table, and /proc/strace takes a name to trace. Marking everything 2 made filewrite()
|
||||||
// refuse with "permission denied", which the USB suite caught immediately --
|
// refuse with "permission denied", which the USB suite caught immediately --
|
||||||
// three of its checks drive the disk through sysrq.
|
// three of its checks drive the disk through sysrq.
|
||||||
ip->rwmode = (INO_IS_GLOBAL(ino) &&
|
ip->rwmode = (INO_IS_GLOBAL(ino) &&
|
||||||
((ino & 0xFF) == 10 || (ino & 0xFF) == 16)) ? 3 : 2;
|
((ino & 0xFF) == 10 || (ino & 0xFF) == 16 ||
|
||||||
|
(ino & 0xFF) == 31)) ? 3 : 2;
|
||||||
ip->showmode = 1;
|
ip->showmode = 1;
|
||||||
ip->atime = ip->mtime = ip->ctime = 0;
|
ip->atime = ip->mtime = ip->ctime = 0;
|
||||||
|
|
||||||
if (ino == PROCFS_ROOT ||
|
if (ino == PROCFS_ROOT ||
|
||||||
(!INO_IS_GLOBAL(ino) && (INO_FILE(ino) == PF_DIR || INO_FILE(ino) == PF_FD))) {
|
(!INO_IS_GLOBAL(ino) && (INO_FILE(ino) == PF_DIR || INO_FILE(ino) == PF_FD ||
|
||||||
|
INO_FILE(ino) == PF_TASK))) {
|
||||||
ip->type = T_DIR;
|
ip->type = T_DIR;
|
||||||
ip->size = 0;
|
ip->size = 0;
|
||||||
ip->nlink = 2;
|
ip->nlink = 2;
|
||||||
|
|
@ -460,6 +480,8 @@ procfs_write(struct inode *ip, int user_src, uint64 src, uint64 off, uint n)
|
||||||
// should be doing, and PID 1 already knows when the table changed.
|
// should be doing, and PID 1 already knows when the table changed.
|
||||||
if (INO_IS_GLOBAL(ip->inum) && (ip->inum & 0xFF) == 16)
|
if (INO_IS_GLOBAL(ip->inum) && (ip->inum & 0xFF) == 16)
|
||||||
return proc_write_schinit(user_src, src, n);
|
return proc_write_schinit(user_src, src, n);
|
||||||
|
if (INO_IS_GLOBAL(ip->inum) && (ip->inum & 0xFF) == 31)
|
||||||
|
return proc_write_strace(user_src, src, n);
|
||||||
(void)off;
|
(void)off;
|
||||||
return -EROFS;
|
return -EROFS;
|
||||||
}
|
}
|
||||||
|
|
@ -495,9 +517,17 @@ procfs_dirlookup(struct inode *dp, char *name, uint *poff)
|
||||||
if (namecmp(name, ".") == 0)
|
if (namecmp(name, ".") == 0)
|
||||||
return iget(dp->dev, dp->inum);
|
return iget(dp->dev, dp->inum);
|
||||||
if (namecmp(name, "..") == 0) // /proc/<pid>/.. is /proc
|
if (namecmp(name, "..") == 0) // /proc/<pid>/.. is /proc
|
||||||
return iget(dp->dev, INO_FILE(dp->inum) == PF_FD ? (dp->inum & ~0xFFULL)
|
return iget(dp->dev, INO_FILE(dp->inum) == PF_FD ||
|
||||||
|
INO_FILE(dp->inum) == PF_TASK ? (dp->inum & ~0xFFULL)
|
||||||
: PROCFS_ROOT);
|
: PROCFS_ROOT);
|
||||||
|
|
||||||
|
// Inside /proc/<pid>/task/: the one thread, which is the process.
|
||||||
|
if (dp->inum != PROCFS_ROOT && INO_FILE(dp->inum) == PF_TASK) {
|
||||||
|
if (name_to_pid(name) != INO_PID(dp->inum))
|
||||||
|
return 0;
|
||||||
|
return iget(dp->dev, (dp->inum & ~0xFFULL) | PF_DIR);
|
||||||
|
}
|
||||||
|
|
||||||
// Inside /proc/<pid>/fd/: a number that is an open descriptor.
|
// Inside /proc/<pid>/fd/: a number that is an open descriptor.
|
||||||
if (dp->inum != PROCFS_ROOT && INO_FILE(dp->inum) == PF_FD) {
|
if (dp->inum != PROCFS_ROOT && INO_FILE(dp->inum) == PF_FD) {
|
||||||
char tmp[4];
|
char tmp[4];
|
||||||
|
|
@ -631,12 +661,18 @@ procfs_getdents(struct inode *dp, char *out, int cap, uint64 *off)
|
||||||
utoa((uint)i, name);
|
utoa((uint)i, name);
|
||||||
EMIT((dp->inum & ~0xFFULL) | PF_FDENT | i, DT_LNK_, name);
|
EMIT((dp->inum & ~0xFFULL) | PF_FDENT | i, DT_LNK_, name);
|
||||||
}
|
}
|
||||||
|
} else if (INO_FILE(dp->inum) == PF_TASK) {
|
||||||
|
EMIT(dp->inum, DT_DIR_, ".");
|
||||||
|
EMIT(dp->inum & ~0xFFULL, DT_DIR_, "..");
|
||||||
|
utoa((uint)INO_PID(dp->inum), name);
|
||||||
|
EMIT((dp->inum & ~0xFFULL) | PF_DIR, DT_DIR_, name);
|
||||||
} else {
|
} else {
|
||||||
EMIT(dp->inum, DT_DIR_, ".");
|
EMIT(dp->inum, DT_DIR_, ".");
|
||||||
EMIT(PROCFS_ROOT, DT_DIR_, "..");
|
EMIT(PROCFS_ROOT, DT_DIR_, "..");
|
||||||
for (i = 0; i < NPIDFILES; i++)
|
for (i = 0; i < NPIDFILES; i++)
|
||||||
EMIT((dp->inum & ~0xFFULL) | pidfiles[i].id,
|
EMIT((dp->inum & ~0xFFULL) | pidfiles[i].id,
|
||||||
pidfiles[i].id == PF_FD ? DT_DIR_ : pidfiles[i].id == PF_EXE ? DT_LNK_ : DT_REG_,
|
pidfiles[i].id == PF_FD || pidfiles[i].id == PF_TASK ? DT_DIR_ :
|
||||||
|
pidfiles[i].id == PF_EXE ? DT_LNK_ : DT_REG_,
|
||||||
pidfiles[i].name);
|
pidfiles[i].name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,7 @@ schfs_vfs_iload(struct inode *ip)
|
||||||
ip->nlink = in.nlink > 0x7fffffffu ? 0x7fffffff : (int)in.nlink;
|
ip->nlink = in.nlink > 0x7fffffffu ? 0x7fffffff : (int)in.nlink;
|
||||||
ip->size = in.size;
|
ip->size = in.size;
|
||||||
ip->rwmode = RW;
|
ip->rwmode = RW;
|
||||||
|
ip->mode = in.mode & 07777; // 0 on disk: never recorded
|
||||||
ip->supermode = 0;
|
ip->supermode = 0;
|
||||||
ip->showmode = 1;
|
ip->showmode = 1;
|
||||||
ip->atime = (uint)in.atime;
|
ip->atime = (uint)in.atime;
|
||||||
|
|
@ -134,6 +135,8 @@ schfs_vfs_istore(struct inode *ip)
|
||||||
in.mode = ip->type == T_DIR ? 0755 : 0644;
|
in.mode = ip->type == T_DIR ? 0755 : 0644;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ip->mode)
|
||||||
|
in.mode = ip->mode & 07777;
|
||||||
in.type = type_from_vfs(ip->type);
|
in.type = type_from_vfs(ip->type);
|
||||||
in.rdev_major = (uint16_t)ip->major;
|
in.rdev_major = (uint16_t)ip->major;
|
||||||
in.rdev_minor = (uint16_t)ip->minor;
|
in.rdev_minor = (uint16_t)ip->minor;
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,11 @@ alloc_locked(int type)
|
||||||
s->ref = 1;
|
s->ref = 1;
|
||||||
s->type = type;
|
s->type = type;
|
||||||
s->state = SS_UNBOUND;
|
s->state = SS_UNBOUND;
|
||||||
|
if(myproc()){
|
||||||
|
s->cred_pid = myproc()->pid;
|
||||||
|
s->cred_uid = myproc()->euid;
|
||||||
|
s->cred_gid = myproc()->egid;
|
||||||
|
}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -852,6 +857,9 @@ sockconnect(struct socket *s, uint dev, uint64 ino)
|
||||||
|
|
||||||
peer->peer = s;
|
peer->peer = s;
|
||||||
peer->state = SS_CONNECTED;
|
peer->state = SS_CONNECTED;
|
||||||
|
peer->cred_pid = srv->cred_pid;
|
||||||
|
peer->cred_uid = srv->cred_uid;
|
||||||
|
peer->cred_gid = srv->cred_gid;
|
||||||
s->peer = peer;
|
s->peer = peer;
|
||||||
s->state = SS_CONNECTED;
|
s->state = SS_CONNECTED;
|
||||||
|
|
||||||
|
|
@ -1016,3 +1024,21 @@ socksendto(struct socket *s, char *buf, int n, uint addr, ushort port)
|
||||||
return -EAFNOSUPPORT; // an AF_INET destination on an AF_UNIX socket
|
return -EAFNOSUPPORT; // an AF_INET destination on an AF_UNIX socket
|
||||||
return sockwrite(s, buf, n);
|
return sockwrite(s, buf, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SO_PEERCRED: the credentials recorded for the far end of a connected Unix
|
||||||
|
// socket. -1 if there is no far end.
|
||||||
|
int
|
||||||
|
sock_peercred(struct socket *s, int *pid, uint *uid, uint *gid)
|
||||||
|
{
|
||||||
|
int r = -1;
|
||||||
|
|
||||||
|
acquire(&stab.lock);
|
||||||
|
if(s->domain != AF_INET && s->peer){
|
||||||
|
*pid = s->peer->cred_pid;
|
||||||
|
*uid = s->peer->cred_uid;
|
||||||
|
*gid = s->peer->cred_gid;
|
||||||
|
r = 0;
|
||||||
|
}
|
||||||
|
release(&stab.lock);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,10 @@ struct {
|
||||||
struct proc proc[NPROC];
|
struct proc proc[NPROC];
|
||||||
} ptable;
|
} ptable;
|
||||||
|
|
||||||
|
// CPU time of every process already reaped, so that /proc/stat's busy time
|
||||||
|
// does not drop each time one is. Under ptable.lock, like the processes.
|
||||||
|
static uint64 reaped_utime, reaped_stime;
|
||||||
|
|
||||||
// Kernel stacks, one per ptable slot, statically.
|
// Kernel stacks, one per ptable slot, statically.
|
||||||
//
|
//
|
||||||
// **They used to be a single kalloc() page**, and a measurement said that was
|
// **They used to be a single kalloc() page**, and a measurement said that was
|
||||||
|
|
@ -313,6 +317,8 @@ found:
|
||||||
p->pending_signals = 0;
|
p->pending_signals = 0;
|
||||||
p->alarm_at = p->alarm_every = 0; // a child's timers start disarmed
|
p->alarm_at = p->alarm_every = 0; // a child's timers start disarmed
|
||||||
p->utime = p->stime = p->cutime = p->cstime = 0;
|
p->utime = p->stime = p->cutime = p->cstime = 0;
|
||||||
|
p->strace = 0;
|
||||||
|
p->umask = 022;
|
||||||
|
|
||||||
// A recycled slot must not inherit a dead process's place in the queue --
|
// A recycled slot must not inherit a dead process's place in the queue --
|
||||||
// in either direction. sched_init_proc starts it at min_vruntime, which is
|
// in either direction. sched_init_proc starts it at min_vruntime, which is
|
||||||
|
|
@ -509,6 +515,8 @@ fork(void)
|
||||||
np->fs_base = myproc()->fs_base;
|
np->fs_base = myproc()->fs_base;
|
||||||
|
|
||||||
safestrcpy(np->name, myproc()->name, sizeof(myproc()->name));
|
safestrcpy(np->name, myproc()->name, sizeof(myproc()->name));
|
||||||
|
np->strace = myproc()->strace;
|
||||||
|
np->umask = myproc()->umask;
|
||||||
|
|
||||||
// The child inherits the parent's place in the queue, priority and all.
|
// The child inherits the parent's place in the queue, priority and all.
|
||||||
//
|
//
|
||||||
|
|
@ -746,6 +754,8 @@ waitpid(int pid, int *status, int options, uint64 *ut, uint64 *st)
|
||||||
// of processes rather than one generation deep.
|
// of processes rather than one generation deep.
|
||||||
myproc()->cutime += p->utime + p->cutime;
|
myproc()->cutime += p->utime + p->cutime;
|
||||||
myproc()->cstime += p->stime + p->cstime;
|
myproc()->cstime += p->stime + p->cstime;
|
||||||
|
reaped_utime += p->utime;
|
||||||
|
reaped_stime += p->stime;
|
||||||
// wait4()'s fourth argument wants the *child's* usage, not the
|
// wait4()'s fourth argument wants the *child's* usage, not the
|
||||||
// parent's running total, so it has to be read here -- three lines
|
// parent's running total, so it has to be read here -- three lines
|
||||||
// further down the slot is UNUSED and these are zero.
|
// further down the slot is UNUSED and these are zero.
|
||||||
|
|
@ -1786,6 +1796,11 @@ proc_setsid(void)
|
||||||
}
|
}
|
||||||
myproc()->sid = myproc()->pid;
|
myproc()->sid = myproc()->pid;
|
||||||
myproc()->pgid = myproc()->pid;
|
myproc()->pgid = myproc()->pid;
|
||||||
|
// A new session starts with no controlling terminal; that is what lets the
|
||||||
|
// first terminal it opens become one. Keeping the old one meant a tmux pane
|
||||||
|
// still had tty1 as its /dev/tty, and zsh and htop drew straight onto the
|
||||||
|
// console underneath tmux instead of into their pane.
|
||||||
|
myproc()->ctty = 0;
|
||||||
r = myproc()->sid;
|
r = myproc()->sid;
|
||||||
release(&ptable.lock);
|
release(&ptable.lock);
|
||||||
return r;
|
return r;
|
||||||
|
|
@ -1913,6 +1928,7 @@ proc_snapshot(int idx, struct procinfo *out)
|
||||||
out->nivcsw = p->nivcsw;
|
out->nivcsw = p->nivcsw;
|
||||||
safestrcpy(out->name, p->name, sizeof(out->name));
|
safestrcpy(out->name, p->name, sizeof(out->name));
|
||||||
safestrcpy(out->exe, p->exe, sizeof(out->exe));
|
safestrcpy(out->exe, p->exe, sizeof(out->exe));
|
||||||
|
out->kthread = p->kfn != 0;
|
||||||
release(&ptable.lock);
|
release(&ptable.lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -2012,3 +2028,24 @@ proc_kill_init(void)
|
||||||
if (initproc)
|
if (initproc)
|
||||||
initproc->killed = 1;
|
initproc->killed = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ticks spent in user and kernel mode by every process, living or reaped.
|
||||||
|
// What /proc/stat's busy columns are made of; the rest of the time was idle.
|
||||||
|
void
|
||||||
|
proc_cputime(uint64 *user, uint64 *sys)
|
||||||
|
{
|
||||||
|
struct proc *p;
|
||||||
|
uint64 u, s;
|
||||||
|
|
||||||
|
acquire(&ptable.lock);
|
||||||
|
u = reaped_utime;
|
||||||
|
s = reaped_stime;
|
||||||
|
for (p = ptable.proc; p < &ptable.proc[NPROC]; p++)
|
||||||
|
if (p->state != UNUSED) {
|
||||||
|
u += p->utime;
|
||||||
|
s += p->stime;
|
||||||
|
}
|
||||||
|
release(&ptable.lock);
|
||||||
|
*user = u;
|
||||||
|
*sys = s;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
#include "fs/schfs.h" /* SCHFS_BLKSIZE, SCHFS_NAME_MAX — statfs reports them */
|
#include "fs/schfs.h" /* SCHFS_BLKSIZE, SCHFS_NAME_MAX — statfs reports them */
|
||||||
#include "gfx/gfx.h" /* gfx_cols/gfx_rows — TIOCGWINSZ reports them */
|
#include "gfx/gfx.h" /* gfx_cols/gfx_rows — TIOCGWINSZ reports them */
|
||||||
#include "dev/vt.h" /* the terminals the TIOC and VT_ ioctls act on */
|
#include "dev/vt.h" /* the terminals the TIOC and VT_ ioctls act on */
|
||||||
|
#include "dev/pty.h"
|
||||||
#include "dev/pciuser.h"
|
#include "dev/pciuser.h"
|
||||||
#include "dev/keymap.h" /* KB_*LAYOUT — the keyboard layout catalogue */
|
#include "dev/keymap.h" /* KB_*LAYOUT — the keyboard layout catalogue */
|
||||||
#include "net/netuser.h"
|
#include "net/netuser.h"
|
||||||
|
|
@ -83,6 +84,10 @@ linux_tty_rdev(uint32 major, uint32 minor)
|
||||||
if (major == 1 && minor >= 11 && minor <= 16) return linux_makedev(4, minor - 10);
|
if (major == 1 && minor >= 11 && minor <= 16) return linux_makedev(4, minor - 10);
|
||||||
if (major == 1 && minor == 10) return linux_makedev(5, 0);
|
if (major == 1 && minor == 10) return linux_makedev(5, 0);
|
||||||
if (major == 1 && minor == 1) return linux_makedev(5, 1);
|
if (major == 1 && minor == 1) return linux_makedev(5, 1);
|
||||||
|
// Pseudo-terminals, as Linux numbers them: ptmx (5, 2), slaves 136.
|
||||||
|
if (major == PTMX_MAJOR) return linux_makedev(5, 2);
|
||||||
|
if (major == PTS_MAJOR) return linux_makedev(136, minor);
|
||||||
|
if (major == PTM_MAJOR) return linux_makedev(128, minor);
|
||||||
return linux_makedev(major, minor);
|
return linux_makedev(major, minor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -112,6 +117,10 @@ stati_linux(struct inode *ip, struct linux_stat *st)
|
||||||
case T_SOCK: type_bits = LINUX_S_IFSOCK; perm_bits = 0755; break;
|
case T_SOCK: type_bits = LINUX_S_IFSOCK; perm_bits = 0755; break;
|
||||||
default: type_bits = LINUX_S_IFREG; perm_bits = (ip->rwmode & 2) ? 0755 : 0555; break;
|
default: type_bits = LINUX_S_IFREG; perm_bits = (ip->rwmode & 2) ? 0755 : 0555; break;
|
||||||
}
|
}
|
||||||
|
// A filesystem that keeps real permission bits -- SchFS does -- reports
|
||||||
|
// those instead. tmux, ssh and gpg refuse a directory others can read.
|
||||||
|
if (ip->mode)
|
||||||
|
perm_bits = ip->mode & 07777;
|
||||||
st->st_mode = type_bits | perm_bits;
|
st->st_mode = type_bits | perm_bits;
|
||||||
|
|
||||||
// Which device this node actually is. Left at zero until now, so every
|
// Which device this node actually is. Left at zero until now, so every
|
||||||
|
|
@ -264,9 +273,21 @@ path_toolong(const char *path)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The mode argument of a Linux open/mkdir, or -1 from a native program, whose
|
||||||
|
// calls have no such argument and leave whatever was in the register.
|
||||||
|
static int
|
||||||
|
linux_arg_mode(int n)
|
||||||
|
{
|
||||||
|
int mode;
|
||||||
|
if (!myproc()->syscall_map)
|
||||||
|
return -1;
|
||||||
|
argint(n, &mode);
|
||||||
|
return mode & 07777;
|
||||||
|
}
|
||||||
|
|
||||||
static struct inode*
|
static struct inode*
|
||||||
createat_x(int dirfd, char *path, short type, short major, short minor,
|
createat_x(int dirfd, char *path, short type, short major, short minor,
|
||||||
int *err)
|
int *err, int mode)
|
||||||
{
|
{
|
||||||
struct inode *ip, *dp;
|
struct inode *ip, *dp;
|
||||||
char name[VFS_NAME_MAX + 1];
|
char name[VFS_NAME_MAX + 1];
|
||||||
|
|
@ -320,6 +341,14 @@ createat_x(int dirfd, char *path, short type, short major, short minor,
|
||||||
ilock(ip);
|
ilock(ip);
|
||||||
ip->major = major; ip->minor = minor; ip->nlink = 1;
|
ip->major = major; ip->minor = minor; ip->nlink = 1;
|
||||||
ip->showmode = 1;
|
ip->showmode = 1;
|
||||||
|
// The permission bits asked for, less the umask -- or, from a caller that
|
||||||
|
// has none to give, what open() and mkdir() would ask for. A symlink's
|
||||||
|
// are 0777 on Linux whatever the umask, and nothing reads them.
|
||||||
|
if (type == T_SYMLINK)
|
||||||
|
ip->mode = 0777;
|
||||||
|
else
|
||||||
|
ip->mode = (uint)(mode >= 0 ? mode : type == T_DIR ? 0777 : 0666)
|
||||||
|
& 07777 & ~myproc()->umask;
|
||||||
iupdate(ip);
|
iupdate(ip);
|
||||||
if (type == T_DIR) {
|
if (type == T_DIR) {
|
||||||
dp->nlink++;
|
dp->nlink++;
|
||||||
|
|
@ -343,7 +372,7 @@ createat_x(int dirfd, char *path, short type, short major, short minor,
|
||||||
static struct inode*
|
static struct inode*
|
||||||
create(char *path, short type, short major, short minor)
|
create(char *path, short type, short major, short minor)
|
||||||
{
|
{
|
||||||
return createat_x(AT_FDCWD, path, type, major, minor, 0);
|
return createat_x(AT_FDCWD, path, type, major, minor, 0, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
|
|
@ -571,12 +600,16 @@ addr_t sys_pipe(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The flags were ignored, and they are the point of pipe2: libevent makes its
|
||||||
|
// signal pipe with O_NONBLOCK and drains it until EAGAIN, so the drain blocked
|
||||||
|
// on the read after the last byte and tmux's client stopped dead.
|
||||||
addr_t sys_pipe2(void)
|
addr_t sys_pipe2(void)
|
||||||
{
|
{
|
||||||
addr_t fdarray;
|
addr_t fdarray;
|
||||||
struct file *rf, *wf;
|
struct file *rf, *wf;
|
||||||
int fd0, fd1;
|
int fd0, fd1, flags;
|
||||||
if (argaddr(0, &fdarray) < 0) return -1;
|
if (argaddr(0, &fdarray) < 0) return -1;
|
||||||
|
argint(1, &flags);
|
||||||
int *fds = (int*)user_ptr(fdarray, 2 * sizeof(int));
|
int *fds = (int*)user_ptr(fdarray, 2 * sizeof(int));
|
||||||
if (!fds || pipealloc(&rf, &wf) < 0) return -1;
|
if (!fds || pipealloc(&rf, &wf) < 0) return -1;
|
||||||
fd0 = fd1 = -1;
|
fd0 = fd1 = -1;
|
||||||
|
|
@ -585,6 +618,11 @@ addr_t sys_pipe2(void)
|
||||||
fileclose(rf); fileclose(wf);
|
fileclose(rf); fileclose(wf);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
rf->nonblock = wf->nonblock = (flags & O_NONBLOCK) != 0;
|
||||||
|
if (flags & O_CLOEXEC) {
|
||||||
|
myproc()->ofile_flags[fd0] |= FD_CLOEXEC;
|
||||||
|
myproc()->ofile_flags[fd1] |= FD_CLOEXEC;
|
||||||
|
}
|
||||||
fds[0] = fd0; fds[1] = fd1;
|
fds[0] = fd0; fds[1] = fd1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -642,7 +680,8 @@ addr_t sys_open(void)
|
||||||
if (ip->type == T_DIR) { iunlockput(ip); end_op(); return -EISDIR; }
|
if (ip->type == T_DIR) { iunlockput(ip); end_op(); return -EISDIR; }
|
||||||
} else
|
} else
|
||||||
ip = createat_x(AT_FDCWD, path,
|
ip = createat_x(AT_FDCWD, path,
|
||||||
(omode & O_EXTENT) ? T_EXTENT : T_FILE, 0, 0, &cerr);
|
(omode & O_EXTENT) ? T_EXTENT : T_FILE, 0, 0, &cerr,
|
||||||
|
linux_arg_mode(2));
|
||||||
// A refusal because the filesystem is read-only is not "no such file".
|
// A refusal because the filesystem is read-only is not "no such file".
|
||||||
// A shell prints the same sentence for every create failure, so
|
// A shell prints the same sentence for every create failure, so
|
||||||
// returning ENOENT here made writing to a snapshot look like a missing
|
// returning ENOENT here made writing to a snapshot look like a missing
|
||||||
|
|
@ -660,6 +699,13 @@ addr_t sys_open(void)
|
||||||
if (ip->type == T_DIR && (omode & O_ACCMODE) != O_RDONLY) { iunlockput(ip); end_op(); return -EISDIR; }
|
if (ip->type == T_DIR && (omode & O_ACCMODE) != O_RDONLY) { iunlockput(ip); end_op(); return -EISDIR; }
|
||||||
if ((omode & O_TRUNC) && ip->type == T_FILE) itrunc(ip);
|
if ((omode & O_TRUNC) && ip->type == T_FILE) itrunc(ip);
|
||||||
}
|
}
|
||||||
|
// /dev/ptmx: every open is a new pseudo-terminal, and the file holds its
|
||||||
|
// master rather than the ptmx node. See kernel/dev/pty.c.
|
||||||
|
if (ip->type == T_DEV && ip->major == PTMX_MAJOR) {
|
||||||
|
struct inode *m = ptmx_open(ip);
|
||||||
|
if (!m) { iunlockput(ip); end_op(); return -ENOSPC; }
|
||||||
|
ip = m;
|
||||||
|
}
|
||||||
if (!(f = filealloc()) || (fd = fdalloc(f)) < 0) {
|
if (!(f = filealloc()) || (fd = fdalloc(f)) < 0) {
|
||||||
if (f) fileclose(f);
|
if (f) fileclose(f);
|
||||||
iunlockput(ip); end_op(); return -EMFILE;
|
iunlockput(ip); end_op(); return -EMFILE;
|
||||||
|
|
@ -699,7 +745,8 @@ addr_t sys_openat(void)
|
||||||
if ((ip = open_existing(dirfd, path, omode)) != 0) {
|
if ((ip = open_existing(dirfd, path, omode)) != 0) {
|
||||||
ilock(ip);
|
ilock(ip);
|
||||||
if (ip->type == T_DIR) { iunlockput(ip); end_op(); return -EISDIR; }
|
if (ip->type == T_DIR) { iunlockput(ip); end_op(); return -EISDIR; }
|
||||||
} else if (!(ip = createat_x(dirfd, path, T_FILE, 0, 0, &cerr))) {
|
} else if (!(ip = createat_x(dirfd, path, T_FILE, 0, 0, &cerr,
|
||||||
|
linux_arg_mode(3)))) {
|
||||||
end_op();
|
end_op();
|
||||||
return cerr ? -cerr : -ENOENT;
|
return cerr ? -cerr : -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
@ -717,6 +764,13 @@ addr_t sys_openat(void)
|
||||||
// depending on which entry point libc happened to call.
|
// depending on which entry point libc happened to call.
|
||||||
if ((omode & O_TRUNC) && (ip->type == T_FILE || ip->type == T_EXTENT))
|
if ((omode & O_TRUNC) && (ip->type == T_FILE || ip->type == T_EXTENT))
|
||||||
itrunc(ip);
|
itrunc(ip);
|
||||||
|
// /dev/ptmx: every open is a new pseudo-terminal, and the file holds its
|
||||||
|
// master rather than the ptmx node. See kernel/dev/pty.c.
|
||||||
|
if (ip->type == T_DEV && ip->major == PTMX_MAJOR) {
|
||||||
|
struct inode *m = ptmx_open(ip);
|
||||||
|
if (!m) { iunlockput(ip); end_op(); return -ENOSPC; }
|
||||||
|
ip = m;
|
||||||
|
}
|
||||||
if (!(f = filealloc()) || (fd = fdalloc(f)) < 0) {
|
if (!(f = filealloc()) || (fd = fdalloc(f)) < 0) {
|
||||||
if (f) fileclose(f);
|
if (f) fileclose(f);
|
||||||
iunlockput(ip); end_op(); return -EMFILE;
|
iunlockput(ip); end_op(); return -EMFILE;
|
||||||
|
|
@ -905,7 +959,8 @@ addr_t sys_mkdir(void)
|
||||||
return -EEXIST;
|
return -EEXIST;
|
||||||
}
|
}
|
||||||
int cerr = 0;
|
int cerr = 0;
|
||||||
if (!(ip = createat_x(AT_FDCWD, path, T_DIR, 0, 0, &cerr))) {
|
if (!(ip = createat_x(AT_FDCWD, path, T_DIR, 0, 0, &cerr,
|
||||||
|
linux_arg_mode(1)))) {
|
||||||
end_op();
|
end_op();
|
||||||
return cerr ? -cerr : -ENOENT;
|
return cerr ? -cerr : -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
@ -948,7 +1003,7 @@ do_mknod(int dirfd, char *path, uint32 mode, uint64 dev)
|
||||||
if (ex) { iput(ex); end_op(); return -EEXIST; }
|
if (ex) { iput(ex); end_op(); return -EEXIST; }
|
||||||
int cerr = 0;
|
int cerr = 0;
|
||||||
struct inode *ip = createat_x(dirfd, path, type, (short)major,
|
struct inode *ip = createat_x(dirfd, path, type, (short)major,
|
||||||
(short)minor, &cerr);
|
(short)minor, &cerr, (int)(mode & 07777));
|
||||||
if (!ip) { end_op(); return cerr ? -cerr : -EEXIST; }
|
if (!ip) { end_op(); return cerr ? -cerr : -EEXIST; }
|
||||||
iunlockput(ip); end_op();
|
iunlockput(ip); end_op();
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -1275,7 +1330,7 @@ do_symlink(int newdirfd, char *target, char *linkpath)
|
||||||
return -EEXIST;
|
return -EEXIST;
|
||||||
}
|
}
|
||||||
int cerr = 0;
|
int cerr = 0;
|
||||||
if (!(ip = createat_x(newdirfd, linkpath, T_SYMLINK, 0, 0, &cerr))) {
|
if (!(ip = createat_x(newdirfd, linkpath, T_SYMLINK, 0, 0, &cerr, -1))) {
|
||||||
end_op();
|
end_op();
|
||||||
return cerr ? -cerr : -ENOENT;
|
return cerr ? -cerr : -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
@ -1503,6 +1558,9 @@ addr_t sys_ioctl(void)
|
||||||
if (is_dev && f->ip->major == AUDIO_MAJOR)
|
if (is_dev && f->ip->major == AUDIO_MAJOR)
|
||||||
return dsp_ioctl((int)request, argp);
|
return dsp_ioctl((int)request, argp);
|
||||||
|
|
||||||
|
if (is_dev && (f->ip->major == PTM_MAJOR || f->ip->major == PTS_MAJOR))
|
||||||
|
return pty_ioctl(f->ip, (uint)request, argp);
|
||||||
|
|
||||||
int is_tty = (is_dev && f->ip->major == 1);
|
int is_tty = (is_dev && f->ip->major == 1);
|
||||||
if (!is_tty) {
|
if (!is_tty) {
|
||||||
// -ENOTTY, not 0.
|
// -ENOTTY, not 0.
|
||||||
|
|
@ -1776,8 +1834,50 @@ addr_t sys_ioctl(void)
|
||||||
// createat() — every *at() syscall below uses them for dirfd resolution.)
|
// createat() — every *at() syscall below uses them for dirfd resolution.)
|
||||||
|
|
||||||
// chmod / fchmod / chown / fchown / lchown — stubs (no permission model yet).
|
// chmod / fchmod / chown / fchown / lchown — stubs (no permission model yet).
|
||||||
addr_t sys_chmod(void) { return 0; }
|
// The new bits go in the inode and through iupdate to the filesystem. One
|
||||||
addr_t sys_fchmod(void) { return 0; }
|
// that keeps none (FAT) holds them only while the inode is cached, which is
|
||||||
|
// as much as it can; there is no failure to report.
|
||||||
|
static int
|
||||||
|
chmod_inode(struct inode *ip, int mode)
|
||||||
|
{
|
||||||
|
if (fs_readonly(ip->dev))
|
||||||
|
return -EROFS;
|
||||||
|
ilock(ip);
|
||||||
|
ip->mode = (uint)mode & 07777;
|
||||||
|
if (ip->mode == 0)
|
||||||
|
ip->mode = 01000000; // "no bits", as distinct from "never set"
|
||||||
|
iupdate(ip);
|
||||||
|
iunlock(ip);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
addr_t sys_chmod(void)
|
||||||
|
{
|
||||||
|
char *path;
|
||||||
|
int mode, err, r;
|
||||||
|
struct inode *ip;
|
||||||
|
if (argstr(0, &path) < 0) return -EFAULT;
|
||||||
|
argint(1, &mode);
|
||||||
|
begin_op();
|
||||||
|
if (!(ip = at_nameif(AT_FDCWD, path, &err))) { end_op(); return -err; }
|
||||||
|
r = chmod_inode(ip, mode);
|
||||||
|
iput(ip);
|
||||||
|
end_op();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
addr_t sys_fchmod(void)
|
||||||
|
{
|
||||||
|
struct file *f;
|
||||||
|
int mode, r;
|
||||||
|
if (argfd(0, 0, &f) < 0) return -EBADF;
|
||||||
|
argint(1, &mode);
|
||||||
|
if (f->type != FD_INODE || !f->ip) return 0;
|
||||||
|
begin_op();
|
||||||
|
r = chmod_inode(f->ip, mode);
|
||||||
|
end_op();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
addr_t sys_chown(void) { return 0; }
|
addr_t sys_chown(void) { return 0; }
|
||||||
addr_t sys_fchown(void) { return 0; }
|
addr_t sys_fchown(void) { return 0; }
|
||||||
addr_t sys_lchown(void) { return 0; }
|
addr_t sys_lchown(void) { return 0; }
|
||||||
|
|
@ -2054,7 +2154,8 @@ addr_t sys_mkdirat(void)
|
||||||
return -EEXIST;
|
return -EEXIST;
|
||||||
}
|
}
|
||||||
int cerr = 0;
|
int cerr = 0;
|
||||||
if (!(ip = createat_x(dirfd, path, T_DIR, 0, 0, &cerr))) {
|
if (!(ip = createat_x(dirfd, path, T_DIR, 0, 0, &cerr,
|
||||||
|
linux_arg_mode(2)))) {
|
||||||
end_op();
|
end_op();
|
||||||
return cerr ? -cerr : -ENOENT;
|
return cerr ? -cerr : -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
@ -2183,7 +2284,21 @@ addr_t sys_readlinkat(void)
|
||||||
return got;
|
return got;
|
||||||
}
|
}
|
||||||
|
|
||||||
addr_t sys_fchmodat(void) { return 0; } // stub
|
addr_t sys_fchmodat(void)
|
||||||
|
{
|
||||||
|
int dirfd, mode, err, r;
|
||||||
|
char *path;
|
||||||
|
struct inode *ip;
|
||||||
|
argint(0, &dirfd);
|
||||||
|
if (argstr(1, &path) < 0) return -EFAULT;
|
||||||
|
argint(2, &mode);
|
||||||
|
begin_op();
|
||||||
|
if (!(ip = at_nameif(dirfd, path, &err))) { end_op(); return -err; }
|
||||||
|
r = chmod_inode(ip, mode);
|
||||||
|
iput(ip);
|
||||||
|
end_op();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
addr_t sys_fchownat(void) { return 0; } // stub
|
addr_t sys_fchownat(void) { return 0; } // stub
|
||||||
|
|
||||||
addr_t sys_faccessat(void)
|
addr_t sys_faccessat(void)
|
||||||
|
|
|
||||||
|
|
@ -752,6 +752,23 @@ SYSCALL_DEFINE(getsockopt)
|
||||||
argaddr(3, &uval);
|
argaddr(3, &uval);
|
||||||
argaddr(4, &ulenp);
|
argaddr(4, &ulenp);
|
||||||
|
|
||||||
|
// SO_PEERCRED: who is on the other end of a Unix socket. tmux asks its
|
||||||
|
// client this and, told nothing, refuses it as a stranger.
|
||||||
|
if (level == 1 && name == 17) {
|
||||||
|
struct { int pid; uint uid, gid; } cr;
|
||||||
|
int want;
|
||||||
|
if (sock_peercred(s, &cr.pid, &cr.uid, &cr.gid) < 0)
|
||||||
|
return -ENOTCONN;
|
||||||
|
if (!uval || !ulenp || get_user(want, ulenp) < 0)
|
||||||
|
return -EFAULT;
|
||||||
|
if (want < (int)sizeof(cr))
|
||||||
|
return -EINVAL;
|
||||||
|
if (copy_to_user(uval, &cr, sizeof(cr)) < 0)
|
||||||
|
return -EFAULT;
|
||||||
|
want = sizeof(cr);
|
||||||
|
return put_user(want, ulenp) < 0 ? -EFAULT : 0;
|
||||||
|
}
|
||||||
|
|
||||||
if ((r = sockopt_get(s, level, name, &val)) < 0)
|
if ((r = sockopt_get(s, level, name, &val)) < 0)
|
||||||
return r;
|
return r;
|
||||||
return opt_out(uval, ulenp, val);
|
return opt_out(uval, ulenp, val);
|
||||||
|
|
@ -769,11 +786,11 @@ SYSCALL_DEFINE(getsockopt)
|
||||||
// docs/plan/foundations.md item 1, and docs/plan/hybrid.md mechanism 1.
|
// docs/plan/foundations.md item 1, and docs/plan/hybrid.md mechanism 1.
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
// Linux caps an iovec at UIO_MAXIOV = 1024. Thirty-two is the cap here for one
|
// Linux caps an iovec at UIO_MAXIOV = 1024, and so does this. It was 32, and
|
||||||
// reason: each entry is validated and copied individually, and a caller that
|
// tmux met it on its first message: imsg hands sendmsg every queued buffer at
|
||||||
// needs more than thirty-two separate buffers in one message is describing a
|
// once, one iovec each, and a client announcing its environment has dozens.
|
||||||
// problem this kernel has not met. EMSGSIZE is Linux's answer for too many.
|
// Nothing is sized by the count -- each entry is fetched and copied on its own.
|
||||||
#define SCH_IOV_MAX 32
|
#define SCH_IOV_MAX 1024
|
||||||
|
|
||||||
// The control buffer is copied onto the kernel stack, and that stack is 4 KB
|
// The control buffer is copied onto the kernel stack, and that stack is 4 KB
|
||||||
// (param.h, KSTACKSIZE). 256 bytes holds CMSG_SPACE(SCM_MAXFD * 4) six times
|
// (param.h, KSTACKSIZE). 256 bytes holds CMSG_SPACE(SCM_MAXFD * 4) six times
|
||||||
|
|
|
||||||
|
|
@ -272,7 +272,14 @@ sys_setgid(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
addr_t sys_umask(void) { return 022; }
|
addr_t sys_umask(void)
|
||||||
|
{
|
||||||
|
int mask;
|
||||||
|
argint(0, &mask);
|
||||||
|
uint old = myproc()->umask;
|
||||||
|
myproc()->umask = (uint)mask & 0777;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
addr_t
|
addr_t
|
||||||
sys_setreuid(void)
|
sys_setreuid(void)
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,26 @@ fetchstr(addr_t addr, char **pp)
|
||||||
return len;
|
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
|
static addr_t
|
||||||
fetcharg(int n)
|
fetcharg(int n)
|
||||||
{
|
{
|
||||||
|
|
@ -117,6 +137,10 @@ static addr_t (*syscalls[NSYSCALLS])(void) = {
|
||||||
void
|
void
|
||||||
syscall(struct trapframe *tf)
|
syscall(struct trapframe *tf)
|
||||||
{
|
{
|
||||||
|
if (myproc()->strace) {
|
||||||
|
strace_call(tf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
kstack_check_canary("syscall entry");
|
kstack_check_canary("syscall entry");
|
||||||
myproc()->tf = tf;
|
myproc()->tf = tf;
|
||||||
uint64 orig = myproc()->tf->rax;
|
uint64 orig = myproc()->tf->rax;
|
||||||
|
|
@ -139,3 +163,63 @@ syscall(struct trapframe *tf)
|
||||||
|
|
||||||
kstack_check_canary("syscall exit");
|
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");
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,3 +26,24 @@ xinit
|
||||||
windowmaker
|
windowmaker
|
||||||
xterm
|
xterm
|
||||||
font-dejavu
|
font-dejavu
|
||||||
|
# Stress: terminal programs that draw, multiplex and poll. htop and btop read
|
||||||
|
# /proc hard; tmux wants ptys, sockets and signals; mc does everything at once.
|
||||||
|
htop
|
||||||
|
btop
|
||||||
|
tmux
|
||||||
|
mc
|
||||||
|
ncdu
|
||||||
|
fastfetch
|
||||||
|
# Games. In the terminal: nethack, the BSD set, and two toys. On X: lbreakout2
|
||||||
|
# (SDL 1.2) and the classic X clients.
|
||||||
|
nethack
|
||||||
|
bsd-games
|
||||||
|
cmatrix
|
||||||
|
sl
|
||||||
|
lbreakout2
|
||||||
|
xeyes
|
||||||
|
xclock
|
||||||
|
xcalc
|
||||||
|
# A browser: NetSurf, on GTK 3 -- glib's threads, cairo, pango, TLS. The step
|
||||||
|
# before WebKit (epiphany), which is `apk add` away.
|
||||||
|
netsurf
|
||||||
|
|
|
||||||
|
|
@ -1,61 +1,122 @@
|
||||||
# Generated by tools/posix/alpine-lock.sh from world. Do not edit;
|
# Generated by tools/posix/alpine-lock.sh from world. Do not edit;
|
||||||
# change world and run `make alpine-lock`.
|
# change world and run `make alpine-lock`.
|
||||||
# file sha256 repository
|
# file sha256 repository
|
||||||
|
at-spi2-core-2.60.5-r0.apk c9989ba9b2ff44f87a2106ca9560c2e554a56811d0fc2863b22741d576f1d84f main
|
||||||
|
at-spi2-core-libs-2.60.5-r0.apk bcd4846311c8b19ad7e08ffc3cf726ddbe3d36bbc524386bda64758f5c5981a6 main
|
||||||
|
avahi-libs-0.9_rc4-r0.apk 380d59c58cfce9cb5cf342cf73947749ca77a880f729970e2f508aaa6fc2f713 main
|
||||||
brotli-libs-1.2.1-r0.apk 62a7e37e43bbfc1f8525a00ad2125fd5897135dc93b849733884cb78ffd8e26c main
|
brotli-libs-1.2.1-r0.apk 62a7e37e43bbfc1f8525a00ad2125fd5897135dc93b849733884cb78ffd8e26c main
|
||||||
|
bsd-games-3.3-r2.apk 0297419e0bb67817708c9150c74320a57c647020be9297237e6410df0de17a36 community
|
||||||
|
btop-1.4.6-r0.apk 82369611eaa6a1bd2e3738f2e5990429526060ad5319f729b4bc4acaddf296f3 community
|
||||||
|
bubblewrap-0.12.0-r0.apk 7755592c3f7c6de79a5c3c13554ada6302dbd98e520fe6dd8064f87ba77092e5 main
|
||||||
|
bubblewrap-zsh-completion-0.12.0-r0.apk 0a851aeb4cf8e9048d3863e3b43b3ace9295a57b583141646f475e04bf0ec3a9 main
|
||||||
busybox-1.37.0-r31.apk 0e626fa97cf937fda7bba4ed4dde8e2f2dbb5083ac34e02180cec0c4eba9351b main
|
busybox-1.37.0-r31.apk 0e626fa97cf937fda7bba4ed4dde8e2f2dbb5083ac34e02180cec0c4eba9351b main
|
||||||
busybox-binsh-1.37.0-r31.apk 93f803ae5db6b729c0f66f4afba6e907ccc17d7d5f8b8c3d25bdb4636de2c48a main
|
busybox-binsh-1.37.0-r31.apk 93f803ae5db6b729c0f66f4afba6e907ccc17d7d5f8b8c3d25bdb4636de2c48a main
|
||||||
|
ca-certificates-bundle-20261020-r0.apk bb008f6d214b6af7576324659856fb63dc8dcb4373f8345ff706388696f1254d main
|
||||||
|
cairo-1.18.4-r1.apk 23364d77506b5f60eae26daa0a5489cdee8bc100fcd010cd73774dfa5a7f9305 main
|
||||||
|
cairo-gobject-1.18.4-r1.apk ec302c75a24edaa4a2f867cfb5eec85cf30efe184044bab6e2b9678840e2bc2a main
|
||||||
|
c-ares-1.34.9-r0.apk 3539a3e11c1ef1516117d8179a546ac882086e94e0d78ad51d687b3fa2b3b5f2 main
|
||||||
|
cmatrix-2.0-r2.apk d9f6bea3851492a6db2a9cd3f7e2df0f0756c6651a702b36ed2b887328ad1f00 community
|
||||||
|
cups-libs-2.4.19-r1.apk 5739c568d90f23006d74ff82619e1a18447a6f698ae2af61970debe6658a7bea main
|
||||||
|
dbus-libs-1.16.2-r2.apk 105f6ba17454c587ca9345f9deb348012d1950588a22da42120c954d396c99b6 main
|
||||||
|
e2fsprogs-libs-1.47.4-r0.apk 2e067395a6981b06cfd2cd3abf0e6c6574f7ef64d02e01f75fc5323d4a337fb9 main
|
||||||
encodings-1.1.0-r0.apk f7c0eb762638d0249b4fbbe07a8e4ed6aab8e86b74c239c40e47a36219777d52 main
|
encodings-1.1.0-r0.apk f7c0eb762638d0249b4fbbe07a8e4ed6aab8e86b74c239c40e47a36219777d52 main
|
||||||
eudev-libs-3.2.14-r6.apk 45fdbcf37c5c0e82067e98a818d3c1fd16d0261c62128dcb6b935b86b4ec6c86 main
|
eudev-libs-3.2.14-r6.apk 45fdbcf37c5c0e82067e98a818d3c1fd16d0261c62128dcb6b935b86b4ec6c86 main
|
||||||
|
fastfetch-2.64.2-r0.apk 9c593d689f1c04625d3b433066ad92e430a384fa8e99585fee71310a20a965cb community
|
||||||
|
fastfetch-zsh-completion-2.64.2-r0.apk ded7a5c7911c4db60ab71a320104f9af3f629976429c897db67ea87c5ac53ec8 community
|
||||||
font-alias-1.0.6-r0.apk 754e5c1d19c2c69d5a2c3c02c7340cad7df3c26e63789516372ea164b564b927 main
|
font-alias-1.0.6-r0.apk 754e5c1d19c2c69d5a2c3c02c7340cad7df3c26e63789516372ea164b564b927 main
|
||||||
fontconfig-2.17.1-r1.apk 7634cf2fb1066ec97c8f41ec90ec9f036540e056276b7bf9a10dc4ec9e44d238 main
|
fontconfig-2.17.1-r1.apk 7634cf2fb1066ec97c8f41ec90ec9f036540e056276b7bf9a10dc4ec9e44d238 main
|
||||||
font-cursor-misc-1.0.4-r1.apk a1a0c0659a9a48e24b4cc4b35583f4e57da9d2a0ae2cc8adb9be1143b66911b4 main
|
font-cursor-misc-1.0.4-r1.apk a1a0c0659a9a48e24b4cc4b35583f4e57da9d2a0ae2cc8adb9be1143b66911b4 main
|
||||||
font-dejavu-2.37-r6.apk 6e7609e7a1aaed947afe4ddcc02bbdeec571dac5ff4ea1c04af9b8480b7b9dcf main
|
font-dejavu-2.37-r6.apk 6e7609e7a1aaed947afe4ddcc02bbdeec571dac5ff4ea1c04af9b8480b7b9dcf main
|
||||||
font-misc-misc-1.1.3-r1.apk 2c48f2a8529ac63243a0bdd5e7ab2329e0a8b8f996ffe95faed8b79fb05b4a52 main
|
font-misc-misc-1.1.3-r1.apk 2c48f2a8529ac63243a0bdd5e7ab2329e0a8b8f996ffe95faed8b79fb05b4a52 main
|
||||||
freetype-2.14.3-r0.apk 09d76fde675a9ca3cfeb83d72fb09cb2e9ccf79b2937daa05a1af9a37a6b7815 main
|
freetype-2.14.3-r0.apk 09d76fde675a9ca3cfeb83d72fb09cb2e9ccf79b2937daa05a1af9a37a6b7815 main
|
||||||
|
fribidi-1.0.16-r3.apk be0cba50bc7bfdaadfba020fe8239ba3f25f35d309806a9e89e9e09860f985c0 main
|
||||||
|
gdk-pixbuf-2.44.7-r1.apk 71c422b35822af144fbc14baf7308fa6bd59c490b19842de93aef4ed46c88422 community
|
||||||
|
glib-2.88.1-r1.apk 2f32203b97789bc9a92e57e2d9b89eb2e6951b69534ba082a683c6e49031cb5d main
|
||||||
|
glycin-image-rs-2.1.5-r0.apk 9c9d639e1484e244316acfd8b9815ec90773f695b90f99451e914c66ddaf4726 community
|
||||||
|
glycin-svg-2.1.5-r0.apk 59cf8cb8816249019f9498ef3acf20073f5443d2e89d4d3baf7878c0a454137a community
|
||||||
gmp-6.3.0-r4.apk 85323ef5bce8ff234736dc2d6592ea37aebdc6348a531e27430bdfe38364d256 main
|
gmp-6.3.0-r4.apk 85323ef5bce8ff234736dc2d6592ea37aebdc6348a531e27430bdfe38364d256 main
|
||||||
|
gnutls-3.8.13-r0.apk 8d3f3612145db8cd7feb83607187a60f809f06cfa3c2e74d310d58a6b053d54c main
|
||||||
|
gobject-introspection-1.86.0-r1.apk ad831499410b235a1072dde40705f4ea5303be9ad47ce1e97dcb1b9658c6eee9 main
|
||||||
|
gpm-libs-1.20.7-r6.apk 4893672b19ff5b14e7a40e945105bca1c2ff2bcedcafd1f9298d4bbb46dbf58f main
|
||||||
|
graphite2-1.3.14-r6.apk 2caa36d901f79215753ce3b614818ca71596baf2bbb2c21b6eadcf073d81b552 main
|
||||||
|
gtk+3.0-3.24.52-r0.apk c7c0c0faffa3061bc8fca9540b250d024bdba1aa6ae1fb06f5185adefceaa070 community
|
||||||
|
harfbuzz-13.2.1-r0.apk 01f7862265ef2256bbadd0fc998df3d1243dee606df3bd8284471cf390f86f29 main
|
||||||
|
htop-3.5.3-r0.apk 6f6d444ca9a4133c2c13323ded04128783d59f924bcff0276d150a982904a2f3 main
|
||||||
hwdata-pci-0.408-r0.apk fe61404e6d2bd1e04c021d53641c0ebce97911084d77994e43a288a1278686c1 main
|
hwdata-pci-0.408-r0.apk fe61404e6d2bd1e04c021d53641c0ebce97911084d77994e43a288a1278686c1 main
|
||||||
hwdata-usb-0.408-r0.apk 272d365cb05dd085dd8022154cf71e3f3b0aaddd41c1269eeaf939c9d38675c8 main
|
hwdata-usb-0.408-r0.apk 272d365cb05dd085dd8022154cf71e3f3b0aaddd41c1269eeaf939c9d38675c8 main
|
||||||
|
lbreakout2-2.6.5-r2.apk 2c0f448af62e8138d9e612a858198f4bb906af33db50a32bb4bd468239521449 community
|
||||||
|
lcms2-2.19-r0.apk b4cb038af3c2f02bedd8456ae876c97ac0a9f29789add77871bfd6ce084ff136 main
|
||||||
|
libatk-1.0-2.60.5-r0.apk ab5f603b17f0f0c5577d259b8263c5cbc6c7b3fd031f06ddc1e3e26417e1609a main
|
||||||
|
libatk-bridge-2.0-2.60.5-r0.apk f5e38bc54bae780680134f7b96ce8e01ef24e98ae0ac67ded7229af07aa0b0f1 main
|
||||||
|
libblkid-2.42.3-r1.apk 2512c2bff496ac2b8561a3c814d21d35ce10cda579b3d410f9f96da93a5aaac1 main
|
||||||
libbsd-0.12.2-r0.apk 407597c04b41dbfe8a05b35d45a3453e32fae7df84aed3a06ff48dd4df1338b6 main
|
libbsd-0.12.2-r0.apk 407597c04b41dbfe8a05b35d45a3453e32fae7df84aed3a06ff48dd4df1338b6 main
|
||||||
libbz2-1.0.8-r6.apk 2737e2b23ed08d4911ee62cd0163b5f4bf0379cacf4dc35b23a00209584c98fd main
|
libbz2-1.0.8-r6.apk 2737e2b23ed08d4911ee62cd0163b5f4bf0379cacf4dc35b23a00209584c98fd main
|
||||||
libcap2-2.78-r0.apk 42886a7b4bcde26347c712925c8dc7db202b6029ef9b8c7ee81577ab7bdd8fad main
|
libcap2-2.78-r0.apk 42886a7b4bcde26347c712925c8dc7db202b6029ef9b8c7ee81577ab7bdd8fad main
|
||||||
|
libcom_err-1.47.4-r0.apk 4723d0fb8e286dc6adb54dd3cdaedcd8db54260f75ee778a5acb4c16922c0f52 main
|
||||||
|
libcrypto3-3.5.9-r0.apk 6632d758d8f5e9ea3b650fe966f23bbf9a202f8b8dceecac93da135dec5e3689 main
|
||||||
|
libcurl-8.22.0-r0.apk 40b2a8e15a8d24bc95a5f42aaeb018313a1ff457cee71d8e15aaf1328da765e8 main
|
||||||
|
libdav1d-1.5.3-r0.apk 40a2b1b92cdcc6fd295934f799ee80ef67e2e384097624d565c6222d827f8480 main
|
||||||
libdrm-2.4.134-r0.apk 584fd84ad5030040e613fa1816097cd1706ddd542100070852b0eab6e09e12ca main
|
libdrm-2.4.134-r0.apk 584fd84ad5030040e613fa1816097cd1706ddd542100070852b0eab6e09e12ca main
|
||||||
|
libeconf-0.8.3-r0.apk 2a791c7957961f830cf31b04f35f814a63afcf633badc9052425ff1cf2f64380 main
|
||||||
libelf-0.195-r0.apk 78c5a1993342ab1bdcdce30639fd0937e8000f6d0d85cea171f573d31decd036 main
|
libelf-0.195-r0.apk 78c5a1993342ab1bdcdce30639fd0937e8000f6d0d85cea171f573d31decd036 main
|
||||||
libepoxy-1.5.10-r1.apk 0be13b4b524fe64315e0663ab615a244d75d7da73130ac1c630cd6096841444e main
|
libepoxy-1.5.10-r1.apk 0be13b4b524fe64315e0663ab615a244d75d7da73130ac1c630cd6096841444e main
|
||||||
libevdev-1.13.6-r0.apk b38a27a4ac55701fd2eb0032977af2664b6fc23b2f9c2cc6e893ad4452fd82d5 community
|
libevdev-1.13.6-r0.apk b38a27a4ac55701fd2eb0032977af2664b6fc23b2f9c2cc6e893ad4452fd82d5 community
|
||||||
|
libevent-2.1.13-r0.apk 870d4b22a2e4068205b67bb8e4631342c9e84060d052fb36cf4721e18d4043a2 main
|
||||||
libexpat-2.9.0-r0.apk 32b127f31e0a919110a18a7f0bb3780d8fb66ba4d8d61b2d301d633f0e319d0e main
|
libexpat-2.9.0-r0.apk 32b127f31e0a919110a18a7f0bb3780d8fb66ba4d8d61b2d301d633f0e319d0e main
|
||||||
libffi-3.5.2-r1.apk 6da1a19313a40c56cac756fb11e2c4d5267831e1c22fc8323f24c943c94da6e0 main
|
libffi-3.5.2-r1.apk 6da1a19313a40c56cac756fb11e2c4d5267831e1c22fc8323f24c943c94da6e0 main
|
||||||
libfontenc-1.1.9-r0.apk ea7f6d097653572991da749ac0764c2568d8902a01eea4236e56b10b90a7b0e3 main
|
libfontenc-1.1.9-r0.apk ea7f6d097653572991da749ac0764c2568d8902a01eea4236e56b10b90a7b0e3 main
|
||||||
libgcc-15.2.0-r5.apk 393dcd32629f06d7d85409c272d142d0c082772d10b87ef55ee82f47de3be637 main
|
libgcc-15.2.0-r5.apk 393dcd32629f06d7d85409c272d142d0c082772d10b87ef55ee82f47de3be637 main
|
||||||
|
libglycin-2.1.5-r0.apk 3541d1a5d4d2599d145904f4febf26e56942403b6d80834461e196636b35b280 community
|
||||||
libice-1.1.2-r0.apk 08daa589d57149b52b00c666b4009212aa3af95021ebdb2a359555c9bcaa76e1 main
|
libice-1.1.2-r0.apk 08daa589d57149b52b00c666b4009212aa3af95021ebdb2a359555c9bcaa76e1 main
|
||||||
|
libidn2-2.3.8-r0.apk 7d038ac558dde464964b033ce5126dd2eaf84cc414dc15e77c5424a321d0ebee main
|
||||||
|
libintl-1.0-r0.apk cb03919cba5624b5e8759d04f3b31913b02585355988b914bc31de4746ff5ef6 main
|
||||||
libjpeg-turbo-3.1.3-r0.apk 6299627dd0a9156a7463511e4915a6294dfb592d8b4bdf85e55265adf1b8ea04 main
|
libjpeg-turbo-3.1.3-r0.apk 6299627dd0a9156a7463511e4915a6294dfb592d8b4bdf85e55265adf1b8ea04 main
|
||||||
libmd-1.2.0-r0.apk f6d68d072fcefc8edd4ffe3afc341ca391f8a9a209130628fb065d98b18a2792 main
|
libmd-1.2.0-r0.apk f6d68d072fcefc8edd4ffe3afc341ca391f8a9a209130628fb065d98b18a2792 main
|
||||||
|
libmount-2.42.3-r1.apk e2ae003260f2c1ec13666c6e108d5e8e3d64974b0667166a02432bd3be263a7a main
|
||||||
libncursesw-6.6_p20260516-r0.apk cf8caa8a88bc4ce9d9e395567fefaf9ef7fbd55c50ae6155b35c1b58f3755023 main
|
libncursesw-6.6_p20260516-r0.apk cf8caa8a88bc4ce9d9e395567fefaf9ef7fbd55c50ae6155b35c1b58f3755023 main
|
||||||
libpciaccess-0.19-r0.apk 03fdccd1e2a0825e7f7fabbac67212e05d271d31d2eff44cc92f707e69908ad4 main
|
libpciaccess-0.19-r0.apk 03fdccd1e2a0825e7f7fabbac67212e05d271d31d2eff44cc92f707e69908ad4 main
|
||||||
libpng-1.6.59-r0.apk d16aeb0597f2bc43e0583ac9ff779388ec160c3e22568b640122e94ba55be6d7 main
|
libpng-1.6.59-r0.apk d16aeb0597f2bc43e0583ac9ff779388ec160c3e22568b640122e94ba55be6d7 main
|
||||||
|
libpsl-0.21.5-r3.apk ec665a344b6e887f00f0a47015cc732c3a281b69e84641dc8662ea234fde6cdb main
|
||||||
|
librsvg-2.62.3-r0.apk e2aeb4a2b6b1ab20627dbb0e9136ea90f7120ec61412ed52f4335ee231361fde community
|
||||||
|
libseccomp-2.6.1-r0.apk b22ccfe8cb072e4c8d3bc17222eeda132e270ce6258c27622acb193bf27b48e1 main
|
||||||
libsharpyuv-1.6.0-r0.apk 34621ff53f43030d24d1224fe9cb53b1590ab614cbce6080005cde703e6c874a main
|
libsharpyuv-1.6.0-r0.apk 34621ff53f43030d24d1224fe9cb53b1590ab614cbce6080005cde703e6c874a main
|
||||||
libsm-1.2.6-r0.apk dd7eff58a46fd60e4afee9c7c1c3508920b0e1d6560129e59aee82329f68afc8 main
|
libsm-1.2.6-r0.apk dd7eff58a46fd60e4afee9c7c1c3508920b0e1d6560129e59aee82329f68afc8 main
|
||||||
|
libssh2-1.11.1-r4.apk 13f707f411380e1e195e590ba80bce4c2406ffed6b28ef03f75043b45db156d5 main
|
||||||
|
libssl3-3.5.9-r0.apk 05e3393fb95aa5751ca2f9d242f659f6cff82c1cc7767cc2df4a086f7ad01877 main
|
||||||
libstdc++-15.2.0-r5.apk 14c987b556f5385a5db18376e788c75f37d85321b8dc1920d926ea7daac1d6f6 main
|
libstdc++-15.2.0-r5.apk 14c987b556f5385a5db18376e788c75f37d85321b8dc1920d926ea7daac1d6f6 main
|
||||||
|
libtasn1-4.21.0-r0.apk 407f255c5af989f4e22a5e9404c4558f6fda04f14a4209a2ca1e06c3f2ba476e main
|
||||||
|
libunistring-1.4.2-r0.apk 3aa6030e603e34d96192742f327c0509f409c6769ce2e7b1463685dede38d714 main
|
||||||
libuuid-2.42.3-r1.apk 8306e5bb577696c9069fe1dfd9e1dcc39d2d481c6a1b0e707fd03c3e21aa6aa2 main
|
libuuid-2.42.3-r1.apk 8306e5bb577696c9069fe1dfd9e1dcc39d2d481c6a1b0e707fd03c3e21aa6aa2 main
|
||||||
libwebp-1.6.0-r0.apk dc50c0280348330bb113079331c4a35d85e28351c7baa9268ee346aad8398288 main
|
libwebp-1.6.0-r0.apk dc50c0280348330bb113079331c4a35d85e28351c7baa9268ee346aad8398288 main
|
||||||
libx11-1.8.14-r0.apk 016efc189c6d566061ffe206ed0780e32686c1a8c832a3917d35d2c66d61a40f main
|
libx11-1.8.14-r0.apk 016efc189c6d566061ffe206ed0780e32686c1a8c832a3917d35d2c66d61a40f main
|
||||||
libxau-1.0.12-r0.apk 3506cdba7ed916a6f814a010fbb127dbfdae4147107f64a15f5fa2651c5d8510 main
|
libxau-1.0.12-r0.apk 3506cdba7ed916a6f814a010fbb127dbfdae4147107f64a15f5fa2651c5d8510 main
|
||||||
libxaw-1.0.16-r1.apk 8ac469cddeac40ff2e91c84cb02ba526d7c62e30a79a25c2e3b6e5269236801d main
|
libxaw-1.0.16-r1.apk 8ac469cddeac40ff2e91c84cb02ba526d7c62e30a79a25c2e3b6e5269236801d main
|
||||||
libxcb-1.17.0-r2.apk 7935d5ad9c7137b7b3f1292d62f55f7e0cc5c919155218c3060ea46503b3f211 main
|
libxcb-1.17.0-r2.apk 7935d5ad9c7137b7b3f1292d62f55f7e0cc5c919155218c3060ea46503b3f211 main
|
||||||
|
libxcomposite-0.4.7-r0.apk 90d6c22a5a730c7f884c992a8ca4c41102a71918aff9d804313356c721f2b50e main
|
||||||
|
libxcursor-1.2.3-r0.apk 5be10886e7361c8a9a6ae68b83ea664a6bd6a021043a9203fe6c8b878e99c0cd main
|
||||||
libxcvt-0.1.3-r0.apk 809b454981887c1d1d835c909c46a80650ca72a73a92268a2cf8a666ad82b6b7 community
|
libxcvt-0.1.3-r0.apk 809b454981887c1d1d835c909c46a80650ca72a73a92268a2cf8a666ad82b6b7 community
|
||||||
|
libxdamage-1.1.7-r0.apk d3fbef1ed735be3385531682c40a593d78467ff1713ad2c4a57daecb755b1bf7 main
|
||||||
libxdmcp-1.1.5-r1.apk 2a93f44f3f85c1eda9ed53691c35d0742b5fcd8e0057cd7380934025936cbf6f main
|
libxdmcp-1.1.5-r1.apk 2a93f44f3f85c1eda9ed53691c35d0742b5fcd8e0057cd7380934025936cbf6f main
|
||||||
libxext-1.3.7-r0.apk 8afe1ee6ab8cb656e12f20dc5b7fb2d4103faee4a38298b94f26ab52e597cbc8 main
|
libxext-1.3.7-r0.apk 8afe1ee6ab8cb656e12f20dc5b7fb2d4103faee4a38298b94f26ab52e597cbc8 main
|
||||||
|
libxfixes-6.0.2-r0.apk cb00578d1b83abe8baf291f311f194d4deca0f9cf525f47ab10e3bd7c0e54e24 main
|
||||||
libxfont2-2.0.8-r0.apk f8bf87b9ce835cb307eb8a3d270e226c4437757478c52ba3ad384cecc2ac6fef community
|
libxfont2-2.0.8-r0.apk f8bf87b9ce835cb307eb8a3d270e226c4437757478c52ba3ad384cecc2ac6fef community
|
||||||
libxft-2.3.9-r0.apk eba6fd85599b4fedf3acdd3ad3e2eec84ea97a5ba8f1ac570fa1ee37a7d81420 main
|
libxft-2.3.9-r0.apk eba6fd85599b4fedf3acdd3ad3e2eec84ea97a5ba8f1ac570fa1ee37a7d81420 main
|
||||||
|
libxi-1.8.4-r0.apk e41ff864833603ab1065ac37680b47838436f78d4d9510604723341fcb933723 main
|
||||||
libxinerama-1.1.6-r0.apk 9f780efb3cf675b5253b73b84f278b40757fa7869c6fe70a9790a73f4f7e810f main
|
libxinerama-1.1.6-r0.apk 9f780efb3cf675b5253b73b84f278b40757fa7869c6fe70a9790a73f4f7e810f main
|
||||||
|
libxkbcommon-1.13.1-r0.apk f52efd26a9069b1c3edc89cab399f7f81bdc47c9e88a54e017c470bd9734b003 main
|
||||||
libxkbfile-1.2.0-r0.apk e575c6c314ad87e74887526aca3b1429f5c1d6290e66f2945d4053f387beea57 main
|
libxkbfile-1.2.0-r0.apk e575c6c314ad87e74887526aca3b1429f5c1d6290e66f2945d4053f387beea57 main
|
||||||
libxml2-2.13.9-r2.apk 4b2f986159c659f014b942fe8a5d70c67af475153ca826c3dd53389eea46a300 main
|
libxml2-2.13.9-r2.apk 4b2f986159c659f014b942fe8a5d70c67af475153ca826c3dd53389eea46a300 main
|
||||||
libxmu-1.3.1-r0.apk 5d0b06d1a4b38ac0e070e0dad5e61583e5e147e378ba50418c83e1be4b19986c main
|
libxmu-1.3.1-r0.apk 5d0b06d1a4b38ac0e070e0dad5e61583e5e147e378ba50418c83e1be4b19986c main
|
||||||
libxpm-3.5.20-r0.apk 1ed312a00abac5a5840c1f3721921ea8183e664566c19763ed00aa9f35bb2178 main
|
libxpm-3.5.20-r0.apk 1ed312a00abac5a5840c1f3721921ea8183e664566c19763ed00aa9f35bb2178 main
|
||||||
|
libxrandr-1.5.5-r0.apk c64856b77001cdf31a32841729b1950ad45e6981c16d09f9fdf08824ab1c0271 main
|
||||||
libxrender-0.9.13-r0.apk 724aa6c6353e70869e0eb5aa5461e8fc0753b99db765de47a3ae238573dbdcf8 main
|
libxrender-0.9.13-r0.apk 724aa6c6353e70869e0eb5aa5461e8fc0753b99db765de47a3ae238573dbdcf8 main
|
||||||
libxshmfence-1.3.3-r0.apk 53790552de7eea17a47aa4c082b06b735c545a0947c41128f97c7be95a7d3d92 main
|
libxshmfence-1.3.3-r0.apk 53790552de7eea17a47aa4c082b06b735c545a0947c41128f97c7be95a7d3d92 main
|
||||||
libxt-1.3.1-r0.apk 08b167b05ac7dfb892421876cc2467214208b83b2960a9f49e84f9dbcf36e812 main
|
libxt-1.3.1-r0.apk 08b167b05ac7dfb892421876cc2467214208b83b2960a9f49e84f9dbcf36e812 main
|
||||||
|
libxtst-1.2.6-r0.apk e6426212f638e23091a22e1a792684d0e105c5d7a020f3ba30c43b7d2f4e955b main
|
||||||
libxxf86vm-1.1.7-r0.apk 887a3c5438e1db624d07ac23d16ec08d21fb16de438d75ad2dd5637c958b4108 main
|
libxxf86vm-1.1.7-r0.apk 887a3c5438e1db624d07ac23d16ec08d21fb16de438d75ad2dd5637c958b4108 main
|
||||||
llvm22-libs-22.1.3-r1.apk dec15bdd29b6ec7fc22aee43bac153a22e6b132c599e5e5d9ccd0d159eae764f main
|
llvm22-libs-22.1.3-r1.apk dec15bdd29b6ec7fc22aee43bac153a22e6b132c599e5e5d9ccd0d159eae764f main
|
||||||
|
lsof-4.99.6-r0.apk 14e8ea4fd6346de5d2fb899d5287ccc839d8e6f4e6f47d1677c533a1eeef69e8 main
|
||||||
|
mc-4.8.33-r3.apk ccb74b5f22d00589c369df30f7bf786d5dee69c040ac2411a57eb3c1af4a57fb main
|
||||||
mcookie-2.42.3-r1.apk 64f86340991e2f283ce95b1bae21157da6849118cd9f1a2931de5c73d0f9fc3d main
|
mcookie-2.42.3-r1.apk 64f86340991e2f283ce95b1bae21157da6849118cd9f1a2931de5c73d0f9fc3d main
|
||||||
mesa-26.1.6-r0.apk 8fb0e3d386f09df4f3e3643108b38197558465b2558e6d66d6a9c055adc8d1a9 main
|
mesa-26.1.6-r0.apk 8fb0e3d386f09df4f3e3643108b38197558465b2558e6d66d6a9c055adc8d1a9 main
|
||||||
mesa-egl-26.1.6-r0.apk ccdd25c8f3a069b018a39a5641e4f13ab7ceb511d5991e9f8fb18bd551172ffa main
|
mesa-egl-26.1.6-r0.apk ccdd25c8f3a069b018a39a5641e4f13ab7ceb511d5991e9f8fb18bd551172ffa main
|
||||||
|
|
@ -65,16 +126,39 @@ mesa-gles-26.1.6-r0.apk 2544a75e74778ab9ddcecfc8d9dc35375cb3a4ac3661e94a1156e2c5
|
||||||
mkfontscale-1.2.4-r0.apk 2385a7aad3971fcad9d862cb90937e643e633b14bf4b91f8b8ce3040a1468d20 main
|
mkfontscale-1.2.4-r0.apk 2385a7aad3971fcad9d862cb90937e643e633b14bf4b91f8b8ce3040a1468d20 main
|
||||||
mtdev-1.1.7-r0.apk 004f42bc67bc0eb573d64d802c5f0325f49013cf4aa7576d4b571399df46f3f7 community
|
mtdev-1.1.7-r0.apk 004f42bc67bc0eb573d64d802c5f0325f49013cf4aa7576d4b571399df46f3f7 community
|
||||||
musl-1.2.6-r3.apk e142255d0fa397f14318996bc10378093dcc069dc3dd1b6818c8f28a789c21da main
|
musl-1.2.6-r3.apk e142255d0fa397f14318996bc10378093dcc069dc3dd1b6818c8f28a789c21da main
|
||||||
|
ncdu-1.22-r2.apk 433ef46212f18b1d009080ace0a64a75d04610f60f2c38922f2a03fafffc75a0 main
|
||||||
ncurses-terminfo-base-6.6_p20260516-r0.apk ce83be6d0bd10584a53c3a5868cc1595ce5f0f8e0b621f8f7f70b6144e521fc9 main
|
ncurses-terminfo-base-6.6_p20260516-r0.apk ce83be6d0bd10584a53c3a5868cc1595ce5f0f8e0b621f8f7f70b6144e521fc9 main
|
||||||
|
nethack-3.6.7-r2.apk 6ebc4642fe66037d3f0d34c07ae5bbfd5c138644e682cf3db8877f965980a3b2 community
|
||||||
|
netsurf-3.11-r1.apk 4ea4200bb12faec841295d9e432be27c90a2c2b454ca8aa85e58507751d4c767 community
|
||||||
nettle-3.10.2-r0.apk 693c1a99b781cd17149916df30113ccd6bdddac130298c17b1286fd2ae438386 main
|
nettle-3.10.2-r0.apk 693c1a99b781cd17149916df30113ccd6bdddac130298c17b1286fd2ae438386 main
|
||||||
|
nghttp2-libs-1.70.0-r0.apk f0c4014eb2f10308dcdfd84cd49c121673c7b78cb8182b6e7a1388d8ae112a3b main
|
||||||
|
p11-kit-0.26.2-r0.apk 134d653aeeb4ada32746fbc3e2a7ae4201f584725de9263e4f32503658f3ddd8 main
|
||||||
|
p11-kit-zsh-completion-0.26.2-r0.apk e7393c59f19ebeda5080712b203d3e3049cdd15a8c35be2bf9d5cf7dad93082d main
|
||||||
|
pango-1.57.1-r0.apk c616b62df908026ef413b338b9043e36706b05b6d035049394e5d14022e64adc main
|
||||||
|
pcre2-10.49-r0.apk 2919897914820f42a17a8d7d37df310d2dfad1c82ebe5f539cc5c765f916752a main
|
||||||
pixman-0.46.4-r0.apk 378550cbe348dda9b0733014375dba87a89f0077c5f3dbeba7dce8dbcdda707e main
|
pixman-0.46.4-r0.apk 378550cbe348dda9b0733014375dba87a89f0077c5f3dbeba7dce8dbcdda707e main
|
||||||
pkgconf-2.5.1-r0.apk 20cc47b76b82e9424a35ecc4fdd7bd904ef49ada1684214910fae31bc0cab737 main
|
pkgconf-2.5.1-r0.apk 20cc47b76b82e9424a35ecc4fdd7bd904ef49ada1684214910fae31bc0cab737 main
|
||||||
|
sdl12-compat-1.2.76-r1.apk 124791270d613d1e33115625b95725487fe76ccfbc08638b8baa1b180287deb9 community
|
||||||
|
sdl2-compat-2.32.68-r0.apk 0c4c1279446cba3c2065a3d3c2cacfa9eb4aeff507757c5a37193aaf028c5cf7 community
|
||||||
|
sdl3-3.4.10-r0.apk e9e4537d455f8b710a1f6fed7607c3cdb492fdc155684cf38e1b054bba809427 community
|
||||||
|
sdl_mixer-1.2.12-r3.apk 9733a4e906b76f8bb5f246578ffc123a39a489fbfe60212abbaaaa0782e62e1c community
|
||||||
|
shared-mime-info-2.4-r7.apk b0ed78b502cacfa2dfb6ff87aaef36a034258026873ab5b73f5e603d6623a147 main
|
||||||
|
sl-5.02-r1.apk aa3ceb845953904ab61459f0f97416903c56a80b428f8bc320d879b9e58c1a57 community
|
||||||
|
slang-2.3.3-r3.apk 96daf8e02cbcc0d4fd7163d087ae2a71a1aea279a0ac8d2a00a1612ccdccf644 main
|
||||||
spirv-tools-1.4.341.0-r0.apk 6034c22c4f61fb76e517c1d5b11ef5c26c7154d2baeacec8b0a9dcca94ff9a93 main
|
spirv-tools-1.4.341.0-r0.apk 6034c22c4f61fb76e517c1d5b11ef5c26c7154d2baeacec8b0a9dcca94ff9a93 main
|
||||||
|
ssl_client-1.37.0-r31.apk e578a140670e4cf9816117b0c2063632bf3754ecbe8fa593a7fd2b4039372bf0 main
|
||||||
tiff-4.7.2-r0.apk 757ce87ebe4923a6be9869d026e250958ca09696529c464781e8a91d2b81670b main
|
tiff-4.7.2-r0.apk 757ce87ebe4923a6be9869d026e250958ca09696529c464781e8a91d2b81670b main
|
||||||
|
tmux-3.7c-r0.apk 16f3438f5a24124dd500f70bf6f2a232c9b3dfe5e7f7e615969ac5c00c2c8d0b main
|
||||||
|
tmux-zsh-completion-5.9-r7.apk e505b2a8c0d56beb65b6f47fd732f6ee6d8d8a72a1a1ea27a6922dad48d9ce89 main
|
||||||
util-macros-1.20.2-r0.apk 7c129d085b646b13ccef3738e93038221aec840c1a6764279c5adc9950ba1bdc main
|
util-macros-1.20.2-r0.apk 7c129d085b646b13ccef3738e93038221aec840c1a6764279c5adc9950ba1bdc main
|
||||||
wayland-libs-client-1.25.0-r0.apk a728f7103217cafdc83ff6ad0d679c197dbbe280bd5788c8336259f7a39cb303 main
|
wayland-libs-client-1.25.0-r0.apk a728f7103217cafdc83ff6ad0d679c197dbbe280bd5788c8336259f7a39cb303 main
|
||||||
|
wayland-libs-cursor-1.25.0-r0.apk 16ec369226507abaeb84b496ce2bb1a4ef29987ea91ba6bedc40bb0e0af274e7 main
|
||||||
|
wayland-libs-egl-1.25.0-r0.apk c944a59c91ce89972b4cf0ecbb46dfcd43f95c4b86ee63b5522b9a843a3d6af9 main
|
||||||
windowmaker-0.96.0-r0.apk 15b500d9afccf94a385e042b01ed9b1d709b1627d97597cea59bf63d0bab4f6b community
|
windowmaker-0.96.0-r0.apk 15b500d9afccf94a385e042b01ed9b1d709b1627d97597cea59bf63d0bab4f6b community
|
||||||
xauth-1.1.5-r0.apk 94e80e9687a1829a6fe95fb28d4b08a5531dab182cde8a58ff1b370b2cd293fb community
|
xauth-1.1.5-r0.apk 94e80e9687a1829a6fe95fb28d4b08a5531dab182cde8a58ff1b370b2cd293fb community
|
||||||
|
xcalc-1.1.3-r0.apk 2f0d9ab5674a5f82f4adfb6dd5f0a9c944421feea2d6aaf18998b0326fe4c236 community
|
||||||
|
xclock-1.1.1-r0.apk ae2a3cb515664cf34dd9cd0f81945cad75d0b6bbf44696830f29f1ab4713b475 community
|
||||||
|
xeyes-1.3.1-r0.apk 4b31dabe8d7f5000d82a24445972859bf960c1b07bca3d4f87e80b3c710902c6 community
|
||||||
xf86-input-evdev-2.11.0-r0.apk 1f3fcd3e837b43ad5e32005e03095b547ddbfc25973f0511e9ff74bf342d2fe0 community
|
xf86-input-evdev-2.11.0-r0.apk 1f3fcd3e837b43ad5e32005e03095b547ddbfc25973f0511e9ff74bf342d2fe0 community
|
||||||
xf86-video-fbdev-0.5.0-r6.apk 6a8a1b16f82d8810896f00c00b356c8b8d923e9aa0ce64737ae8274ca82f507a community
|
xf86-video-fbdev-0.5.0-r6.apk 6a8a1b16f82d8810896f00c00b356c8b8d923e9aa0ce64737ae8274ca82f507a community
|
||||||
xinit-1.4.4-r0.apk efb5dc1e7929824f5add7d5d51d0ecb34ecfd0a773dfca4f061adc8a353b234f community
|
xinit-1.4.4-r0.apk efb5dc1e7929824f5add7d5d51d0ecb34ecfd0a773dfca4f061adc8a353b234f community
|
||||||
|
|
|
||||||
|
|
@ -234,6 +234,12 @@ ROOT_PI=$ROOT sh -c '
|
||||||
done
|
done
|
||||||
grep -qw schos "$ROOT_PI/etc/hosts" || printf "127.0.1.1\tschos\n" >> "$ROOT_PI/etc/hosts"'
|
grep -qw schos "$ROOT_PI/etc/hosts" || printf "127.0.1.1\tschos\n" >> "$ROOT_PI/etc/hosts"'
|
||||||
|
|
||||||
|
# The shared scratch directories, world-writable and sticky as baselayout
|
||||||
|
# means them. apk running as an ordinary user on the build host lands them as
|
||||||
|
# 0755, and with the kernel reporting real modes that is what tmux and every
|
||||||
|
# other program checking /tmp would see.
|
||||||
|
chmod 1777 "$ROOT/tmp" "$ROOT/var/tmp"
|
||||||
|
|
||||||
# The repository goes in too, so the installed system can reinstall and
|
# The repository goes in too, so the installed system can reinstall and
|
||||||
# upgrade schos-base, and Alpine's are listed for everything else.
|
# upgrade schos-base, and Alpine's are listed for everything else.
|
||||||
mkdir -p "$ROOT/var/lib/schos"
|
mkdir -p "$ROOT/var/lib/schos"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue