root: find the filesystem instead of being told at build time, and fix dmesg
`#define ROOTDEV 1` -- IDE disk 1 -- so a kernel could only ever root on the
disk it was *built* expecting. Booting the same image from a USB stick was
impossible by construction, and that was the one thing standing between this
kernel and its own acceptance test, which begins "boot on real hardware from a
USB stick".
rootdev_mount() probes. schfs_kmount() was already written for exactly this --
its own comment says "the uberblock scan doubles as the probe: no valid
uberblock means this is simply not a SchFS volume, which is not an error worth
reporting loudly" -- and a failed probe leaves the slot unmounted and reusable,
which is what makes a loop over candidates legal at all. So the change is one
function and one call site; the only real use of ROOTDEV was `schfs_kmount()`.
Order is preference, not confidence: a RAMDISK_ROOT build wins outright, then
USB, then IDE 1, then IDE 0. USB before the fixed disks because a stick that is
plugged in was plugged in on purpose. iderw() panics on an absent drive rather
than failing, so each candidate is checked with ide_disk_present() /
usb_storage_present() first -- which is what those predicates are for.
Verified with **no IDE disk attached at all**: boots, mounts the stick, writes
a file, and the file is still there on the next boot.
[ 0.000021] root: USB disk (device 3)
And when nothing matches, it names what it looked at, because "no root
filesystem" with no list is the least useful thing a kernel can say at the one
moment somebody needs to know which disk it expected:
root: no SchFS volume on any candidate device. Looked at:
device 3, USB disk: not present
device 1, IDE disk 1: not present
device 0, IDE disk 0: present, but no SchFS on it
**That message is also how dmesg turned out to be broken**, because it did not
appear in `dmesg` and should have. `dmesg` printed *nothing at all*, on a
system whose boot banner says "or dmesg once you are in" and whose entire
quiet-boot design is "route the detail into the kernel log and say how to read
it". The log was there the whole time -- `cat /proc/kmsg` has always worked --
but the one command anybody would reach for could not see it.
Two separate faults, each alone enough:
- **sys_syslog() was `return 0`.** dmesg does not read /proc/kmsg; it calls
klogctl(10) for the buffer size and klogctl(3) to read. 0 to the first one
means "the log is zero bytes", and a stub that answers 0 is
indistinguishable from a machine that logged nothing. Implemented for the
actions that mean something here; the rest return -EINVAL rather than 0,
because a console log level this kernel does not have is not a request that
succeeded.
- **With that fixed, dmesg was still empty.** It parses every line with
`sscanf(msg, "<%u>[%llu.%llu] %n", ...)` and silently drops anything that
does not match. The ring's `[ 0.00] text` already matches everything
after the priority -- scanf skips the leading spaces -- so the only missing
thing was the `<6>`. klog_render_syslog() adds it on the way out rather
than storing it: the ring is also what a person reads with `cat
/proc/kmsg` and what PSOGM points at after a crash, and an ABI detail
belongs at the boundary that has the ABI.
Also: the first version of the no-root diagnostic printed
%-11s device 1596400
because cprintf() has no field-width specifier -- it prints "%-11s" literally
and then reads the next argument for the %d. Plain %s now, with the note.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
fdbd9bcf95
commit
7e88cea151
6 changed files with 307 additions and 21 deletions
|
|
@ -168,6 +168,44 @@ It does **not** mount the root filesystem, and cannot: mounting reads blocks,
|
||||||
`forkret()`, the first time any process is scheduled — guarded by a `static int
|
`forkret()`, the first time any process is scheduled — guarded by a `static int
|
||||||
first`.
|
first`.
|
||||||
|
|
||||||
|
### Which device the root is on is decided there, not at build time
|
||||||
|
|
||||||
|
`ROOTDEV` used to be `#define ROOTDEV 1` — IDE disk 1 — so a kernel could only
|
||||||
|
ever root on the disk it was *built* expecting. Booting the same image from a
|
||||||
|
USB stick was impossible by construction, and that was the one thing standing
|
||||||
|
between this kernel and its own acceptance test, which begins "boot on real
|
||||||
|
hardware from a USB stick".
|
||||||
|
|
||||||
|
`rootdev_mount()` probes instead. `schfs_kmount()` was already written to be
|
||||||
|
used this way — *"the uberblock scan doubles as the probe: no valid uberblock
|
||||||
|
means this is simply not a SchFS volume, which is not an error worth reporting
|
||||||
|
loudly"* — and a failed probe leaves the slot unmounted and reusable, which is
|
||||||
|
what makes a loop over candidates legal at all.
|
||||||
|
|
||||||
|
| order | device | |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 | `RAMDISK_DEV` | only in a `RAMDISK_ROOT` build, where it is an explicit choice |
|
||||||
|
| 2 | `USBDISK_DEV` | a stick that is plugged in was plugged in on purpose |
|
||||||
|
| 3 | IDE 1 | what the old constant named |
|
||||||
|
| 4 | IDE 0 | usually the ESP, so usually not SchFS |
|
||||||
|
|
||||||
|
`iderw()` *panics* on an absent drive rather than failing, so each candidate is
|
||||||
|
checked with `ide_disk_present()` / `usb_storage_present()` first. That is the
|
||||||
|
whole reason those predicates exist.
|
||||||
|
|
||||||
|
When nothing matches, the panic names what it looked at, because "no root
|
||||||
|
filesystem" with no list is the least useful thing a kernel can say at the one
|
||||||
|
moment somebody needs to know which disk it expected:
|
||||||
|
|
||||||
|
```
|
||||||
|
root: no SchFS volume on any candidate device. Looked at:
|
||||||
|
device 3, USB disk: not present
|
||||||
|
device 1, IDE disk 1: not present
|
||||||
|
device 0, IDE disk 0: present, but no SchFS on it
|
||||||
|
```
|
||||||
|
|
||||||
|
The chosen device is reported in `dmesg` as `root: USB disk (device 3)`.
|
||||||
|
|
||||||
That is also where PID 1's working directory is re-pointed at the real root,
|
That is also where PID 1's working directory is re-pointed at the real root,
|
||||||
and the reason is worth keeping:
|
and the reason is worth keeping:
|
||||||
|
|
||||||
|
|
@ -233,12 +271,31 @@ that routing, and the whole boot is still there afterwards:
|
||||||
|
|
||||||
```
|
```
|
||||||
# dmesg | head -4
|
# dmesg | head -4
|
||||||
--- boot 1 ---
|
[ 0.000000] e820: 1017 MB usable in 8 regions, top 1002 MB (21 entries)
|
||||||
[ 0.00] e820: 1017 MB usable in 8 regions, top 1006 MB (22 entries)
|
[ 0.000000] klog: 128 KB at ffff800000737000, nothing recoverable was there
|
||||||
[ 0.00] acpi: 2 cpus, lapic at ffff8000fee00000, ioapic id 0
|
[ 0.000000] acpi: 4 cpus, lapic at ffff8000fee00000, ioapic id 0
|
||||||
[ 0.00] pci: 7 devices
|
[ 0.000000] console: JetBrains Mono, 10x21 cells, 128 by 38
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**`dmesg` printed nothing at all until 2026-09-22**, which made that promise in
|
||||||
|
the banner a lie for as long as it had been there. Two separate things were
|
||||||
|
wrong, and each alone was enough:
|
||||||
|
|
||||||
|
- `sys_syslog()` was `return 0`. `dmesg` does not read `/proc/kmsg`; it calls
|
||||||
|
`klogctl(10)` for the buffer size and `klogctl(3)` to read, and 0 to the
|
||||||
|
first one means "the log is zero bytes". A stub that answers 0 is
|
||||||
|
indistinguishable from a machine that logged nothing.
|
||||||
|
- With that implemented, `dmesg` was *still* empty. It parses each line with
|
||||||
|
`sscanf(msg, "<%u>[%llu.%llu] %n", ...)` and **silently drops** anything that
|
||||||
|
does not match. The ring's `[ 0.00] text` already matches everything
|
||||||
|
after the priority — scanf skips the leading spaces — so the only missing
|
||||||
|
thing was the `<6>`.
|
||||||
|
|
||||||
|
`klog_render_syslog()` adds it on the way out rather than storing it, because
|
||||||
|
the ring is also what a person reads with `cat /proc/kmsg` and what PSOGM
|
||||||
|
points at after a crash. An ABI detail belongs at the boundary that has the
|
||||||
|
ABI.
|
||||||
|
|
||||||
The rule for `kinfo` against `cprintf` is not how interesting a line is —
|
The rule for `kinfo` against `cprintf` is not how interesting a line is —
|
||||||
everything was interesting to somebody once. It is: **would you want to see
|
everything was interesting to somebody once. It is: **would you want to see
|
||||||
this on a machine that is working?** `pci: 7 devices` is yes the first time and
|
this on a machine that is working?** `pci: 7 devices` is yes the first time and
|
||||||
|
|
|
||||||
|
|
@ -332,10 +332,14 @@ known reasons stops being read, and then it is red for unknown reasons too.
|
||||||
|
|
||||||
**The identity item:**
|
**The identity item:**
|
||||||
|
|
||||||
- [ ] **Boot from a USB disk.** The xHCI stack, bulk-only transport, SCSI and
|
- [x] ~~**Boot from a USB disk.**~~ The root device is probed at boot instead of
|
||||||
the block device all work; a superblock reads back through `bread()`. What
|
compiled in: ramdisk (only in a `RAMDISK_ROOT` build), then USB, then the
|
||||||
is missing is that the root device is compiled in rather than selectable.
|
IDE disks, first SchFS volume wins. Verified with **no IDE disk attached
|
||||||
Without this the acceptance test above cannot run at all.
|
at all** — boots, mounts the stick, writes a file, and the file is still
|
||||||
|
there on the next boot. `dmesg` says `root: USB disk (device 3)`, and a
|
||||||
|
machine with no root names every candidate it looked at instead of
|
||||||
|
panicking with "no root filesystem". See
|
||||||
|
[../architecture/boot.md](../architecture/boot.md).
|
||||||
|
|
||||||
#### The `ls -lR` hang, and why it is closed
|
#### The `ls -lR` hang, and why it is closed
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -396,6 +396,7 @@ int klog_read(uint64 off, char *dst, int n);
|
||||||
uint64 klog_len(void);
|
uint64 klog_len(void);
|
||||||
uint64 klog_prev_bytes(void);
|
uint64 klog_prev_bytes(void);
|
||||||
int klog_recovered(void);
|
int klog_recovered(void);
|
||||||
|
int klog_render_syslog(char *dst, int cap);
|
||||||
|
|
||||||
// ksym.c -- naming a kernel address. See docs/architecture/debugging.md, D0.
|
// ksym.c -- naming a kernel address. See docs/architecture/debugging.md, D0.
|
||||||
int ksym_ready(void);
|
int ksym_ready(void);
|
||||||
|
|
|
||||||
|
|
@ -296,3 +296,95 @@ klog_read(uint64 off, char *dst, int n)
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render the ring as syslog(2) records, for dmesg.
|
||||||
|
//
|
||||||
|
// The ring holds plain readable text -- `[ 0.00] e820: ...` -- because it
|
||||||
|
// is also what a person reads with `cat /proc/kmsg`, and what PSOGM points at
|
||||||
|
// after a crash. syslog(2)'s callers expect Linux's record format, which is
|
||||||
|
// the same line with a priority in front:
|
||||||
|
//
|
||||||
|
// <6>[ 0.00] e820: 1017 MB usable in 8 regions
|
||||||
|
//
|
||||||
|
// toybox's dmesg parses each line with
|
||||||
|
//
|
||||||
|
// sscanf(msg, "<%u>[%llu.%llu] %n", ...) != 3 -> return
|
||||||
|
//
|
||||||
|
// and *silently drops* anything that does not match. So a working syslog(2)
|
||||||
|
// handing back the ring verbatim produced an empty dmesg with no error: every
|
||||||
|
// line was parsed, rejected and discarded. Only the `<6>` was missing -- the
|
||||||
|
// bracketed timestamp already matches, because scanf skips the leading spaces.
|
||||||
|
//
|
||||||
|
// Rendering here rather than storing the prefix in the ring: the write path
|
||||||
|
// runs in panic context and from every cprintf, and the stored form is read by
|
||||||
|
// humans. An ABI detail belongs at the boundary that has the ABI, which is the
|
||||||
|
// same rule sigset_from_user() follows one directory over.
|
||||||
|
//
|
||||||
|
// <6> is KERN_INFO. There are no per-message levels to derive one from, and
|
||||||
|
// inventing severity from the text would be a worse lie than a constant.
|
||||||
|
int
|
||||||
|
klog_render_syslog(char *dst, int cap)
|
||||||
|
{
|
||||||
|
uint64 have = klog_len(), off = 0;
|
||||||
|
int out = 0, atline = 1;
|
||||||
|
char chunk[128];
|
||||||
|
|
||||||
|
if (!klog || cap <= 0 || have == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Linux returns the *most recent* `cap` bytes. The prefix makes the output
|
||||||
|
// longer than the source, so find the first whole line that lets the rest
|
||||||
|
// fit: count what a render from `off` would cost, and drop lines from the
|
||||||
|
// front until it does. Whole lines, because half a record is worse to a
|
||||||
|
// parser than a missing one.
|
||||||
|
for (;;) {
|
||||||
|
uint64 scan = off;
|
||||||
|
int need = 0, nl = 1; // nl: next byte starts a line
|
||||||
|
while (scan < have) {
|
||||||
|
int got = klog_read(scan, chunk, sizeof(chunk));
|
||||||
|
if (got <= 0)
|
||||||
|
break;
|
||||||
|
for (int i = 0; i < got; i++) {
|
||||||
|
if (nl) { need += 3; nl = 0; }
|
||||||
|
need++;
|
||||||
|
if (chunk[i] == '\n') nl = 1;
|
||||||
|
}
|
||||||
|
scan += got;
|
||||||
|
}
|
||||||
|
if (need <= cap || off >= have)
|
||||||
|
break;
|
||||||
|
// Advance past one line.
|
||||||
|
uint64 p = off;
|
||||||
|
int done = 0;
|
||||||
|
while (p < have && !done) {
|
||||||
|
int got = klog_read(p, chunk, sizeof(chunk));
|
||||||
|
if (got <= 0) { off = have; break; }
|
||||||
|
for (int i = 0; i < got; i++) {
|
||||||
|
p++;
|
||||||
|
if (chunk[i] == '\n') { off = p; done = 1; break; }
|
||||||
|
}
|
||||||
|
if (!done) off = p;
|
||||||
|
}
|
||||||
|
if (!done && off >= have)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (off < have && out < cap) {
|
||||||
|
int got = klog_read(off, chunk, sizeof(chunk));
|
||||||
|
if (got <= 0)
|
||||||
|
break;
|
||||||
|
for (int i = 0; i < got && out < cap; i++) {
|
||||||
|
if (atline) {
|
||||||
|
if (out + 3 > cap)
|
||||||
|
return out; // never emit half a prefix
|
||||||
|
dst[out++] = '<'; dst[out++] = '6'; dst[out++] = '>';
|
||||||
|
atline = 0;
|
||||||
|
}
|
||||||
|
dst[out++] = chunk[i];
|
||||||
|
if (chunk[i] == '\n')
|
||||||
|
atline = 1;
|
||||||
|
}
|
||||||
|
off += got;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -836,6 +836,81 @@ yield(void)
|
||||||
sched();
|
sched();
|
||||||
release(&ptable.lock);
|
release(&ptable.lock);
|
||||||
}
|
}
|
||||||
|
// Which device the root filesystem is on.
|
||||||
|
//
|
||||||
|
// It was `#define ROOTDEV 1` -- IDE disk 1 -- so a kernel could only ever root
|
||||||
|
// on the disk it was *built* expecting. Booting the same image from a USB
|
||||||
|
// stick was impossible by construction, and that is the one thing standing
|
||||||
|
// between this kernel and its own acceptance test, which begins "boot on real
|
||||||
|
// hardware from a USB stick".
|
||||||
|
//
|
||||||
|
// A probe rather than a boot argument, because the answer is discoverable and
|
||||||
|
// asking someone to name a device number is a worse interface than looking.
|
||||||
|
// schfs_kmount() is already written to be used this way -- its own comment
|
||||||
|
// says "the uberblock scan doubles as the probe: no valid uberblock means this
|
||||||
|
// is simply not a SchFS volume, which is not an error worth reporting loudly"
|
||||||
|
// -- so each candidate costs one scan and answers yes or no. A failed probe
|
||||||
|
// leaves the slot unmounted and reusable, which is what makes a loop over
|
||||||
|
// candidates legal at all.
|
||||||
|
//
|
||||||
|
// **The order is preference, not confidence.** A ramdisk root is an explicit
|
||||||
|
// build choice and wins outright. After it, USB comes before the fixed disks:
|
||||||
|
// a stick that is plugged in was plugged in on purpose, and a machine with
|
||||||
|
// both should boot the thing the person is holding.
|
||||||
|
uint rootdev;
|
||||||
|
|
||||||
|
static int
|
||||||
|
rootdev_usable(uint dev)
|
||||||
|
{
|
||||||
|
if (dev == RAMDISK_DEV)
|
||||||
|
return ramdisk_present();
|
||||||
|
if (dev == USBDISK_DEV)
|
||||||
|
return usb_storage_present();
|
||||||
|
// iderw() panics on an absent drive rather than failing, so a probe has to
|
||||||
|
// ask first. That is the whole reason ide_disk_present() exists.
|
||||||
|
return ide_disk_present((int)dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rootdev_mount(void)
|
||||||
|
{
|
||||||
|
static const struct { uint dev; const char *what; } cand[] = {
|
||||||
|
#ifdef RAMDISK_ROOT
|
||||||
|
{ RAMDISK_DEV, "ramdisk" },
|
||||||
|
#endif
|
||||||
|
{ USBDISK_DEV, "USB disk" },
|
||||||
|
{ 1, "IDE disk 1" },
|
||||||
|
{ 0, "IDE disk 0" },
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < (int)NELEM(cand); i++) {
|
||||||
|
if (!rootdev_usable(cand[i].dev))
|
||||||
|
continue;
|
||||||
|
schfs_kmount(cand[i].dev);
|
||||||
|
if (schfs_kmounted()) {
|
||||||
|
rootdev = cand[i].dev;
|
||||||
|
kinfo("root: %s (device %d)\n", cand[i].what, cand[i].dev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
kinfo("root: %s (device %d) carries no SchFS volume\n",
|
||||||
|
cand[i].what, cand[i].dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name what was looked at. "no root filesystem" with no list is the least
|
||||||
|
// useful thing a kernel can say at the one moment somebody needs to know
|
||||||
|
// which disk it expected.
|
||||||
|
// Plain %s and no width. cprintf() has no field-width specifier -- it
|
||||||
|
// prints "%-11s" literally and then reads the *next* argument for the %d,
|
||||||
|
// which is how the first version of this message came out as
|
||||||
|
// `%-11s device 1596400`. See print_int() in kernel/core/console.c.
|
||||||
|
cprintf("root: no SchFS volume on any candidate device. Looked at:\n");
|
||||||
|
for (int i = 0; i < (int)NELEM(cand); i++)
|
||||||
|
cprintf(" device %d, %s: %s\n", cand[i].dev, cand[i].what,
|
||||||
|
rootdev_usable(cand[i].dev) ? "present, but no SchFS on it"
|
||||||
|
: "not present");
|
||||||
|
panic("no root filesystem");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
forkret(void) {
|
forkret(void) {
|
||||||
|
|
@ -860,17 +935,10 @@ forkret(void) {
|
||||||
iinit();
|
iinit();
|
||||||
mountinit();
|
mountinit();
|
||||||
|
|
||||||
// SchFS is the root filesystem.
|
// SchFS is the root filesystem, on whichever device turns out to have
|
||||||
//
|
// one. Here rather than in main() because mounting reads blocks, bread()
|
||||||
// Mounted here rather than in main() because mounting reads blocks,
|
// sleeps, and sleeping needs a process context.
|
||||||
// bread() sleeps, and sleeping needs a process context.
|
rootdev_mount();
|
||||||
//
|
|
||||||
// schfs_kmount() reports failure by leaving the volume unmounted rather
|
|
||||||
// than by returning -- it is a probe elsewhere, where "not SchFS" is an
|
|
||||||
// ordinary answer. Here it is fatal, so ask afterwards.
|
|
||||||
schfs_kmount(ROOTDEV);
|
|
||||||
if (!schfs_kmounted())
|
|
||||||
panic("schfs root: no SchFS volume on the root device");
|
|
||||||
// Re-point pid 1's cwd at the real root, now that there is one.
|
// Re-point pid 1's cwd at the real root, now that there is one.
|
||||||
//
|
//
|
||||||
// sinit() sets cwd = namei("/") back in main(), which is before this runs.
|
// sinit() sets cwd = namei("/") back in main(), which is before this runs.
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
#include "posix.h"
|
#include "posix.h"
|
||||||
#include "uaccess.h"
|
#include "uaccess.h"
|
||||||
#include "errno.h"
|
#include "errno.h"
|
||||||
|
#include "klog.h" /* KLOG_BYTES, for syslog(2) action 10 */
|
||||||
|
|
||||||
extern int linux_syscall_map[];
|
extern int linux_syscall_map[];
|
||||||
extern int linux_syscall_map_size;
|
extern int linux_syscall_map_size;
|
||||||
|
|
@ -126,8 +127,71 @@ sys_uname(void)
|
||||||
// socket/bind/listen/accept/connect live in sys_net.c now that they do
|
// socket/bind/listen/accept/connect live in sys_net.c now that they do
|
||||||
// something.
|
// something.
|
||||||
|
|
||||||
// syslog: kernel logging facility — no-op for user processes.
|
// syslog(2) -- what `dmesg` actually calls.
|
||||||
addr_t sys_syslog(void) { return 0; }
|
//
|
||||||
|
// This returned 0 for every action, and 0 to action 10 means "the log buffer
|
||||||
|
// is zero bytes". So `dmesg` printed nothing, on a system whose boot banner
|
||||||
|
// says *"or dmesg once you are in"* and whose entire quiet-boot design is
|
||||||
|
// "put the detail in the kernel log and tell them how to read it". The detail
|
||||||
|
// was there the whole time -- `cat /proc/kmsg` has always worked -- but the
|
||||||
|
// one command anybody would reach for could not see it.
|
||||||
|
//
|
||||||
|
// dmesg does not read /proc/kmsg. It calls klogctl(10) for the buffer size and
|
||||||
|
// klogctl(3) to read, which is this syscall, and a stub that answers 0 is
|
||||||
|
// indistinguishable from a machine that logged nothing.
|
||||||
|
//
|
||||||
|
// Only the actions that mean something here are implemented. The rest say so
|
||||||
|
// rather than returning 0: a console log level this kernel does not have is
|
||||||
|
// not a request that succeeded.
|
||||||
|
#define SYSLOG_CLOSE 0
|
||||||
|
#define SYSLOG_OPEN 1
|
||||||
|
#define SYSLOG_READ 2
|
||||||
|
#define SYSLOG_READ_ALL 3
|
||||||
|
#define SYSLOG_CLEAR 5
|
||||||
|
#define SYSLOG_SIZE_UNREAD 9
|
||||||
|
#define SYSLOG_SIZE_BUFFER 10
|
||||||
|
|
||||||
|
addr_t sys_syslog(void)
|
||||||
|
{
|
||||||
|
int type, len;
|
||||||
|
addr_t bufaddr;
|
||||||
|
|
||||||
|
argint(0, &type);
|
||||||
|
argaddr(1, &bufaddr);
|
||||||
|
argint(2, &len);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case SYSLOG_CLOSE:
|
||||||
|
case SYSLOG_OPEN:
|
||||||
|
return 0; // genuinely nothing to do, as on Linux
|
||||||
|
|
||||||
|
case SYSLOG_SIZE_BUFFER:
|
||||||
|
return KLOG_BYTES;
|
||||||
|
|
||||||
|
case SYSLOG_SIZE_UNREAD:
|
||||||
|
return (addr_t)klog_len();
|
||||||
|
|
||||||
|
case SYSLOG_READ_ALL: {
|
||||||
|
char *dst;
|
||||||
|
|
||||||
|
if (len < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
if (len == 0 || klog_len() == 0)
|
||||||
|
return 0;
|
||||||
|
if (argptr(1, &dst, len) < 0)
|
||||||
|
return -EFAULT;
|
||||||
|
return (addr_t)klog_render_syslog(dst, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reading destructively needs a per-reader cursor the ring does not carry,
|
||||||
|
// and clearing needs the ring to forget -- which is the one thing it exists
|
||||||
|
// not to do, since its whole purpose is surviving the kernel that wrote it.
|
||||||
|
case SYSLOG_READ:
|
||||||
|
case SYSLOG_CLEAR:
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// getrandom: fill buffer with pseudo-random bytes.
|
// getrandom: fill buffer with pseudo-random bytes.
|
||||||
addr_t sys_getrandom(void)
|
addr_t sys_getrandom(void)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue