From c00f25c74ef71554ac8ea945cc324fea24b1396e Mon Sep 17 00:00:00 2001 From: Dave Dykstra <2129743+DrDaveD@users.noreply.github.com> Date: Tue, 24 Dec 2024 10:27:40 -0600 Subject: [PATCH] add CVMFS_INFO_HEADER --- cvmfs/cache_plugin/libcvmfs_cache.cc | 2 +- cvmfs/cache_stream.cc | 2 +- cvmfs/capabilities.cc | 56 ++++++- cvmfs/capabilities.h | 2 + cvmfs/cvmfs.cc | 9 +- cvmfs/fetch.cc | 2 +- cvmfs/loader.cc | 14 +- cvmfs/loader.h | 2 + cvmfs/monitor.cc | 30 ++-- cvmfs/monitor.h | 4 +- cvmfs/mountpoint.cc | 19 ++- cvmfs/mountpoint.h | 6 +- cvmfs/network/download.cc | 80 ++-------- cvmfs/network/download.h | 7 +- cvmfs/network/jobinfo.cc | 217 ++++++++++++++++++++++++++- cvmfs/network/jobinfo.h | 14 +- cvmfs/quota_posix.cc | 2 +- cvmfs/swissknife_sync.cc | 2 +- 18 files changed, 361 insertions(+), 109 deletions(-) diff --git a/cvmfs/cache_plugin/libcvmfs_cache.cc b/cvmfs/cache_plugin/libcvmfs_cache.cc index 904114aff6..f63351298c 100644 --- a/cvmfs/cache_plugin/libcvmfs_cache.cc +++ b/cvmfs/cache_plugin/libcvmfs_cache.cc @@ -361,7 +361,7 @@ void cvmcache_get_session(cvmcache_session *session) { void cvmcache_spawn_watchdog(const char *crash_dump_file) { if (g_watchdog != NULL) return; - g_watchdog = Watchdog::Create(NULL); + g_watchdog = Watchdog::Create(NULL, false); assert(g_watchdog != NULL); g_watchdog->Spawn((crash_dump_file != NULL) ? string(crash_dump_file) : ""); } diff --git a/cvmfs/cache_stream.cc b/cvmfs/cache_stream.cc index c88dca51e8..963840a2c8 100644 --- a/cvmfs/cache_stream.cc +++ b/cvmfs/cache_stream.cc @@ -179,7 +179,7 @@ int64_t StreamingCacheManager::Stream( download::JobInfo download_job(&url, is_zipped, true /* probe_hosts */, &info.object_id, &sink); - download_job.SetExtraInfo(&info.label.path); + download_job.SetPathInfo(&info.label.path); download_job.SetRangeOffset(info.label.range_offset); download_job.SetRangeSize(static_cast(info.label.size)); ClientCtx *ctx = ClientCtx::GetInstance(); diff --git a/cvmfs/capabilities.cc b/cvmfs/capabilities.cc index 985c92312a..b635f1ad43 100644 --- a/cvmfs/capabilities.cc +++ b/cvmfs/capabilities.cc @@ -25,11 +25,21 @@ bool ClearPermittedCapabilities(int, return true; } +static uid_t old_uid; +static gid_t old_gid; + static bool obtainCapability(const cap_value_t, const char *) { // there are no individual capabilities on OSX so switch back to root + old_uid = geteuid(); + old_gid = getegid(); return (SwitchCredentials(0, getgid(), true)); } +static bool dropCapability(const cap_value_t, const char *) { + // there are no individual capabilities on OSX so temporarily back to user + return (SwitchCredentials(old_uid, old_gid, true)); +} + #else // Clear all CAP_PERMITTED capabilities except those requested. @@ -174,9 +184,41 @@ static bool obtainCapability(const cap_value_t cap, const char *capname) { if (retval != 0) { LogCvmfs(kLogCvmfs, kLogStderr, - "Cannot reset capabilities for current process " - "(errno: %d)", - errno); + "Cannot set %s capability for current process (errno: %d)", + capname, errno); + return false; + } + + return true; +} + +static bool dropCapability(const cap_value_t cap, const char *capname) { +#ifdef CAP_IS_SUPPORTED + assert(CAP_IS_SUPPORTED(cap)); +#endif + + cap_t caps_proc = cap_get_proc(); + assert(caps_proc != NULL); + + cap_flag_value_t cap_state; + int retval = cap_get_flag(caps_proc, cap, CAP_EFFECTIVE, &cap_state); + assert(retval == 0); + + if (cap_state == CAP_CLEAR) { + cap_free(caps_proc); + return true; + } + + retval = cap_set_flag(caps_proc, CAP_EFFECTIVE, 1, &cap, CAP_CLEAR); + assert(retval == 0); + + retval = cap_set_proc(caps_proc); + cap_free(caps_proc); + + if (retval != 0) { + LogCvmfs(kLogCvmfs, kLogStderr, + "Cannot reset %s capability for current process (errno: %d)", + capname, errno); return false; } @@ -189,6 +231,10 @@ bool ObtainDacReadSearchCapability() { return obtainCapability(CAP_DAC_READ_SEARCH, "CAP_DAC_READ_SEARCH"); } +bool DropDacReadSearchCapability() { + return dropCapability(CAP_DAC_READ_SEARCH, "CAP_DAC_READ_SEARCH"); +} + bool ObtainSysAdminCapability() { return obtainCapability(CAP_SYS_ADMIN, "CAP_SYS_ADMIN"); } @@ -197,6 +243,10 @@ bool ObtainSysPtraceCapability() { return obtainCapability(CAP_SYS_PTRACE, "CAP_SYS_PTRACE"); } +bool DropSysPtraceCapability() { + return dropCapability(CAP_SYS_PTRACE, "CAP_SYS_PTRACE"); +} + #ifdef CVMFS_NAMESPACE_GUARD } // namespace CVMFS_NAMESPACE_GUARD diff --git a/cvmfs/capabilities.h b/cvmfs/capabilities.h index 387b5e5f55..b7ac497dfd 100644 --- a/cvmfs/capabilities.h +++ b/cvmfs/capabilities.h @@ -19,8 +19,10 @@ namespace CVMFS_NAMESPACE_GUARD { #endif bool ObtainDacReadSearchCapability(); +bool DropDacReadSearchCapability(); bool ObtainSysAdminCapability(); bool ObtainSysPtraceCapability(); +bool DropSysPtraceCapability(); bool ClearPermittedCapabilities(int nreservecaps, const cap_value_t *reservecaps, int ninheritablecaps, diff --git a/cvmfs/cvmfs.cc b/cvmfs/cvmfs.cc index 1793be95a8..6a1a43a9ed 100644 --- a/cvmfs/cvmfs.cc +++ b/cvmfs/cvmfs.cc @@ -2308,6 +2308,11 @@ static unsigned CheckMaxOpenFiles() { } +static bool NeedsReadEnviron() { + return MountPoint::needs_read_environ(cvmfs::options_mgr_); +} + + static int Init(const loader::LoaderExports *loader_exports) { g_boot_error = new string("unknown error"); cvmfs::loader_exports_ = loader_exports; @@ -2323,7 +2328,8 @@ static int Init(const loader::LoaderExports *loader_exports) { // Monitor, check for maximum number of open files if (cvmfs::UseWatchdog()) { auto_umount::SetMountpoint(loader_exports->mount_point); - cvmfs::watchdog_ = Watchdog::Create(auto_umount::UmountOnExit); + cvmfs::watchdog_ = Watchdog::Create(auto_umount::UmountOnExit, + NeedsReadEnviron()); if (cvmfs::watchdog_ == NULL) { *g_boot_error = "failed to initialize watchdog."; return loader::kFailMonitor; @@ -3001,6 +3007,7 @@ static void __attribute__((constructor)) LibraryMain() { g_cvmfs_exports->fnSaveState = SaveState; g_cvmfs_exports->fnRestoreState = RestoreState; g_cvmfs_exports->fnFreeSavedState = FreeSavedState; + g_cvmfs_exports->fnNeedsReadEnviron = NeedsReadEnviron; cvmfs::SetCvmfsOperations(&g_cvmfs_exports->cvmfs_operations); } diff --git a/cvmfs/fetch.cc b/cvmfs/fetch.cc index e89b512eb9..157bc0d07d 100644 --- a/cvmfs/fetch.cc +++ b/cvmfs/fetch.cc @@ -157,7 +157,7 @@ int Fetcher::Fetch( tls->download_job.SetUrl(&url); tls->download_job.SetSink(&sink); tls->download_job.SetExpectedHash(&object.id); - tls->download_job.SetExtraInfo(&object.label.path); + tls->download_job.SetPathInfo(&object.label.path); ClientCtx *ctx = ClientCtx::GetInstance(); if (ctx->IsSet()) { ctx->Get(tls->download_job.GetUidPtr(), diff --git a/cvmfs/loader.cc b/cvmfs/loader.cc index 962fc959c5..0497d4bb0f 100644 --- a/cvmfs/loader.cc +++ b/cvmfs/loader.cc @@ -1072,11 +1072,15 @@ int FuseMain(int argc, char *argv[]) { } else if ((getuid() == 0) && (geteuid() != 0)) { LogCvmfs(kLogCvmfs, kLogDebug, "Reducing to minimum capabilities"); // Have temporarily dropped credentials, now drop credentials permanently. - // CAP_DAC_READ_SEARCH will be sometimes needed for a future feature; - // for now reserve it all the time for testing. - cap_value_t reservecaps[] = {CAP_DAC_READ_SEARCH}; - retval = ClearPermittedCapabilities( - sizeof(reservecaps)/sizeof(cap_value_t), reservecaps, 0, 0); + + if (cvmfs_exports_->fnNeedsReadEnviron()) { + // Reserve the capabilities to read process environments + cap_value_t reservecaps[] = {CAP_DAC_READ_SEARCH, CAP_SYS_PTRACE}; + retval = ClearPermittedCapabilities( + sizeof(reservecaps)/sizeof(cap_value_t), reservecaps, 0, 0); + } else { + retval = ClearPermittedCapabilities(0, 0, 0, 0); + } if (!retval) { LogCvmfs(kLogCvmfs, kLogStderr, "Failed to reduce to minimum capabilities"); diff --git a/cvmfs/loader.h b/cvmfs/loader.h index 9ff89b1503..37a2bf99c2 100644 --- a/cvmfs/loader.h +++ b/cvmfs/loader.h @@ -223,6 +223,7 @@ struct CvmfsExports { fnSaveState = NULL; fnRestoreState = NULL; fnFreeSavedState = NULL; + fnNeedsReadEnviron = NULL; memset(&cvmfs_operations, 0, sizeof(cvmfs_operations)); } @@ -240,6 +241,7 @@ struct CvmfsExports { bool (*fnRestoreState)(const int fd_progress, const StateList &saved_states); void (*fnFreeSavedState)(const int fd_progress, const StateList &saved_states); + bool (*fnNeedsReadEnviron)(); struct fuse_lowlevel_ops cvmfs_operations; }; diff --git a/cvmfs/monitor.cc b/cvmfs/monitor.cc index 7884ba0296..ea414f7628 100644 --- a/cvmfs/monitor.cc +++ b/cvmfs/monitor.cc @@ -68,10 +68,10 @@ int Watchdog::g_crash_signals[] = { SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGSEGV, SIGBUS, SIGPIPE, SIGXFSZ }; -Watchdog *Watchdog::Create(FnOnExit on_exit) { +Watchdog *Watchdog::Create(FnOnExit on_exit, bool needs_read_environ) { assert(instance_ == NULL); instance_ = new Watchdog(on_exit); - instance_->Fork(); + instance_->Fork(needs_read_environ); return instance_; } @@ -386,7 +386,7 @@ Watchdog::SigactionMap Watchdog::SetSignalHandlers( /** * Fork the watchdog process and put it on hold until Spawn() is called. */ -void Watchdog::Fork() { +void Watchdog::Fork(bool needs_read_environ) { Pipe pipe_pid; pipe_watchdog_ = new Pipe(); pipe_listener_ = new Pipe(); @@ -406,14 +406,22 @@ void Watchdog::Fork() { if (on_exit_) { // Reduce to minimum capabilities, which unfortunately is // still quite powerful. - // CAP_SYS_ADMIN is needed to unmount, and CAP_DAC_READ_SEARCH - // is needed because the main process might have it and ptrace - // is not allowed on a process with more capabilities. - cap_value_t reservecaps[] = {CAP_SYS_ADMIN, CAP_DAC_READ_SEARCH}; - cap_value_t inheritablecaps[] = {CAP_DAC_READ_SEARCH}; - assert(ClearPermittedCapabilities( - sizeof(reservecaps)/sizeof(cap_value_t), reservecaps, - sizeof(inheritablecaps)/sizeof(cap_value_t), inheritablecaps)); + // CAP_SYS_ADMIN is needed to unmount, and CAP_SYS_PTRACE + // is needed when needs_read_environ is true because then + // the main Fuse process has elevated capabilities and + // ptrace (needed for collecting a stack trace) is not + // allowed on a process with more capabilities. + if (needs_read_environ) { + cap_value_t reservecaps[] = {CAP_SYS_ADMIN, CAP_SYS_PTRACE}; + cap_value_t inheritcaps[] = {CAP_SYS_PTRACE}; + assert(ClearPermittedCapabilities( + sizeof(reservecaps)/sizeof(cap_value_t), reservecaps, + sizeof(inheritcaps)/sizeof(cap_value_t), inheritcaps)); + } else { + cap_value_t reservecaps[] = {CAP_SYS_ADMIN}; + assert(ClearPermittedCapabilities( + sizeof(reservecaps)/sizeof(cap_value_t), reservecaps, 0, 0)); + } } else { // Only need to be able to do the stack trace, and the // main process needs no extra capabilities, so we can diff --git a/cvmfs/monitor.h b/cvmfs/monitor.h index 00d110d6c9..20a2103589 100644 --- a/cvmfs/monitor.h +++ b/cvmfs/monitor.h @@ -37,7 +37,7 @@ class Watchdog : SingleCopy { */ typedef void (*FnOnExit)(const bool crashed); - static Watchdog *Create(FnOnExit on_exit); + static Watchdog *Create(FnOnExit on_exit, bool needs_read_environ); static pid_t GetPid(); ~Watchdog(); void Spawn(const std::string &crash_dump_path); @@ -92,7 +92,7 @@ class Watchdog : SingleCopy { static void SendTrace(int sig, siginfo_t *siginfo, void *context); explicit Watchdog(FnOnExit on_exit); - void Fork(); + void Fork(bool needs_read_environ); bool WaitForSupervisee(); SigactionMap SetSignalHandlers(const SigactionMap &signal_handlers); void Supervise(); diff --git a/cvmfs/mountpoint.cc b/cvmfs/mountpoint.cc index ce936a54ce..73066355ee 100644 --- a/cvmfs/mountpoint.cc +++ b/cvmfs/mountpoint.cc @@ -2244,13 +2244,30 @@ void MountPoint::SetupHttpTuning() { { download_mgr_->EnableRedirects(); } - if (options_mgr_->GetValue("CVMFS_SEND_INFO_HEADER", &optarg) && + if (options_mgr_->GetValue("CVMFS_INFO_HEADER", &optarg) && (optarg != "")) + { + download_mgr_->EnableInfoHeader(); + download_mgr_->SetInfoHeaderTemplate(optarg); + } else if (options_mgr_->GetValue("CVMFS_SEND_INFO_HEADER", &optarg) && options_mgr_->IsOn(optarg)) { download_mgr_->EnableInfoHeader(); + download_mgr_->SetInfoHeaderTemplate("%{path}"); } } +/* + * Check whether permission is needed to read from user process environment. + */ + +bool MountPoint::needs_read_environ(OptionsManager *omgr) { + // This is a class (static) method because it is used early, before + // all the above initialization is done, so can't rely on mountpoint + // object data. + string info_header; + omgr->GetValue("CVMFS_INFO_HEADER", &info_header); + return (info_header.find("%{env:") != std::string::npos); +} void MountPoint::SetupInodeAnnotation() { string optarg; diff --git a/cvmfs/mountpoint.h b/cvmfs/mountpoint.h index e889f9c283..c1ee99c515 100644 --- a/cvmfs/mountpoint.h +++ b/cvmfs/mountpoint.h @@ -22,6 +22,7 @@ #include "gtest/gtest_prod.h" #include "loader.h" #include "magic_xattr.h" +#include "network/download.h" #include "util/algorithm.h" #include "util/pointer.h" @@ -39,9 +40,6 @@ namespace cvmfs { class Fetcher; class Uuid; } -namespace download { -class DownloadManager; -} namespace glue { class InodeTracker; class DentryTracker; @@ -493,6 +491,8 @@ class MountPoint : SingleCopy, public BootFactory { OptionsManager *options_mgr = NULL); ~MountPoint(); + static bool needs_read_environ(OptionsManager *omgr); + unsigned GetMaxTtlMn(); unsigned GetEffectiveTtlSec(); void SetMaxTtlMn(unsigned value_minutes); diff --git a/cvmfs/network/download.cc b/cvmfs/network/download.cc index fb6143e38c..b34176ebc9 100644 --- a/cvmfs/network/download.cc +++ b/cvmfs/network/download.cc @@ -397,29 +397,6 @@ const int DownloadManager::kProbeUnprobed = -1; const int DownloadManager::kProbeDown = -2; const int DownloadManager::kProbeGeo = -3; -bool DownloadManager::EscapeUrlChar(unsigned char input, char output[3]) { - if (((input >= '0') && (input <= '9')) || - ((input >= 'A') && (input <= 'Z')) || - ((input >= 'a') && (input <= 'z')) || - (input == '/') || (input == ':') || (input == '.') || - (input == '@') || - (input == '+') || (input == '-') || - (input == '_') || (input == '~') || - (input == '[') || (input == ']') || (input == ',')) - { - output[0] = static_cast(input); - return false; - } - - output[0] = '%'; - output[1] = static_cast( - (input / 16) + ((input / 16 <= 9) ? '0' : 'A'-10)); - output[2] = static_cast( - (input % 16) + ((input % 16 <= 9) ? '0' : 'A'-10)); - return true; -} - - /** * Escape special chars from the URL, except for ':' and '/', * which should keep their meaning. @@ -430,7 +407,7 @@ string DownloadManager::EscapeUrl(const int64_t jobinfo_id, const string &url) { char escaped_char[3]; for (unsigned i = 0, s = url.length(); i < s; ++i) { - if (EscapeUrlChar(url[i], escaped_char)) { + if (JobInfo::EscapeUrlChar(url[i], escaped_char)) { escaped.append(escaped_char, 3); } else { escaped.push_back(escaped_char[0]); @@ -442,39 +419,6 @@ string DownloadManager::EscapeUrl(const int64_t jobinfo_id, const string &url) { return escaped; } -/** - * escaped array needs to be sufficiently large. Its size is calculated by - * passing NULL to EscapeHeader. - */ -unsigned DownloadManager::EscapeHeader(const string &header, - char *escaped_buf, - size_t buf_size) -{ - unsigned esc_pos = 0; - char escaped_char[3]; - for (unsigned i = 0, s = header.size(); i < s; ++i) { - if (EscapeUrlChar(header[i], escaped_char)) { - for (unsigned j = 0; j < 3; ++j) { - if (escaped_buf) { - if (esc_pos >= buf_size) - return esc_pos; - escaped_buf[esc_pos] = escaped_char[j]; - } - esc_pos++; - } - } else { - if (escaped_buf) { - if (esc_pos >= buf_size) - return esc_pos; - escaped_buf[esc_pos] = escaped_char[0]; - } - esc_pos++; - } - } - - return esc_pos; -} - /** * -1 of digits is not a valid Http return code */ @@ -2019,16 +1963,18 @@ Failures DownloadManager::Fetch(JobInfo *info) { // Prepare cvmfs-info: header, allocate string on the stack info->SetInfoHeader(NULL); - if (enable_info_header_ && info->extra_info()) { - const char *header_name = "cvmfs-info: "; - const size_t header_name_len = strlen(header_name); - const unsigned header_size = 1 + header_name_len + - EscapeHeader(*(info->extra_info()), NULL, 0); - info->SetInfoHeader(static_cast(alloca(header_size))); - memcpy(info->info_header(), header_name, header_name_len); - EscapeHeader(*(info->extra_info()), info->info_header() + header_name_len, - header_size - header_name_len); - info->info_header()[header_size-1] = '\0'; + if (enable_info_header_) { + const string header_info = info->info_header_contents(info_header_template_); + if (header_info != "") { + const char * const header_name = "cvmfs-info: "; + const size_t header_name_len = strlen(header_name); + const size_t info_len = header_info.length(); + char *buf = static_cast(alloca(header_name_len + info_len + 1)); + memcpy(buf, header_name, header_name_len); + memcpy(buf + header_name_len, header_info.c_str(), info_len); + buf[header_name_len + info_len] = '\0'; + info->SetInfoHeader(buf); + } } if (enable_http_tracing_) { diff --git a/cvmfs/network/download.h b/cvmfs/network/download.h index 62a5df2ce8..79ebe9c44e 100644 --- a/cvmfs/network/download.h +++ b/cvmfs/network/download.h @@ -246,6 +246,9 @@ class DownloadManager { // NOLINT(clang-analyzer-optin.performance.Padding) bool SetShardingPolicy(const ShardingPolicySelector type); void SetFailoverIndefinitely(); void SetFqrn(const std::string &fqrn) { fqrn_ = fqrn; } + void SetInfoHeaderTemplate(const std::string templ) { + info_header_template_ = templ; + } unsigned num_hosts() { if (opt_host_.chain) return opt_host_.chain->size(); @@ -295,10 +298,7 @@ class DownloadManager { // NOLINT(clang-analyzer-optin.performance.Padding) void CheckHostInfoReset(const std::string &typ, HostInfo &info, JobInfo *jobinfo, time_t &now); - bool EscapeUrlChar(unsigned char input, char output[3]); std::string EscapeUrl(const int64_t jobinfo_id, const std::string &url); - unsigned EscapeHeader(const std::string &header, char *escaped_buf, - size_t buf_size); inline std::vector *current_proxy_group() const { return (opt_proxy_groups_ ? @@ -327,6 +327,7 @@ class DownloadManager { // NOLINT(clang-analyzer-optin.performance.Padding) pthread_mutex_t *lock_options_; pthread_mutex_t *lock_synchronous_mode_; std::string opt_dns_server_; + std::string info_header_template_; unsigned opt_timeout_proxy_; unsigned opt_timeout_direct_; unsigned opt_low_speed_limit_; diff --git a/cvmfs/network/jobinfo.cc b/cvmfs/network/jobinfo.cc index 46d84cec2a..4ce488f259 100644 --- a/cvmfs/network/jobinfo.cc +++ b/cvmfs/network/jobinfo.cc @@ -2,7 +2,15 @@ * This file is part of the CernVM File System. */ +#include +#include +#include +#include +#include + +#include "capabilities.h" #include "jobinfo.h" +#include "util/logging.h" #include "util/string.h" namespace download { @@ -52,7 +60,7 @@ void JobInfo::Init() { interrupt_cue_ = NULL; sink_ = NULL; expected_hash_ = NULL; - extra_info_ = NULL; + path_info_ = NULL; // range_offset_ = -1; range_size_ = -1; @@ -80,4 +88,211 @@ void JobInfo::Init() { memset(&zstream_, 0, sizeof(zstream_)); } + +/* + * Return true if input character is escaped to 3 output characters, + * otherwise return false and leave the input character in the first + * output character. + */ +bool JobInfo::EscapeUrlChar(unsigned char input, + char output[3]) { + if (((input >= '0') && (input <= '9')) || + ((input >= 'A') && (input <= 'Z')) || + ((input >= 'a') && (input <= 'z')) || + (input == '/') || (input == ':') || (input == '.') || + (input == '@') || + (input == '+') || (input == '-') || + (input == '_') || (input == '~') || + (input == '[') || (input == ']') || (input == ',')) + { + output[0] = static_cast(input); + return false; + } + + output[0] = '%'; + output[1] = static_cast( + (input / 16) + ((input / 16 <= 9) ? '0' : 'A'-10)); + output[2] = static_cast( + (input % 16) + ((input % 16 <= 9) ? '0' : 'A'-10)); + return true; +} + + +static std::string EscapeHeader(const std::string &header) +{ + std::string escaped = ""; + char escaped_char[3]; + for (char c : header) { + if (JobInfo::EscapeUrlChar(c, escaped_char)) { + for (unsigned j = 0; j < 3; ++j) { + escaped += escaped_char[j]; + } + } else { + escaped += escaped_char[0]; + } + } + + return escaped; +} + + +/* + * Return the filled-in template of CVMFS_INFO_HEADER + */ + +const std::string JobInfo::info_header_contents(const std::string templ) { + std::string ans = ""; + std::string key; + int parsemode = 0; // 0 default, 1 after '%', 2 after '%{' + int envsize = -1; + char *envbuf = NULL; + + for (char c : templ) { + switch (parsemode) { + case 0: + if (c == '%') { + parsemode = 1; + key = ""; + } else { + ans += c; + } + break; + case 1: + if (c == '{') { + parsemode = 2; + } else { + ans += '%'; + ans += c; + } + break; + case 2: + if (c != '}') { + key += c; + } else { + parsemode = 0; + if (key == "path") { + if (path_info_ != NULL) { + ans += EscapeHeader(*path_info_); + } else { + ans += "-"; + } + } else if (key == "pid") { + if (pid_ != (pid_t) -1) { + ans += StringifyInt(pid_); + } else { + ans += "-"; + } + } else if (key == "uid") { + if (uid_ != (uid_t) -1) { + ans += StringifyInt(uid_); + } else { + ans += "-"; + } + } else if (key == "gid") { + if (gid_ != (gid_t) -1) { + ans += StringifyInt(gid_); + } else { + ans += "-"; + } + } else if (key.substr(0, 4) == "env:") { + if (envsize == -1) { + envsize = 0; + if (pid_ != (pid_t) -1) { + ObtainDacReadSearchCapability(); + ObtainSysPtraceCapability(); + std::string fname = "/proc/" + StringifyInt(pid_) + "/environ"; + int fd = open(fname.c_str(), O_RDONLY); + if (fd != -1) { + // Unfortunately fstat does not show the size so need to + // read it to find out the size + ssize_t n; + int size = 0; + char buf[BUFSIZ]; + while ((n = read(fd, buf, BUFSIZ)) > 0) { + size += (int) n; + } + if (n >= 0) { + if (lseek(fd, 0, SEEK_SET) >= 0) { + envbuf = (char *) malloc(size); + if (envbuf != NULL) { + if (read(fd, envbuf, size) > 0) { + envsize = size; + LogCvmfs(kLogDownload, kLogDebug, + "(job id %" PRId64 ") read %d bytes from %s", + id_, size, fname.c_str()); + } else { + free(envbuf); + envbuf = NULL; + } + } + } + } + close(fd); + } else { + LogCvmfs(kLogDownload, kLogDebug, + "(job id %" PRId64 ") unable to open %s: %s", + id_, fname.c_str(), strerror(errno)); + } + DropSysPtraceCapability(); + DropDacReadSearchCapability(); + } + } + if (envbuf != NULL) { + const char *var = key.substr(4).c_str(); // everything after "env:" + const char *varp = var; + std::string val = ""; + int matchmode = 1; + const char * const endp = envbuf + envsize; + for (const char *p = envbuf; p < endp; p++) { + switch(matchmode) { + case 0: + // skipping to next null character + if (*p == '\0') { + varp = var; + matchmode = 1; + } + break; + case 1: + // matching variable name + if (*p == '\0') { + // premature end without an '=' + varp = var; + } else if (*varp == *p) { + // so far so good + ++varp; + } else if ((*varp == '\0') && (*p == '=')) { + // matched + matchmode = 2; + } else { + // didn't match + matchmode = 0; + } + break; + case 2: + // matched, collecting value + if (*p == '\0') { + // all done + p = endp; + break; + } + val += *p; + break; + } + } + if (val != "") { + ans += ' '; + ans += EscapeHeader(val); + } + } + } + } + } + } + + if (envbuf != NULL) + free(envbuf); + + return ans; +} + } // namespace download diff --git a/cvmfs/network/jobinfo.h b/cvmfs/network/jobinfo.h index 24d3bed6e5..53d5253793 100644 --- a/cvmfs/network/jobinfo.h +++ b/cvmfs/network/jobinfo.h @@ -5,6 +5,7 @@ #ifndef CVMFS_NETWORK_JOBINFO_H_ #define CVMFS_NETWORK_JOBINFO_H_ +#include #include #include #include @@ -83,7 +84,7 @@ class JobInfo { InterruptCue *interrupt_cue_; cvmfs::Sink *sink_; const shash::Any *expected_hash_; - const std::string *extra_info_; + const std::string *path_info_; // Allow byte ranges to be specified. off_t range_offset_; @@ -135,6 +136,8 @@ class JobInfo { data_tube_.Destroy(); } + static bool EscapeUrlChar(unsigned char input, char output[3]); + void CreatePipeJobResults() { pipe_job_results = new Pipe(); } @@ -185,13 +188,14 @@ class JobInfo { InterruptCue *interrupt_cue() const { return interrupt_cue_; } cvmfs::Sink *sink() const { return sink_; } const shash::Any *expected_hash() const { return expected_hash_; } - const std::string *extra_info() const { return extra_info_; } + const std::string *path_info() const { return path_info_; } off_t range_offset() const { return range_offset_; } off_t range_size() const { return range_size_; } CURL *curl_handle() const { return curl_handle_; } curl_slist *headers() const { return headers_; } + const std::string info_header_contents(const std::string templ); char *info_header() const { return info_header_; } char *tracing_header_pid() const { return tracing_header_pid_; } char *tracing_header_gid() const { return tracing_header_gid_; } @@ -223,9 +227,6 @@ class JobInfo { void SetFollowRedirects(bool follow_redirects) { follow_redirects_ = follow_redirects; } void SetForceNocache(bool force_nocache) { force_nocache_ = force_nocache; } - void SetPid(pid_t pid) { pid_ = pid; } - void SetUid(uid_t uid) { uid_ = uid; } - void SetGid(gid_t gid) { gid_ = gid; } void SetCredData(void *cred_data) { cred_data_ = cred_data; } void SetInterruptCue(InterruptCue *interrupt_cue) { interrupt_cue_ = interrupt_cue; } @@ -233,8 +234,7 @@ class JobInfo { { sink_ = sink; } void SetExpectedHash(const shash::Any *expected_hash) { expected_hash_ = expected_hash; } - void SetExtraInfo(const std::string *extra_info) - { extra_info_ = extra_info; } + void SetPathInfo(const std::string *path_info) { path_info_ = path_info; } void SetRangeOffset(off_t range_offset) { range_offset_ = range_offset; } void SetRangeSize(off_t range_size) { range_size_ = range_size; } diff --git a/cvmfs/quota_posix.cc b/cvmfs/quota_posix.cc index d9e16b8d99..e5a002b174 100644 --- a/cvmfs/quota_posix.cc +++ b/cvmfs/quota_posix.cc @@ -1090,7 +1090,7 @@ int PosixQuotaManager::MainCacheManager(int argc, char **argv) { if (!foreground) Daemonize(); - UniquePtr watchdog(Watchdog::Create(NULL)); + UniquePtr watchdog(Watchdog::Create(NULL, false)); assert(watchdog.IsValid()); watchdog->Spawn("./stacktrace.cachemgr"); diff --git a/cvmfs/swissknife_sync.cc b/cvmfs/swissknife_sync.cc index 7c12d4e615..92367134a5 100644 --- a/cvmfs/swissknife_sync.cc +++ b/cvmfs/swissknife_sync.cc @@ -589,7 +589,7 @@ int swissknife::CommandSync::Main(const swissknife::ArgumentList &args) { watchdog_dir.c_str(), timestamp.c_str(), getpid()); assert(path_size > 0); assert(path_size < PATH_MAX); - UniquePtr watchdog(Watchdog::Create(NULL)); + UniquePtr watchdog(Watchdog::Create(NULL, false)); watchdog->Spawn(std::string(watchdog_path)); SyncParameters params;