// SPDX-License-Identifier: Apache-2.0 // // 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); } 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; 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. if (!force) { char old[512]; if (slurp(DST, old, sizeof(old)) > 0) { puts_("resolvconf: " DST " already exists; -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); puts_("resolvconf: "); write(1, buf, n); return 0; }