// Copyright (C) 2026 Viorel Munteanu // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. #include "pid_file.h" #include #include #include #include #include #include #include #include #include #include namespace { std::string sanitize_for_filename(std::string_view name) { std::string result; result.reserve(name.size()); for (char c : name) { if (std::isalnum(static_cast(c)) || c == '-' || c == '_' || c == '.') { result += c; } else { result += '_'; } } return result.empty() ? "container" : result; } std::filesystem::path session_run_dir() { const char* xdg_state_home = std::getenv("XDG_STATE_HOME"); std::filesystem::path state_home; if (xdg_state_home && *xdg_state_home) { state_home = xdg_state_home; } else { const char* home = std::getenv("HOME"); state_home = std::filesystem::path(home ? home : "") / ".local" / "state"; } return state_home / "slocker-lite" / "run"; } } // namespace std::filesystem::path session_pid_file_path(std::string_view container_name, pid_t pid) { return session_run_dir() / fmt::format("{}-{}", sanitize_for_filename(container_name), pid); } std::optional create_session_lock(std::string_view container_name, pid_t pid) { auto path = session_pid_file_path(container_name, pid); std::error_code ec; std::filesystem::create_directories(path.parent_path(), ec); if (ec) { spdlog::warn("failed to create session state directory {}: {}", path.parent_path().string(), ec.message()); return std::nullopt; } int fd = open(path.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0644); if (fd < 0) { spdlog::warn("failed to create session pid file {}: {}", path.string(), strerror(errno)); return std::nullopt; } if (flock(fd, LOCK_EX | LOCK_NB) != 0) { spdlog::warn("failed to lock session pid file {}: {}", path.string(), strerror(errno)); close(fd); return std::nullopt; } std::string contents = fmt::format("{}\n", pid); if (write(fd, contents.data(), contents.size()) < 0) { spdlog::warn("failed to write session pid file {}: {}", path.string(), strerror(errno)); } return SessionLock{path, fd}; } void release_session_lock(const SessionLock& lock) { if (lock.fd >= 0) { close(lock.fd); } std::error_code ec; std::filesystem::remove(lock.path, ec); if (ec) { spdlog::warn("failed to remove session pid file {}: {}", lock.path.string(), ec.message()); } } std::vector list_sessions() { std::vector sessions; auto dir = session_run_dir(); std::error_code dir_ec; if (!std::filesystem::is_directory(dir, dir_ec)) { return sessions; } std::error_code it_ec; for (auto it = std::filesystem::directory_iterator(dir, it_ec); !it_ec && it != std::filesystem::directory_iterator(); it.increment(it_ec)) { const auto& path = it->path(); int fd = open(path.c_str(), O_RDWR); if (fd < 0) { spdlog::debug("failed to open session file {}: {}", path.string(), strerror(errno)); continue; } char buf[32] = {}; ssize_t n = read(fd, buf, sizeof(buf) - 1); pid_t pid = n > 0 ? static_cast(std::atoi(buf)) : 0; bool running = true; if (flock(fd, LOCK_EX | LOCK_NB) == 0) { running = false; flock(fd, LOCK_UN); } close(fd); if (pid <= 0) { spdlog::debug("skipping malformed session pid file {}", path.string()); continue; } std::string filename = path.filename().string(); std::string suffix = fmt::format("-{}", pid); std::string container_name = filename.size() > suffix.size() && filename.compare(filename.size() - suffix.size(), suffix.size(), suffix) == 0 ? filename.substr(0, filename.size() - suffix.size()) : filename; sessions.push_back(SessionInfo{pid, container_name, running}); } return sessions; }