diff --git a/src/OrcaSlicer.cpp b/src/OrcaSlicer.cpp index 53f0352c09..7e1341ac18 100644 --- a/src/OrcaSlicer.cpp +++ b/src/OrcaSlicer.cpp @@ -1317,7 +1317,13 @@ int CLI::run(int argc, char **argv) return CLI_INVALID_PARAMS; } BOOST_LOG_TRIVIAL(info) << "finished setup params, argc="<< argc << std::endl; - std::string temp_path = wxFileName::GetTempDir().utf8_str().data(); + std::string temp_path = per_user_temp_dir(wxFileName::GetTempDir().utf8_str().data(), per_user_temp_id()); + // Some consumers write into the temp root directly, so create it up front. + try { + boost::filesystem::create_directories(temp_path); + } catch (const std::exception &ex) { + BOOST_LOG_TRIVIAL(warning) << "failed to create per-user temp dir " << temp_path << ": " << ex.what(); + } set_temporary_dir(temp_path); m_extra_config.apply(m_config, true); diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index fee9f2e5ac..a76857dc4b 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -300,6 +300,10 @@ std::string header_gcodeviewer_generated(); // getpid platform wrapper extern unsigned get_current_pid(); +// Per-user id for isolating temp dirs; empty on Windows (its temp dir is already per-user). +std::string per_user_temp_id(); +// Per-user temp root under `base`; an empty `user_id` returns `base` unchanged. +std::string per_user_temp_dir(const std::string &base, const std::string &user_id); // BBS: backup & restore std::string get_process_name(int pid); diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index f3edb516ab..131369d212 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -1285,6 +1285,24 @@ unsigned get_current_pid() #endif } +std::string per_user_temp_id() +{ +#ifdef WIN32 + return {}; +#else + return std::to_string(static_cast(::getuid())); +#endif +} + +std::string per_user_temp_dir(const std::string &base, const std::string &user_id) +{ + if (user_id.empty()) + return base; + // Keep the id at the top level so each user's dir sits directly in the world-writable temp + // root; a shared parent dir would be owned by whichever user created it first. + return base + "/orcaslicer_" + user_id; +} + // BBS: backup & restore std::string get_process_name(int pid) { diff --git a/tests/libslic3r/CMakeLists.txt b/tests/libslic3r/CMakeLists.txt index d5e6eb958e..072e1a16b2 100644 --- a/tests/libslic3r/CMakeLists.txt +++ b/tests/libslic3r/CMakeLists.txt @@ -23,6 +23,7 @@ add_executable(${_TEST_NAME}_tests test_stl.cpp test_meshboolean.cpp test_marchingsquares.cpp + test_utils.cpp test_timeutils.cpp test_voronoi.cpp test_optimizers.cpp diff --git a/tests/libslic3r/test_utils.cpp b/tests/libslic3r/test_utils.cpp new file mode 100644 index 0000000000..c039069b2a --- /dev/null +++ b/tests/libslic3r/test_utils.cpp @@ -0,0 +1,54 @@ +#include + +#include "libslic3r/Utils.hpp" + +#ifndef _WIN32 +#include // getuid +#endif + +using namespace Slic3r; + +TEST_CASE("per_user_temp_dir composes a per-user temp root", "[utils]") { + const std::string base = "/tmp"; + + SECTION("an empty id returns base unchanged") { + REQUIRE(per_user_temp_dir(base, "") == base); + } + SECTION("a non-empty id is appended at the top level") { + REQUIRE(per_user_temp_dir(base, "1000") == base + "/orcaslicer_1000"); + } + SECTION("distinct ids produce distinct roots") { + REQUIRE(per_user_temp_dir(base, "1000") != per_user_temp_dir(base, "1001")); + } +} + +TEST_CASE("per_user_temp_id follows the platform contract", "[utils]") { + const std::string id = per_user_temp_id(); + + SECTION("stable across calls") { + REQUIRE(per_user_temp_id() == id); + } +#ifdef _WIN32 + SECTION("empty on Windows (its temp dir is already per-user)") { + REQUIRE(id.empty()); + } +#else + SECTION("the current uid on Linux/macOS") { + REQUIRE_FALSE(id.empty()); + REQUIRE(id == std::to_string(static_cast(::getuid()))); + } +#endif +} + +// The end-to-end contract callers depend on: the temp root is left alone on +// Windows and isolated per user on Linux/macOS. +TEST_CASE("per-user temp root is unchanged on Windows, isolated elsewhere", "[utils]") { + const std::string base = "/tmp"; + const std::string root = per_user_temp_dir(base, per_user_temp_id()); +#ifdef _WIN32 + REQUIRE(root == base); +#else + REQUIRE(root != base); + REQUIRE_THAT(root, Catch::Matchers::StartsWith(base + "/orcaslicer_")); +#endif +}