5 #include "cvmfs_config.h"
11 #include <linux/limits.h>
15 #include <sys/mount.h>
54 uid_t euid = geteuid();
55 uid_t egid = getegid();
70 if (rvi !=
sizeof(pid_t))
73 if (rvi !=
sizeof(pid_t))
95 if (S_ISDIR(info.st_mode))
96 rv = rmdir(path.c_str());
98 rv = unlink(path.c_str());
100 if (rv == 0 || errno == ENOENT)
104 "cannot remove " + path +
" (" +
StringifyInt(errno) +
")");
109 const std::string &path,
const std::vector<std::string> &new_paths)
112 std::vector<std::string> umount_targets;
113 for (
unsigned i = 0; i < all_mounts.size(); ++i) {
114 if (
HasPrefix(all_mounts[i], path +
"/",
false ))
115 umount_targets.push_back(all_mounts[i]);
118 std::sort(umount_targets.begin(), umount_targets.end());
119 std::vector<std::string>::reverse_iterator iter = umount_targets.rbegin();
120 std::vector<std::string>::reverse_iterator iend = umount_targets.rend();
121 for (; iter != iend; ++iter) {
124 if (!rvb && errno != EINVAL && errno != EACCES) {
126 "cannot unmount " + *iter +
" (" +
StringifyInt(errno) +
")");
130 std::vector<std::string> sorted_new_paths(new_paths);
131 std::sort(sorted_new_paths.begin(), sorted_new_paths.end());
132 iter = sorted_new_paths.rbegin();
133 iend = sorted_new_paths.rend();
134 for (; iter != iend; ++iter) {
135 std::string p = *iter;
136 while (p.length() > path.length()) {
142 if (errno != ENOTEMPTY)
156 void CmdEnter::CreateUnderlay(
157 const std::string &source_dir,
158 const std::string &dest_dir,
159 const std::vector<std::string> &empty_dirs,
160 std::vector<std::string> *new_paths)
163 source_dir.c_str(), dest_dir.c_str());
166 std::vector<std::string> empty_toplevel_dirs;
167 for (
unsigned i = 0; i < empty_dirs.size(); ++i) {
168 std::string toplevel_dir = empty_dirs[i];
171 empty_toplevel_dirs.push_back(toplevel_dir);
174 std::string dest_empty_dir = dest_dir + toplevel_dir;
176 dest_empty_dir.c_str());
178 new_paths->push_back(dest_empty_dir);
181 std::vector<std::string> names;
182 std::vector<mode_t> modes;
184 std::string d = source_dir.empty() ?
"/" : source_dir;
188 throw EPublish(
"cannot list directory " + d);
193 for (
unsigned i = 0; i < names.size(); ++i) {
194 std::string source = source_dir +
"/" + names[i] +
"/";
195 std::string dest = dest_dir +
"/" + names[i] +
"/";
197 iter_swap(names.begin(), names.begin() + i);
198 iter_swap(modes.begin(), modes.begin() + i);
207 for (
unsigned i = 0; i < names.size(); ++i) {
208 if (std::find(empty_toplevel_dirs.begin(), empty_toplevel_dirs.end(),
209 std::string(
"/") + names[i]) != empty_toplevel_dirs.end())
214 std::string source = source_dir +
"/" + names[i];
215 std::string dest = dest_dir +
"/" + names[i];
216 new_paths->push_back(dest);
217 if (S_ISLNK(modes[i])) {
218 char buf[PATH_MAX + 1];
219 ssize_t nchars = readlink(source.c_str(), buf, PATH_MAX);
221 throw EPublish(
"cannot read symlink " + source);
225 if (S_ISDIR(modes[i])) {
231 source.c_str(), dest.c_str());
234 throw EPublish(
"cannot bind mount " + source +
" --> " + dest +
242 for (
unsigned i = 0; i < empty_toplevel_dirs.size(); ++i) {
243 std::string toplevel_dir = empty_toplevel_dirs[i];
244 std::vector<std::string> empty_sub_dir;
245 empty_sub_dir.push_back(empty_dirs[i].substr(toplevel_dir.length()));
246 if (!empty_sub_dir[0].empty()) {
247 CreateUnderlay(source_dir + toplevel_dir,
248 dest_dir + toplevel_dir,
255 void CmdEnter::WriteCvmfsConfig(
const std::string &extra_config) {
259 if (!extra_config.empty())
260 options_manager.
ParsePath(extra_config,
false );
262 options_manager.
SetValue(
"CVMFS_MOUNT_DIR",
263 settings_spool_area_.readonly_mnt());
264 options_manager.
SetValue(
"CVMFS_AUTO_UPDATE",
"no");
265 options_manager.
SetValue(
"CVMFS_NFS_SOURCE",
"no");
266 options_manager.
SetValue(
"CVMFS_HIDE_MAGIC_XATTRS",
"yes");
267 options_manager.
SetValue(
"CVMFS_SERVER_CACHE_MODE",
"yes");
268 options_manager.
SetValue(
"CVMFS_CLAIM_OWNERSHIP",
"yes");
269 options_manager.
SetValue(
"CVMFS_USYSLOG", settings_spool_area_.client_log());
270 options_manager.
SetValue(
"CVMFS_RELOAD_SOCKETS",
271 settings_spool_area_.cache_dir());
272 options_manager.
SetValue(
"CVMFS_WORKSPACE", settings_spool_area_.cache_dir());
273 options_manager.
SetValue(
"CVMFS_CACHE_PRIMARY",
"private");
274 options_manager.
SetValue(
"CVMFS_CACHE_private_TYPE",
"posix");
275 options_manager.
SetValue(
"CVMFS_CACHE_private_BASE",
276 settings_spool_area_.cache_dir());
277 options_manager.
SetValue(
"CVMFS_CACHE_private_SHARED",
"on");
278 options_manager.
SetValue(
"CVMFS_CACHE_private_QUOTA_LIMIT",
"4000");
281 settings_spool_area_.client_config(),
284 throw EPublish(
"cannot write client config to " +
285 settings_spool_area_.client_config());
290 void CmdEnter::MountCvmfs() {
292 throw EPublish(
"cannot find executable " + cvmfs2_binary_,
293 EPublish::kFailMissingDependency);
296 int fd_stdout = open(stdout_path_.c_str(), O_CREAT | O_APPEND | O_WRONLY,
298 int fd_stderr = open(stderr_path_.c_str(), O_CREAT | O_APPEND | O_WRONLY,
301 std::string cvmfs_config = settings_spool_area_.client_config();
303 std::vector<std::string> cmdline;
304 cmdline.push_back(cvmfs2_binary_);
305 cmdline.push_back(
"-o");
306 cmdline.push_back(
"allow_other,config=" + cvmfs_config);
307 cmdline.push_back(fqrn_);
308 cmdline.push_back(settings_spool_area_.readonly_mnt());
309 std::set<int> preserved_fds;
310 preserved_fds.insert(0);
311 preserved_fds.insert(1);
312 preserved_fds.insert(2);
313 std::map<int, int> map_fds;
314 map_fds[fd_stdout] = 1;
315 map_fds[fd_stderr] = 2;
317 bool rvb =
ManagedExec(cmdline, preserved_fds, map_fds,
324 throw EPublish(
"cannot run " + cvmfs2_binary_);
329 if (exit_code != 0) {
330 throw EPublish(
"cannot mount cvmfs read-only branch (" +
332 " command: `" +
JoinStrings(cmdline,
" ").c_str() +
"`");
335 rvb =
BindMount(settings_spool_area_.readonly_mnt(),
336 rootfs_dir_ + settings_spool_area_.readonly_mnt());
338 throw EPublish(
"cannot map CernVM-FS mount point");
342 void CmdEnter::MountOverlayfs() {
344 throw EPublish(
"cannot find executable " + overlayfs_binary_,
345 EPublish::kFailMissingDependency);
348 int fd_stdout = open(stdout_path_.c_str(), O_CREAT | O_APPEND | O_WRONLY,
350 int fd_stderr = open(stderr_path_.c_str(), O_CREAT | O_APPEND | O_WRONLY,
353 std::vector<std::string> cmdline;
354 cmdline.push_back(overlayfs_binary_);
355 cmdline.push_back(
"-o");
356 cmdline.push_back(
string(
"lowerdir=") + settings_spool_area_.readonly_mnt() +
357 ",upperdir=" + settings_spool_area_.scratch_dir() +
358 ",workdir=" + settings_spool_area_.ovl_work_dir());
359 cmdline.push_back(rootfs_dir_ + settings_spool_area_.union_mnt());
360 std::set<int> preserved_fds;
361 preserved_fds.insert(0);
362 preserved_fds.insert(1);
363 preserved_fds.insert(2);
364 std::map<int, int> map_fds;
365 map_fds[fd_stdout] = 1;
366 map_fds[fd_stderr] = 2;
368 bool rvb =
ManagedExec(cmdline, preserved_fds, map_fds,
375 throw EPublish(
"cannot run " + overlayfs_binary_);
380 if (exit_code != 0) {
381 throw EPublish(
"cannot mount overlay file system (" +
383 " command: `" +
JoinStrings(cmdline,
" ").c_str() +
"`");
388 std::string CmdEnter::GetCvmfsXattr(
const std::string &name) {
391 std::string(
"user.") + name, &xattr);
393 throw EPublish(
"cannot get extrended attribute " + name +
" from " +
394 settings_spool_area_.readonly_mnt());
400 void CmdEnter::CleanupSession(
401 bool keep_logs,
const std::vector<std::string> &new_paths)
404 "Cleaning out session directory... ");
406 std::string pid_xattr;
408 "user.pid", &pid_xattr);
410 throw EPublish(
"cannot find CernVM-FS process");
413 const std::string union_mnt = rootfs_dir_ + settings_spool_area_.union_mnt();
416 throw EPublish(
"cannot unmount overlayfs on " + union_mnt);
418 (rootfs_dir_ + settings_spool_area_.readonly_mnt()).c_str());
420 throw EPublish(
"cannot unmount mapped CernVM-FS on " +
421 rootfs_dir_ + settings_spool_area_.readonly_mnt());
425 throw EPublish(
"cannot unmount CernVM-FS on " +
426 settings_spool_area_.readonly_mnt());
437 rvb =
RemoveTree(settings_spool_area_.ovl_work_dir());
439 throw EPublish(
"cannot remove " + settings_spool_area_.ovl_work_dir());
440 rvb =
RemoveTree(settings_spool_area_.scratch_base());
442 throw EPublish(
"cannot remove " + settings_spool_area_.scratch_base());
443 rvb =
RemoveTree(settings_spool_area_.cache_dir());
445 throw EPublish(
"cannot remove " + settings_spool_area_.cache_dir());
447 rvb =
RemoveTree(settings_spool_area_.tmp_dir());
449 throw EPublish(
"cannot remove " + settings_spool_area_.tmp_dir());
453 settings_spool_area_.log_dir().c_str());
457 rvb =
RemoveTree(settings_spool_area_.log_dir());
459 RemoveSingle(session_dir_ +
"/" + fqrn_ +
"/server.conf");
472 if (!sanitizer.
IsValid(fqrn_)) {
473 throw EPublish(
"malformed repository name: " + fqrn_);
481 Env::DropCapabilities();
483 if (options.
Has(
"cvmfs2")) {
484 cvmfs2_binary_ = options.
GetString(
"cvmfs2");
487 setenv(
"CVMFS_LIBRARY_PATH",
GetParentPath(cvmfs2_binary_).c_str(), 0);
494 if (session_dir_.empty())
495 throw EPublish(
"cannot create session directory in " + workspace);
496 settings_spool_area_.SetUnionMount(std::string(
"/cvmfs/") + fqrn_);
497 settings_spool_area_.SetSpoolArea(session_dir_);
498 settings_spool_area_.EnsureDirectories();
499 rootfs_dir_ = session_dir_ +
"/rootfs";
501 stdout_path_ = settings_spool_area_.log_dir() +
"/stdout.log";
502 stderr_path_ = settings_spool_area_.log_dir() +
"/stderr.log";
506 uid_t uid = geteuid();
507 gid_t gid = getegid();
510 "*** NOTE: This is currently an experimental CernVM-FS feature\n");
512 "Entering ephemeral writable shell for %s... ",
513 settings_spool_area_.union_mnt().c_str());
517 std::vector<std::string> empty_dirs;
518 empty_dirs.push_back(settings_spool_area_.union_mnt());
519 empty_dirs.push_back(
"/proc");
520 std::vector<std::string> new_paths;
521 CreateUnderlay(
"", rootfs_dir_, empty_dirs, &new_paths);
524 rvb =
SymlinkForced(session_dir_, rootfs_dir_ +
"/.cvmfsenter");
526 throw EPublish(
"cannot create marker file " + rootfs_dir_ +
"/.cvmfsenter");
527 new_paths.push_back(rootfs_dir_ +
"/.cvmfsenter");
530 throw EPublish(
"cannot mount " + rootfs_dir_ +
"/proc");
531 setenv(
"CVMFS_ENTER_SESSION_DIR", session_dir_.c_str(), 1 );
534 "Mounting CernVM-FS read-only layer... ");
550 env_conf_ = session_dir_ +
"/env.conf";
554 throw EPublish(
"cannot create session environment file");
557 "Mounting union file system... ");
564 throw EPublish(
"cannot create subshell");
567 if (!options.
Has(
"root")) {
578 "Switching to %s... ", rootfs_dir_.c_str());
579 int rvi = chroot(rootfs_dir_.c_str());
581 throw EPublish(
"cannot chroot to " + rootfs_dir_);
587 if (options.
Has(
"transaction")) {
589 "Starting a transaction inside the enter shell");
591 if (options.
Has(
"repo-config")) {
592 repo_config_ = options.
GetString(
"repo-config");
594 "Parsing the external configuration for the repository at %s",
595 repo_config_.c_str());
598 std::string config_file = repo_config_ +
"/" + fqrn_ +
"/server.conf";
599 std::string folderpath = session_dir_ +
"/" + fqrn_;
600 MkdirDeep(folderpath.c_str(), 0700,
true );
602 std::string session_config_file = folderpath +
"/server.conf";
603 int fd_config = open(config_file.c_str(), O_RDONLY);
612 publisher =
new Publisher(*settings_publisher);
613 publisher->Transaction();
617 rvi = chdir(cwd.c_str());
624 "You can attach to this shell from another another terminal with\n");
625 if (options.
Has(
"root")) {
627 " nsenter --preserve-credentials -U -p -m -t %u\n"
629 anchor_pid.init_pid, rootfs_dir_.c_str());
632 " nsenter --preserve-credentials -U -m -t %u\n"
634 " nsenter --preserve-credentials -S %u -G %u -U -p -t 1\n",
635 anchor_pid.parent_pid, rootfs_dir_.c_str(), uid, gid);
638 std::vector<std::string> cmdline;
640 for (
unsigned i = 1; i < options.
plain_args().size(); ++i) {
641 cmdline.push_back(options.
plain_args()[i].value_str);
645 std::set<int> preserved_fds;
646 preserved_fds.insert(0);
647 preserved_fds.insert(1);
648 preserved_fds.insert(2);
650 rvb =
ManagedExec(cmdline, preserved_fds, std::map<int, int>(),
657 std::vector<int> sigs;
658 sigs.push_back(SIGUSR1);
662 if (options.
Has(
"transaction") &&
663 !
FileExists(session_dir_ +
"/shellaction.marker")) {
665 publisher->session()->SetKeepAlive(
false);
674 if (!options.
Has(
"keep-session"))
675 CleanupSession(options.
Has(
"keep-logs"), new_paths);
std::string GetStringDefault(const std::string &key, const std::string &default_value) const
bool SymlinkForced(const std::string &src, const std::string &dest)
static void RemoveSingle(const std::string &path)
SettingsPublisher * CreateSettingsPublisher(const std::string &ident, bool needs_managed=false)
bool CreateMountNamespace()
static void EnsureDirectory(const std::string &path)
bool Has(const std::string &key) const
void CreateFile(const std::string &path, const int mode, const bool ignore_failure)
std::string GetString(const std::string &key) const
string JoinStrings(const vector< string > &strings, const string &joint)
bool ManagedExec(const std::vector< std::string > &command_line, const std::set< int > &preserve_fildes, const std::map< int, int > &map_fildes, const bool drop_credentials, const bool clear_env, const bool double_fork, pid_t *child_pid)
assert((mem||(size==0))&&"Out Of Memory")
static void RemoveUnderlay(const std::string &path, const std::vector< std::string > &new_paths)
bool SafeWriteToFile(const std::string &content, const std::string &path, int mode)
void SetConfigPath(const std::string &config_path)
std::string FindExecutable(const std::string &exe)
const std::vector< Argument > & plain_args() const
int WaitForChild(pid_t pid, const std::vector< int > &sig_ok)
void ParseDefault(const std::string &fqrn)
bool FileExists(const std::string &path)
void SetValue(const std::string &key, const std::string &value)
const int kPrivateFileMode
static AnchorPid EnterRootContainer()
ssize_t SafeRead(int fd, void *buf, size_t nbyte)
NamespaceFailures CreateUserNamespace(uid_t map_uid_to, gid_t map_gid_to)
bool ProcMount(const std::string &to)
bool MkdirDeep(const std::string &path, const mode_t mode, bool verify_writable)
std::string GetHomeDirectory()
string StringifyInt(const int64_t value)
std::string CreateTempDir(const std::string &path_prefix)
bool HasPrefix(const string &str, const string &prefix, const bool ignore_case)
bool CreatePidNamespace(int *fd_parent)
bool DirectoryExists(const std::string &path)
bool RemoveTree(const std::string &path)
bool SafeReadToString(int fd, std::string *final_result)
void ParsePath(const std::string &config_file, const bool external)
uint64_t String2Uint64(const string &value)
PathString GetParentPath(const PathString &path)
bool BindMount(const std::string &from, const std::string &to)
bool ListDirectory(const std::string &directory, std::vector< std::string > *names, std::vector< mode_t > *modes)
Publisher(const SettingsPublisher &settings, const bool exists=true)
bool ProcessExists(pid_t pid)
std::string GetCurrentWorkingDirectory()
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)