Nexoniarz is the sole author and SchOS was never published under Apache-2.0, so this is a straight relicence. LICENSE is the FSF GPLv2 text, every first-party SPDX header moves to GPL-2.0-or-later, and NOTICE, README.md, docs/licensing.md and the site say so. projects/schfs-linux follows the rest of the tree. The GPL gives copyleft: a fork has to ship its source. It also lifts the old "Linux drivers, never" rule, since GPL-2.0-only code combines with GPL-2.0-or-later. "Or later" leaves room for GPLv3, but one binary still has to pick one version, so docs/licensing.md now keeps Apache-2.0 and GPLv3 code out of the kernel to keep Linux code possible there. BSD 4-clause code goes from "avoid" to "no", because the advertising clause is GPL-incompatible. LDLS still ships no .ko. That boundary is now project policy rather than a legal requirement, and ldls.md, devices.md and licensing.md section 6 say so. refmod declares license=GPL, and ldls-check passes (27 checks). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
189 lines
6.1 KiB
C
189 lines
6.1 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
//
|
|
// resolvconf -- put the nameserver where the resolver will look.
|
|
//
|
|
// SchOS has no DNS client, and the interesting part is that it does not need
|
|
// one. musl contains a complete resolver: `getaddrinfo` reads
|
|
// /etc/resolv.conf, sends a query over an ordinary UDP socket and parses the
|
|
// reply, and every one of those pieces already worked here. The single missing
|
|
// fact was *which* nameserver -- which arrives in a DHCP reply the kernel
|
|
// handled and never told anyone about.
|
|
//
|
|
// That is what made `wget` look broken. It was not wget, and it was not the
|
|
// core utilities: nothing on this system could turn a name into an address, so
|
|
// every program that took a hostname failed the same way and each one looked
|
|
// like its own separate bug.
|
|
//
|
|
// resolvconf wait for a nameserver, write it, exit
|
|
// resolvconf -n write whatever there is now, do not wait
|
|
// resolvconf -p print what the kernel has, change nothing
|
|
//
|
|
// The kernel publishes /proc/resolv.conf and stops there, deliberately: it
|
|
// renders resolv.conf's exact syntax so this is a copy rather than a
|
|
// translation, and *whether* /etc/resolv.conf should be overwritten is a
|
|
// policy question. A machine with a hand-written one should keep it, which is
|
|
// why -f exists and why it is not the default.
|
|
|
|
#include "types.h"
|
|
#include "user.h"
|
|
#include "fcntl.h"
|
|
|
|
#define SRC "/proc/resolv.conf"
|
|
#define DST "/etc/resolv.conf"
|
|
|
|
// DHCP takes a few seconds on a quiet network and longer on a busy one. Two
|
|
// minutes is past any of that and short enough that a machine with no DHCP
|
|
// server does not hold a boot job open for ever.
|
|
#define WAIT_SECONDS 120
|
|
|
|
static void
|
|
puts_(const char *s)
|
|
{
|
|
int n = 0;
|
|
while (s[n]) n++;
|
|
write(1, (void *)s, n);
|
|
}
|
|
|
|
// Whether this boot asked to be told what is happening; see /proc/bootmode and
|
|
// kinfo() in kernel/core/console.c for the same distinction one level down.
|
|
//
|
|
// Success here is progress, not news. Names starting to work is the expected
|
|
// outcome of a normal boot, and printing it unconditionally meant a quiet boot
|
|
// still opened with a line about the nameserver -- the one line that was
|
|
// supposed to be "Welcome to SchOS!". Failures stay on puts_(), because a
|
|
// machine where nothing can resolve a hostname should say so at any verbosity.
|
|
static int verbose_boot;
|
|
|
|
static void
|
|
read_bootmode(void)
|
|
{
|
|
char b[16];
|
|
int fd = open("/proc/bootmode", O_RDONLY);
|
|
int n;
|
|
|
|
if (fd < 0)
|
|
return;
|
|
n = read(fd, b, sizeof(b) - 1);
|
|
close(fd);
|
|
if (n > 0) { b[n] = 0; verbose_boot = (b[0] == 'v'); }
|
|
}
|
|
|
|
static int
|
|
same(const char *a, const char *b, int n)
|
|
{
|
|
for (int i = 0; i < n; i++)
|
|
if (a[i] != b[i])
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
slurp(const char *path, char *buf, int cap)
|
|
{
|
|
int fd = open((char *)path, O_RDONLY);
|
|
int n, len = 0;
|
|
|
|
if (fd < 0)
|
|
return -1;
|
|
while (len < cap - 1 && (n = read(fd, buf + len, cap - 1 - len)) > 0)
|
|
len += n;
|
|
close(fd);
|
|
buf[len] = 0;
|
|
return len;
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
char buf[512];
|
|
int i, n, fd, wait = 1, force = 0, print = 0;
|
|
|
|
read_bootmode();
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
if (argv[i][0] != '-' || argv[i][2] != 0)
|
|
goto usage;
|
|
switch (argv[i][1]) {
|
|
case 'n': wait = 0; break;
|
|
case 'f': force = 1; break;
|
|
case 'p': print = 1; wait = 0; break;
|
|
default:
|
|
usage:
|
|
puts_("usage: resolvconf [-n] [-f] [-p]\n"
|
|
" -n do not wait for DHCP\n"
|
|
" -f overwrite an existing " DST "\n"
|
|
" -p print what the kernel has and exit\n");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// Wait by polling rather than by blocking on the file: /proc has no
|
|
// readiness to report and inventing one for this would be a worse
|
|
// interface than a loop that ends.
|
|
for (i = 0; ; i++) {
|
|
n = slurp(SRC, buf, sizeof(buf));
|
|
if (n < 0) {
|
|
puts_("resolvconf: no " SRC " -- this kernel has no network\n");
|
|
return 1;
|
|
}
|
|
if (n > 0 || !wait || i >= WAIT_SECONDS)
|
|
break;
|
|
sleep(100); // one second, at 100 Hz
|
|
}
|
|
|
|
if (print) {
|
|
if (n > 0) write(1, buf, n);
|
|
else puts_("resolvconf: the kernel has no nameserver yet\n");
|
|
return n > 0 ? 0 : 1;
|
|
}
|
|
|
|
if (n == 0) {
|
|
puts_("resolvconf: no nameserver after ");
|
|
puts_(wait ? "two minutes" : "no wait");
|
|
puts_(" -- is there a DHCP server?\n");
|
|
return 1;
|
|
}
|
|
|
|
// An existing file is somebody's decision until told otherwise.
|
|
//
|
|
// Saying so every boot is the wrong volume for it. On a machine that has
|
|
// been up before, /etc/resolv.conf always exists and always says the same
|
|
// thing, so this printed a line about having done nothing on every single
|
|
// start -- which is exactly the kind of message that teaches people to
|
|
// stop reading their own boot.
|
|
//
|
|
// Quiet when the file already says what this would have written. It is
|
|
// still worth a word when the two *disagree*, because then the nameserver
|
|
// has changed and the machine is using the old one.
|
|
if (!force) {
|
|
char old[512];
|
|
int on = slurp(DST, old, sizeof(old));
|
|
if (on > 0) {
|
|
if (on == n && same(old, buf, n))
|
|
return 0; // already right; nothing to say
|
|
puts_("resolvconf: " DST " differs from what DHCP gave;"
|
|
" -f to replace it\n");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
fd = open(DST, O_WRONLY | O_CREAT | O_TRUNC);
|
|
if (fd < 0) {
|
|
puts_("resolvconf: cannot write " DST "\n");
|
|
return 1;
|
|
}
|
|
if (write(fd, buf, n) != n) {
|
|
puts_("resolvconf: short write to " DST "\n");
|
|
close(fd);
|
|
return 1;
|
|
}
|
|
close(fd);
|
|
|
|
// Worth one line under -v, because it is the moment names start working
|
|
// and somebody watching a verbose boot may be waiting to know it.
|
|
if (verbose_boot) {
|
|
puts_("resolvconf: ");
|
|
write(1, buf, n);
|
|
}
|
|
return 0;
|
|
}
|