Commit cdedf5fe authored by jan.koester's avatar jan.koester
Browse files

test

parent bb72cb39
Loading
Loading
Loading
Loading
+8 −1
Changes for src/journal.cpp: 8 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -97,6 +97,8 @@ namespace syslite {

    namespace {

    bool consoleEcho = true;

    class TeeBuf : public std::streambuf {
    public:
        explicit TeeBuf(int console) : _console(console) {}
@@ -116,7 +118,7 @@ namespace syslite {
    private:
        void emit() {
            std::string out = _line + "\n";
            if (_console >= 0) (void)!::write(_console, out.data(), out.size());
            if (_console >= 0 && consoleEcho) (void)!::write(_console, out.data(), out.size());
            // "[syslite] Starting x" -> "Starting x": the tag says syslite
            std::string l = _line;
            if (l.rfind("[syslite] ", 0) == 0) l = l.substr(10);
@@ -130,6 +132,11 @@ namespace syslite {

    }  // namespace

    void setConsoleEcho(bool on) {
        std::cout.flush();
        consoleEcho = on;
    }

    void captureStandardStreams() {
        // one buffer for both: interleaved lines stay whole
        static TeeBuf buf([] {
+7 −2
Changes for src/journal.h: 7 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -37,10 +37,15 @@ namespace syslite {
        long long _nextTryMs = 0;
    };

    // std::cout and std::cerr: each line to the console (as before) and
    // into the journal as "syslite"
    // std::cout and std::cerr: each line into the journal as "syslite",
    // and to the console while that is on
    void captureStandardStreams();

    // syslite's lines on the console too: on while booting, off once the
    // TTYs run (the installer's screen, login prompts) -- then only the
    // journal has them
    void setConsoleEcho(bool on);

    // "2026-10-04T08:12:33.123+0200"
    std::string journalTimestamp();

+151 −33
Changes for src/service.cpp: 151 added lines, 33 removed lines.
Original line number Diff line number Diff line
@@ -544,6 +544,10 @@ namespace syslite {

    void ServiceManager::handleChild(pid_t pid, int status) {
        for (auto &[name, entry] : _services) {
            if (entry.pid == pid && entry.unit.kind == UnitKind::Mount) {
                finishMountHelper(entry, status);
                return;
            }
            if (entry.pid == pid) {
                entry.exitCode = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
                entry.pid = -1;
@@ -652,26 +656,116 @@ namespace syslite {

    // ---- mounts ----

    bool ServiceManager::tryMount(ServiceEntry &entry) {
    bool ServiceManager::spawnMountHelper(ServiceEntry &entry, bool unmount) {
        if (entry.outFd >= 0) drainOutput(entry);
        if (entry.outFd >= 0) { close(entry.outFd); entry.outFd = -1; }
        int out[2];
        if (pipe2(out, O_CLOEXEC) != 0) {
            entry.lastError = std::string("pipe: ") + strerror(errno);
            return false;
        }
        fcntl(out[0], F_SETFL, O_NONBLOCK);
        pid_t pid = fork();
        if (pid < 0) {
            entry.lastError = std::string("fork: ") + strerror(errno);
            close(out[0]);
            close(out[1]);
            return false;
        }
        if (pid == 0) {
            // only write(2) here: syslite's own streams go to the console
            dup2(out[1], STDOUT_FILENO);
            dup2(out[1], STDERR_FILENO);
            const UnitFile &u = entry.unit;
        std::string type = u.mountType;
        std::string probed;
            auto say = [](const std::string &l) {
                std::string line = l + "\n";
                (void)!::write(STDOUT_FILENO, line.data(), line.size());
            };
            std::string err;
            if (unmount) {
                if (!unmountFilesystem(u.mountWhere, false, &err)) { say(err); _exit(1); }
                say("Unmounted " + u.mountWhere);
                _exit(0);
            }
            std::string probed, type = u.mountType;
            std::string dev = resolveMountSource(u.mountWhat, &probed);
        if (dev.empty()) return false;  // not there (yet)
            if (dev.empty()) _exit(3);  // not there (yet)
            if (type.empty()) type = probed;
        std::string err;
        entry.mountRetryMs = entry.mountDeadlineMs = 0;
        if (!mountFilesystem(dev, u.mountWhere, type, u.mountOptions, &err)) {
            entry.lastError = err;
            entry.state = ServiceState::Failed;
            std::cerr << "[syslite] " << u.name << ": " << err << std::endl;
            if (!mountFilesystem(dev, u.mountWhere, type, u.mountOptions, &err)) { say(err); _exit(1); }
            say("Mounted " + dev + " on " + u.mountWhere);
            _exit(0);
        }
        close(out[1]);
        entry.outFd = out[0];
        entry.pid = entry.outPid = pid;
        entry.helperUnmounts = unmount;
        return true;
    }
        entry.mountSource = dev;

    bool ServiceManager::waitMountHelper(ServiceEntry &entry, int ms) {
        for (int waited = 0; entry.pid > 0; waited += 10) {
            drainOutput(entry);
            int status;
            pid_t r = waitpid(entry.pid, &status, WNOHANG);
            if (r == entry.pid) {
                finishMountHelper(entry, status);
                return true;
            }
            if (r < 0 && errno != EINTR) { entry.pid = -1; return true; }
            if (waited >= ms) return false;
            usleep(10000);
        }
        return true;
    }

    void ServiceManager::finishMountHelper(ServiceEntry &entry, int status) {
        drainOutput(entry);
        entry.pid = -1;
        const UnitFile &u = entry.unit;
        int code = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
        long long now = monotonicMs();
        if (entry.helperUnmounts) {
            entry.helperUnmounts = false;
            if (code == 0) {
                entry.state = ServiceState::Stopped;
                entry.lastError.clear();
                std::cout << "[syslite] Unmounted " << u.mountWhere << std::endl;
            } else {
                entry.lastError = entry.outLast.empty() ? "umount " + u.mountWhere + " failed" : entry.outLast;
                entry.state = isMountPoint(u.mountWhere) ? ServiceState::Running : ServiceState::Stopped;
                std::cerr << "[syslite] " << u.name << ": " << entry.lastError << std::endl;
            }
            return;
        }
        if (code == 0) {
            entry.state = ServiceState::Running;
        std::cout << "[syslite] Mounted " << dev << " on " << u.mountWhere << std::endl;
        return true;
            entry.lastError.clear();
            entry.mountRetryMs = entry.mountDeadlineMs = 0;
            // "Mounted /dev/x on /where"
            if (entry.outLast.rfind("Mounted ", 0) == 0)
                entry.mountSource = entry.outLast.substr(8, entry.outLast.find(" on ") - 8);
            std::cout << "[syslite] " << entry.outLast << std::endl;
        } else if (code == 3 && entry.state == ServiceState::Starting) {
            // the device isn't there yet (an md array or LVM volume set up
            // later, a slow USB disk): checkMounts() tries again
            if (now >= entry.mountDeadlineMs) {
                entry.mountRetryMs = entry.mountDeadlineMs = 0;
                entry.state = ServiceState::Failed;
                entry.lastError = u.mountWhat + " didn't show up";
                std::cerr << "[syslite] " << u.name << ": " << entry.lastError << std::endl;
            } else {
                if (entry.lastError.rfind("waiting for ", 0) != 0)
                    std::cout << "[syslite] " << u.name << ": waiting for " << u.mountWhat << std::endl;
                entry.lastError = "waiting for " + u.mountWhat;
                entry.mountRetryMs = now + 1000;
            }
        } else if (code != 3) {
            entry.mountRetryMs = entry.mountDeadlineMs = 0;
            entry.state = ServiceState::Failed;
            entry.lastError = WIFSIGNALED(status) ? "mount helper killed (signal " + std::to_string(WTERMSIG(status)) + ")"
                            : entry.outLast.empty() ? "mount failed" : entry.outLast;
            std::cerr << "[syslite] " << u.name << ": " << entry.lastError << std::endl;
        }
    }

    bool ServiceManager::startMount(ServiceEntry &entry) {
@@ -689,52 +783,75 @@ namespace syslite {
            entry.state = ServiceState::Running;
            return true;
        }
        if (tryMount(entry)) return entry.state == ServiceState::Running;
        // the device isn't there yet (an md array or LVM volume set up
        // later, a slow USB disk): checkMounts() tries again
        if (entry.pid > 0) return true;  // a helper is at it already
        long long now = monotonicMs();
        entry.state = ServiceState::Starting;
        entry.lastError = "waiting for " + u.mountWhat;
        entry.mountRetryMs = now + 1000;
        entry.lastError.clear();
        entry.mountRetryMs = 0;
        entry.mountDeadlineMs = now + 1000LL * (u.mountTimeoutSec > 0 ? u.mountTimeoutSec : 90);
        std::cout << "[syslite] " << u.name << ": waiting for " << u.mountWhat << std::endl;
        return true;
        if (!spawnMountHelper(entry, false)) {
            entry.state = ServiceState::Failed;
            return false;
        }
        // usually done at once; a helper still at it after 10 s (a disk
        // that doesn't answer) finishes later, through handleChild()
        if (!waitMountHelper(entry, 10000)) {
            entry.lastError = "still mounting (the device is slow or doesn't answer)";
            std::cerr << "[syslite] " << u.name << ": " << entry.lastError << std::endl;
        }
        return entry.state != ServiceState::Failed;
    }

    bool ServiceManager::stopMount(ServiceEntry &entry, bool lazy) {
        entry.mountRetryMs = entry.mountDeadlineMs = 0;
        const std::string &where = entry.unit.mountWhere;
        if (!isMountPoint(where)) {
            entry.state = ServiceState::Stopped;
            if (entry.pid <= 0) entry.state = ServiceState::Stopped;
            return true;
        }
        std::string err;
        sync();
        if (!unmountFilesystem(where, lazy, &err)) {
        if (lazy) {
            // the shutdown: detaching doesn't wait for anything
            if (!unmountFilesystem(where, true, &err)) {
                entry.lastError = err;
            std::cerr << "[syslite] " << entry.unit.name << ": " << err << std::endl;
                return false;
            }
        entry.lastError.clear();
            entry.state = ServiceState::Stopped;
        std::cout << "[syslite] Unmounted " << where << std::endl;
            return true;
        }
        if (entry.pid > 0) {
            entry.lastError = entry.helperUnmounts ? "still unmounting" : "still mounting";
            return false;
        }
        entry.state = ServiceState::Stopping;
        if (!spawnMountHelper(entry, true)) {
            entry.state = ServiceState::Running;
            return false;
        }
        if (!waitMountHelper(entry, 10000)) {
            entry.lastError = "still unmounting (the device is slow or doesn't answer)";
            std::cerr << "[syslite] " << entry.unit.name << ": " << entry.lastError << std::endl;
            return false;
        }
        return entry.state == ServiceState::Stopped;
    }

    void ServiceManager::checkMounts() {
        long long now = monotonicMs();
        for (auto &[name, entry] : _services) {
            if (entry.unit.kind != UnitKind::Mount) continue;
            if (entry.state == ServiceState::Starting && entry.mountRetryMs && now >= entry.mountRetryMs) {
                if (tryMount(entry)) continue;
                if (now >= entry.mountDeadlineMs) {
                    entry.mountRetryMs = entry.mountDeadlineMs = 0;
            if (entry.state != ServiceState::Starting) continue;
            if (entry.pid > 0) {
                // a helper that hangs past the deadline: the device doesn't answer
                if (entry.mountDeadlineMs && now >= entry.mountDeadlineMs) {
                    entry.mountDeadlineMs = entry.mountRetryMs = 0;
                    entry.state = ServiceState::Failed;
                    entry.lastError = entry.unit.mountWhat + " didn't show up";
                    entry.lastError = entry.unit.mountWhat + " doesn't answer (mounting hangs)";
                    std::cerr << "[syslite] " << name << ": " << entry.lastError << std::endl;
                } else {
                    entry.mountRetryMs = now + 1000;
                }
            } else if (entry.mountRetryMs && now >= entry.mountRetryMs) {
                entry.mountRetryMs = 0;
                spawnMountHelper(entry, false);  // handleChild() takes its end
            }
        }
        // unmounted (or mounted) behind syslite's back: once a second
@@ -770,6 +887,7 @@ namespace syslite {
                if (buf[i] == '\n' || entry.outLine.size() >= 8192) {
                    if (!entry.outLine.empty() && entry.outLine.back() == '\r') entry.outLine.pop_back();
                    Journal::instance().write(entry.unit.name, entry.outPid, entry.outLine);
                    entry.outLast = entry.outLine;
                    entry.outLine.clear();
                    if (buf[i] == '\n') continue;
                }
+7 −1
Changes for src/service.h: 7 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -37,6 +37,8 @@ namespace syslite {
        int           outFd = -1;           // read end of its pipe
        pid_t         outPid = -1;          // whose lines they are
        std::string   outLine;              // an unfinished line
        std::string   outLast;              // the last whole line
        bool          helperUnmounts = false;  // a mount's helper (pid) unmounts
    };


@@ -94,9 +96,13 @@ namespace syslite {
        // waitpid() for a child that writes into entry's pipe meanwhile
        void waitDraining(ServiceEntry &entry, pid_t pid, int *status);
        bool startMount(ServiceEntry &entry);
        bool tryMount(ServiceEntry &entry);
        bool stopMount(ServiceEntry &entry, bool lazy);
        void checkMounts();
        // mounting and unmounting run in a helper process (a dying disk
        // stalls that, not PID 1); waiting for it at most ms
        bool spawnMountHelper(ServiceEntry &entry, bool unmount);
        bool waitMountHelper(ServiceEntry &entry, int ms);
        void finishMountHelper(ServiceEntry &entry, int status);
        void setupChildProcess(const ServiceEntry &entry);
        void createRuntimeDirectory(const ServiceEntry &entry);
        std::vector<std::string> buildArgv(const std::string &cmdline);
+14 −3
Changes for src/syslite.cpp: 14 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -34,7 +34,8 @@ namespace syslite {
        // Configuration is compile-time (config.h). Hostname from /etc/hostname.
        // Register TTYs based on compiled-in count.
        // On a live ISO (squashfs boot), run the installer on tty1.
        bool liveMode = std::filesystem::exists("/usr/sbin/fenriros-install")
        bool &liveMode = _liveMode;
        liveMode = std::filesystem::exists("/usr/sbin/fenriros-install")
                     && !std::filesystem::exists("/etc/fstab")
                     && !std::filesystem::exists("/etc/fstab.yaml");  // systempp's fstab
        for (int i = 1; i <= _numTtys; ++i) {
@@ -518,8 +519,17 @@ namespace syslite {
        std::cout << "[syslite] Starting target: " << _defaultTarget << std::endl;
        _services.startTarget(_defaultTarget);

        // Spawn TTYs
        // Spawn TTYs. From now on the console is theirs (the installer's
        // screen on a live system, login prompts): syslite's messages go
        // only into the journal, and while the installer runs the kernel
        // shows only emergencies there
        if (isPid1) {
            std::cout << "[syslite] Boot finished; messages from now on: journalctl" << std::endl;
            setConsoleEcho(false);
            if (_liveMode) {
                std::ofstream printk("/proc/sys/kernel/printk");
                printk << "1";
            }
            _ttys.startAll();
        }

@@ -564,7 +574,8 @@ namespace syslite {
        }
        _bus.close();

        // Shutdown sequence
        // Shutdown sequence: on the console again (the TTYs go away)
        if (isPid1) setConsoleEcho(true);
        std::cout << std::endl;
        std::cout << "[syslite] Shutting down..." << std::endl;

Loading