CernVM-FS  2.13.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
cmd_enter.cc
Go to the documentation of this file.
1 
6 #include "cmd_enter.h"
7 
8 #include <dirent.h>
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <linux/limits.h>
12 #include <poll.h>
13 #include <sched.h>
14 #include <signal.h>
15 #include <sys/mount.h>
16 #include <sys/wait.h>
17 #include <unistd.h>
18 
19 #include <algorithm>
20 #include <cassert>
21 #include <cstdio>
22 #include <map>
23 #include <set>
24 #include <string>
25 #include <vector>
26 
27 #include "backoff.h"
28 #include "options.h"
29 #include "publish/except.h"
30 #include "publish/repository.h"
31 #include "publish/settings.h"
32 #include "sanitizer.h"
33 #include "util/logging.h"
34 #include "util/namespace.h"
35 #include "util/platform.h"
36 #include "util/posix.h"
37 #include "util/string.h"
38 
39 using namespace std; // NOLINT
40 
41 namespace {
42 
46 struct AnchorPid {
47  pid_t parent_pid;
48  pid_t init_pid;
49 };
50 
52  const uid_t euid = geteuid();
53  const uid_t egid = getegid();
54  const NamespaceFailures failure = CreateUserNamespace(0, 0);
55  if (failure != kFailNsOk) {
56  throw publish::EPublish(
57  "cannot create root user namespace (" + StringifyInt(failure) + " / "
58  + StringifyInt(errno) + ") [euid=" + StringifyInt(euid)
59  + ", egid=" + StringifyInt(egid) + "]");
60  }
61 
62  bool rvb = CreateMountNamespace();
63  if (!rvb)
64  throw publish::EPublish("cannot create mount namespace");
65  int fd;
66  rvb = CreatePidNamespace(&fd);
67  if (!rvb)
68  throw publish::EPublish("cannot create pid namespace");
69  AnchorPid anchor_pid;
70  int rvi = SafeRead(fd, &anchor_pid.parent_pid, sizeof(pid_t));
71  if (rvi != sizeof(pid_t))
72  throw publish::EPublish("cannot initialize pid namespace");
73  rvi = SafeRead(fd, &anchor_pid.init_pid, sizeof(pid_t));
74  if (rvi != sizeof(pid_t))
75  throw publish::EPublish("cannot initialize pid namespace");
76  close(fd);
77  return anchor_pid;
78 }
79 
80 static void EnsureDirectory(const std::string &path) {
81  const bool rv = MkdirDeep(path, 0700, true /* veryfy_writable */);
82  if (!rv)
83  throw publish::EPublish("cannot create directory " + path);
84 }
85 
86 static void RemoveSingle(const std::string &path) {
87  platform_stat64 info;
88  const int retval = platform_lstat(path.c_str(), &info);
89  if (retval != 0) {
90  if (errno == ENOENT)
91  return;
92  throw publish::EPublish("cannot remove " + path);
93  }
94 
95  int rv = 0;
96  if (S_ISDIR(info.st_mode))
97  rv = rmdir(path.c_str());
98  else
99  rv = unlink(path.c_str());
100 
101  if (rv == 0 || errno == ENOENT)
102  return;
103 
104  throw publish::EPublish("cannot remove " + path + " (" + StringifyInt(errno)
105  + ")");
106 }
107 
108 
109 static void RemoveUnderlay(const std::string &path,
110  const std::vector<std::string> &new_paths) {
111  std::vector<std::string> all_mounts = platform_mountlist();
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 /* ignore_case */))
115  umount_targets.push_back(all_mounts[i]);
116  }
117 
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) {
122  const bool rvb = platform_umount_lazy(iter->c_str());
123  // Some sub mounts, e.g. /sys/kernel/tracing, cannot be unmounted
124  if (!rvb && errno != EINVAL && errno != EACCES) {
125  throw publish::EPublish("cannot unmount " + *iter + " ("
126  + StringifyInt(errno) + ")");
127  }
128  }
129 
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()) {
137  assert(HasPrefix(p, path, false /*ignore_case*/));
138  // The parent path might not be empty until all children are visited
139  try {
140  RemoveSingle(p);
141  } catch (const publish::EPublish &e) {
142  if (errno != ENOTEMPTY)
143  throw;
144  }
145  p = GetParentPath(p);
146  }
147  }
148  RemoveSingle(path);
149 }
150 
151 } // anonymous namespace
152 
153 
154 namespace publish {
155 
156 void CmdEnter::CreateUnderlay(const std::string &source_dir,
157  const std::string &dest_dir,
158  const std::vector<std::string> &empty_dirs,
159  std::vector<std::string> *new_paths) {
160  LogCvmfs(kLogCvmfs, kLogDebug, "underlay: entry %s --> %s",
161  source_dir.c_str(), dest_dir.c_str());
162 
163  // For an empty directory /cvmfs/atlas.cern.ch, we are going to store "/cvmfs"
164  std::vector<std::string> empty_toplevel_dirs;
165  for (unsigned i = 0; i < empty_dirs.size(); ++i) {
166  std::string toplevel_dir = empty_dirs[i];
167  while (!GetParentPath(toplevel_dir).empty())
168  toplevel_dir = GetParentPath(toplevel_dir);
169  empty_toplevel_dirs.push_back(toplevel_dir);
170 
171  // We create $DEST/cvmfs (top-level dir)
172  const std::string dest_empty_dir = dest_dir + toplevel_dir;
173  LogCvmfs(kLogCvmfs, kLogDebug, "underlay: mkdir %s",
174  dest_empty_dir.c_str());
175  EnsureDirectory(dest_empty_dir);
176  new_paths->push_back(dest_empty_dir);
177  }
178 
179  std::vector<std::string> names;
180  std::vector<mode_t> modes;
181  // In a recursive call, the source directory might not exist, which is fine
182  const std::string d = source_dir.empty() ? "/" : source_dir;
183  if (DirectoryExists(d)) {
184  const bool rv = ListDirectory(d, &names, &modes);
185  if (!rv)
186  throw EPublish("cannot list directory " + d);
187  }
188  // We need to order creating the bindmounts such that the destination itself
189  // is handled first; otherwise, the recursive bind mount to the session dir
190  // creates all other sub bind mounts again.
191  for (unsigned i = 0; i < names.size(); ++i) {
192  const std::string source = source_dir + "/" + names[i] + "/";
193  const std::string dest = dest_dir + "/" + names[i] + "/";
194  if (HasPrefix(dest, source, false /* ignore_case */)) {
195  iter_swap(names.begin(), names.begin() + i);
196  iter_swap(modes.begin(), modes.begin() + i);
197  break;
198  }
199  }
200 
201  // List the contents of the source directory
202  // 1. Symlinks are created as they are
203  // 2. Directories become empty directories and are bind-mounted
204  // 3. File become empty regular files and are bind-mounted
205  for (unsigned i = 0; i < names.size(); ++i) {
206  if (std::find(empty_toplevel_dirs.begin(), empty_toplevel_dirs.end(),
207  std::string("/") + names[i])
208  != empty_toplevel_dirs.end()) {
209  continue;
210  }
211 
212  const std::string source = source_dir + "/" + names[i];
213  const std::string dest = dest_dir + "/" + names[i];
214  new_paths->push_back(dest);
215  if (S_ISLNK(modes[i])) {
216  char buf[PATH_MAX + 1];
217  const ssize_t nchars = readlink(source.c_str(), buf, PATH_MAX);
218  if (nchars < 0)
219  throw EPublish("cannot read symlink " + source);
220  buf[nchars] = '\0';
221  SymlinkForced(std::string(buf), dest);
222  } else {
223  if (S_ISDIR(modes[i])) {
224  EnsureDirectory(dest);
225  } else {
226  CreateFile(dest, 0600, false /* ignore_failure */);
227  }
228  LogCvmfs(kLogCvmfs, kLogDebug, "underlay: %s --> %s", source.c_str(),
229  dest.c_str());
230  const bool rv = BindMount(source, dest);
231  if (!rv) {
232  throw EPublish("cannot bind mount " + source + " --> " + dest + " ("
233  + StringifyInt(errno) + ")");
234  }
235  }
236  }
237 
238  // Recurse into the directory trees containing empty directories
239  // CreateUnderlay($SOURCE/cvmfs, $DEST/cvmfs, /atlas.cern.ch)
240  for (unsigned i = 0; i < empty_toplevel_dirs.size(); ++i) {
241  const std::string toplevel_dir = empty_toplevel_dirs[i];
242  std::vector<std::string> empty_sub_dir;
243  empty_sub_dir.push_back(empty_dirs[i].substr(toplevel_dir.length()));
244  if (!empty_sub_dir[0].empty()) {
245  CreateUnderlay(source_dir + toplevel_dir,
246  dest_dir + toplevel_dir,
247  empty_sub_dir,
248  new_paths);
249  }
250  }
251 }
252 
253 void CmdEnter::WriteCvmfsConfig(const std::string &extra_config) {
254  BashOptionsManager options_manager(new DefaultOptionsTemplateManager(fqrn_));
255  options_manager.ParseDefault(fqrn_);
256  if (!extra_config.empty())
257  options_manager.ParsePath(extra_config, false /* external */);
258 
259  options_manager.SetValue("CVMFS_MOUNT_DIR",
260  settings_spool_area_.readonly_mnt());
261  options_manager.SetValue("CVMFS_AUTO_UPDATE", "no");
262  options_manager.SetValue("CVMFS_NFS_SOURCE", "no");
263  options_manager.SetValue("CVMFS_HIDE_MAGIC_XATTRS", "yes");
264  options_manager.SetValue("CVMFS_SERVER_CACHE_MODE", "yes");
265  options_manager.SetValue("CVMFS_CLAIM_OWNERSHIP", "yes");
266  options_manager.SetValue("CVMFS_USYSLOG", settings_spool_area_.client_log());
267  options_manager.SetValue("CVMFS_RELOAD_SOCKETS",
268  settings_spool_area_.cache_dir());
269  options_manager.SetValue("CVMFS_WORKSPACE", settings_spool_area_.cache_dir());
270  options_manager.SetValue("CVMFS_CACHE_PRIMARY", "private");
271  options_manager.SetValue("CVMFS_CACHE_private_TYPE", "posix");
272  options_manager.SetValue("CVMFS_CACHE_private_BASE",
273  settings_spool_area_.cache_dir());
274  options_manager.SetValue("CVMFS_CACHE_private_SHARED", "on");
275  options_manager.SetValue("CVMFS_CACHE_private_QUOTA_LIMIT", "4000");
276 
277  const bool rv =
278  SafeWriteToFile(options_manager.Dump(),
279  settings_spool_area_.client_config(), kPrivateFileMode);
280  if (!rv) {
281  throw EPublish("cannot write client config to "
282  + settings_spool_area_.client_config());
283  }
284 }
285 
286 
287 void CmdEnter::MountCvmfs() {
288  if (FindExecutable(cvmfs2_binary_).empty()) {
289  throw EPublish("cannot find executable " + cvmfs2_binary_,
290  EPublish::kFailMissingDependency);
291  }
292 
293  const int fd_stdout = open(stdout_path_.c_str(),
294  O_CREAT | O_APPEND | O_WRONLY, kPrivateFileMode);
295  const int fd_stderr = open(stderr_path_.c_str(),
296  O_CREAT | O_APPEND | O_WRONLY, kPrivateFileMode);
297 
298  const std::string cvmfs_config = settings_spool_area_.client_config();
299 
300  std::vector<std::string> cmdline;
301  cmdline.push_back(cvmfs2_binary_);
302  cmdline.push_back("-o");
303  cmdline.push_back("allow_other,config=" + cvmfs_config);
304  cmdline.push_back(fqrn_);
305  cmdline.push_back(settings_spool_area_.readonly_mnt());
306  std::set<int> preserved_fds;
307  preserved_fds.insert(0);
308  preserved_fds.insert(1);
309  preserved_fds.insert(2);
310  std::map<int, int> map_fds;
311  map_fds[fd_stdout] = 1;
312  map_fds[fd_stderr] = 2;
313  pid_t pid_child;
314  bool rvb = ManagedExec(cmdline, preserved_fds, map_fds,
315  false /* drop_credentials */, false /* clear_env */,
316  false /* double_fork */, &pid_child);
317  if (!rvb) {
318  close(fd_stdout);
319  close(fd_stderr);
320  throw EPublish("cannot run " + cvmfs2_binary_);
321  }
322  const int exit_code = WaitForChild(pid_child);
323  close(fd_stdout);
324  close(fd_stderr);
325  if (exit_code != 0) {
326  throw EPublish("cannot mount cvmfs read-only branch ("
327  + StringifyInt(exit_code) + ")\n" + " command: `"
328  + JoinStrings(cmdline, " ").c_str() + "`");
329  }
330 
331  rvb = BindMount(settings_spool_area_.readonly_mnt(),
332  rootfs_dir_ + settings_spool_area_.readonly_mnt());
333  if (!rvb)
334  throw EPublish("cannot map CernVM-FS mount point");
335 }
336 
337 
338 void CmdEnter::MountOverlayfs() {
339  if (FindExecutable(overlayfs_binary_).empty()) {
340  throw EPublish("cannot find executable " + overlayfs_binary_,
341  EPublish::kFailMissingDependency);
342  }
343 
344  const int fd_stdout = open(stdout_path_.c_str(),
345  O_CREAT | O_APPEND | O_WRONLY, kPrivateFileMode);
346  const int fd_stderr = open(stderr_path_.c_str(),
347  O_CREAT | O_APPEND | O_WRONLY, kPrivateFileMode);
348 
349  std::vector<std::string> cmdline;
350  cmdline.push_back(overlayfs_binary_);
351  cmdline.push_back("-o");
352  cmdline.push_back(string("lowerdir=") + settings_spool_area_.readonly_mnt()
353  + ",upperdir=" + settings_spool_area_.scratch_dir()
354  + ",workdir=" + settings_spool_area_.ovl_work_dir());
355  cmdline.push_back(rootfs_dir_ + settings_spool_area_.union_mnt());
356  std::set<int> preserved_fds;
357  preserved_fds.insert(0);
358  preserved_fds.insert(1);
359  preserved_fds.insert(2);
360  std::map<int, int> map_fds;
361  map_fds[fd_stdout] = 1;
362  map_fds[fd_stderr] = 2;
363  pid_t pid_child;
364  const bool rvb =
365  ManagedExec(cmdline, preserved_fds, map_fds, true /* drop_credentials */,
366  false /* clear_env */, false /* double_fork */, &pid_child);
367  if (!rvb) {
368  close(fd_stdout);
369  close(fd_stderr);
370  throw EPublish("cannot run " + overlayfs_binary_);
371  }
372  const int exit_code = WaitForChild(pid_child);
373  close(fd_stdout);
374  close(fd_stderr);
375  if (exit_code != 0) {
376  throw EPublish("cannot mount overlay file system ("
377  + StringifyInt(exit_code) + ")\n" + " command: `"
378  + JoinStrings(cmdline, " ").c_str() + "`");
379  }
380 }
381 
382 
383 std::string CmdEnter::GetCvmfsXattr(const std::string &name) {
384  std::string xattr;
385  const bool rvb = platform_getxattr(settings_spool_area_.readonly_mnt(),
386  std::string("user.") + name, &xattr);
387  if (!rvb) {
388  throw EPublish("cannot get extrended attribute " + name + " from "
389  + settings_spool_area_.readonly_mnt());
390  }
391  return xattr;
392 }
393 
394 
395 void CmdEnter::CleanupSession(bool keep_logs,
396  const std::vector<std::string> &new_paths) {
398  "Cleaning out session directory... ");
399 
400  std::string pid_xattr;
401  bool rvb = platform_getxattr(settings_spool_area_.readonly_mnt(), "user.pid",
402  &pid_xattr);
403  if (!rvb)
404  throw EPublish("cannot find CernVM-FS process");
405  const pid_t pid_cvmfs = String2Uint64(GetCvmfsXattr("pid"));
406 
407  const std::string union_mnt = rootfs_dir_ + settings_spool_area_.union_mnt();
408  rvb = platform_umount_lazy(union_mnt.c_str());
409  if (!rvb)
410  throw EPublish("cannot unmount overlayfs on " + union_mnt);
411  rvb = platform_umount_lazy(
412  (rootfs_dir_ + settings_spool_area_.readonly_mnt()).c_str());
413  if (!rvb) {
414  throw EPublish("cannot unmount mapped CernVM-FS on " + rootfs_dir_
415  + settings_spool_area_.readonly_mnt());
416  }
417  rvb = platform_umount_lazy(settings_spool_area_.readonly_mnt().c_str());
418  if (!rvb) {
419  throw EPublish("cannot unmount CernVM-FS on "
420  + settings_spool_area_.readonly_mnt());
421  }
422 
423  BackoffThrottle backoff;
424  while (ProcessExists(pid_cvmfs)) {
425  backoff.Throttle();
426  }
427 
428  RemoveSingle(settings_spool_area_.readonly_mnt());
429  RemoveSingle(settings_spool_area_.client_config());
430  RemoveSingle(env_conf_);
431  rvb = RemoveTree(settings_spool_area_.ovl_work_dir());
432  if (!rvb)
433  throw EPublish("cannot remove " + settings_spool_area_.ovl_work_dir());
434  rvb = RemoveTree(settings_spool_area_.scratch_base());
435  if (!rvb)
436  throw EPublish("cannot remove " + settings_spool_area_.scratch_base());
437  rvb = RemoveTree(settings_spool_area_.cache_dir());
438  if (!rvb)
439  throw EPublish("cannot remove " + settings_spool_area_.cache_dir());
440  RemoveUnderlay(rootfs_dir_, new_paths);
441  rvb = RemoveTree(settings_spool_area_.tmp_dir());
442  if (!rvb)
443  throw EPublish("cannot remove " + settings_spool_area_.tmp_dir());
444 
445  if (keep_logs) {
446  LogCvmfs(kLogCvmfs, kLogStdout, "[logs available in %s]",
447  settings_spool_area_.log_dir().c_str());
448  return;
449  }
450 
451  rvb = RemoveTree(settings_spool_area_.log_dir());
452  RemoveSingle(session_dir_ + "/session_pid");
453  RemoveSingle(session_dir_ + "/" + fqrn_ + "/server.conf");
454  RemoveSingle(session_dir_ + "/" + fqrn_);
455  RemoveSingle(session_dir_ + "/session_token");
456  RemoveSingle(session_dir_ + "/in_transaction.lock");
457  RemoveSingle(session_dir_ + "/shellaction.marker");
458  RemoveSingle(session_dir_);
459  LogCvmfs(kLogCvmfs, kLogStdout, "[done]");
460 }
461 
462 
463 int CmdEnter::Main(const Options &options) {
464  fqrn_ = options.plain_args()[0].value_str;
465  const sanitizer::RepositorySanitizer sanitizer;
466  if (!sanitizer.IsValid(fqrn_)) {
467  throw EPublish("malformed repository name: " + fqrn_);
468  }
469 
470  bool rvb;
471 
472  // We cannot have any capabilities or else we are not allowed to write
473  // to /proc/self/setgroups and /proc/self/[u|g]id_map when creating a user
474  // namespace
475  Env::DropCapabilities();
476 
477  if (options.Has("cvmfs2")) {
478  cvmfs2_binary_ = options.GetString("cvmfs2");
479  // Lucky guess: library in the same directory than the binary,
480  // but don't overwrite an explicit setting
481  setenv("CVMFS_LIBRARY_PATH", GetParentPath(cvmfs2_binary_).c_str(), 0);
482  }
483 
484  // Prepare the session directory
485  const std::string workspace = GetHomeDirectory() + "/.cvmfs/" + fqrn_;
486  EnsureDirectory(workspace);
487  session_dir_ = CreateTempDir(workspace + "/session");
488  if (session_dir_.empty())
489  throw EPublish("cannot create session directory in " + workspace);
490  settings_spool_area_.SetUnionMount(std::string("/cvmfs/") + fqrn_);
491  settings_spool_area_.SetSpoolArea(session_dir_);
492  settings_spool_area_.EnsureDirectories();
493  rootfs_dir_ = session_dir_ + "/rootfs";
494  EnsureDirectory(rootfs_dir_);
495  stdout_path_ = settings_spool_area_.log_dir() + "/stdout.log";
496  stderr_path_ = settings_spool_area_.log_dir() + "/stderr.log";
497 
498  // Save process context information before switching namespaces
499  const string cwd = GetCurrentWorkingDirectory();
500  const uid_t uid = geteuid();
501  const gid_t gid = getegid();
502 
504  "*** NOTE: This is currently an experimental CernVM-FS feature\n");
506  "Entering ephemeral writable shell for %s... ",
507  settings_spool_area_.union_mnt().c_str());
508 
509  // Create root user namespace and rootfs underlay
510  const AnchorPid anchor_pid = EnterRootContainer();
511  std::vector<std::string> empty_dirs;
512  empty_dirs.push_back(settings_spool_area_.union_mnt());
513  empty_dirs.push_back("/proc");
514  std::vector<std::string> new_paths;
515  CreateUnderlay("", rootfs_dir_, empty_dirs, &new_paths);
516  // The .cvmfsenter marker file helps when attaching to the mount namespace
517  // to distinguish it from the system root
518  rvb = SymlinkForced(session_dir_, rootfs_dir_ + "/.cvmfsenter");
519  if (!rvb)
520  throw EPublish("cannot create marker file " + rootfs_dir_ + "/.cvmfsenter");
521  new_paths.push_back(rootfs_dir_ + "/.cvmfsenter");
522  rvb = ProcMount(rootfs_dir_ + "/proc");
523  if (!rvb)
524  throw EPublish("cannot mount " + rootfs_dir_ + "/proc");
525  setenv("CVMFS_ENTER_SESSION_DIR", session_dir_.c_str(), 1 /* overwrite */);
526 
528  "Mounting CernVM-FS read-only layer... ");
529  // We mount in a sub process in order to avoid tainting the main process
530  // with the CVMFS_* environment variables
531  pid_t pid = fork();
532  if (pid < 0)
533  throw EPublish("cannot fork");
534  if (pid == 0) {
535  WriteCvmfsConfig(options.GetStringDefault("cvmfs-config", ""));
536  MountCvmfs();
537  _exit(0);
538  }
539  int exit_code = WaitForChild(pid);
540  if (exit_code != 0)
541  _exit(exit_code);
542  LogCvmfs(kLogCvmfs, kLogStdout, "done");
543 
544  env_conf_ = session_dir_ + "/env.conf";
545  rvb = SafeWriteToFile(std::string("CVMFS_FQRN=") + fqrn_ + "\n", env_conf_,
547  if (!rvb)
548  throw EPublish("cannot create session environment file");
549 
551  "Mounting union file system... ");
552  MountOverlayfs();
553  LogCvmfs(kLogCvmfs, kLogStdout, "done");
554 
555  // Fork the inner process, the outer one is used later for cleanup
556  pid = fork();
557  if (pid < 0)
558  throw EPublish("cannot create subshell");
559 
560  if (pid == 0) {
561  if (!options.Has("root")) {
562  const NamespaceFailures failure = CreateUserNamespace(uid, gid);
563  if (failure != kFailNsOk) {
564  throw publish::EPublish(
565  "cannot create user namespace for " + StringifyInt(uid) + ":"
566  + StringifyInt(gid) + " (" + StringifyInt(failure) + " / "
567  + StringifyInt(errno) + ") [euid=" + StringifyInt(geteuid())
568  + ", egid=" + StringifyInt(getegid()) + "]");
569  }
570  }
571 
572  LogCvmfs(kLogCvmfs, kLogStdout | kLogNoLinebreak, "Switching to %s... ",
573  rootfs_dir_.c_str());
574  int rvi = chroot(rootfs_dir_.c_str());
575  if (rvi != 0)
576  throw EPublish("cannot chroot to " + rootfs_dir_);
577  LogCvmfs(kLogCvmfs, kLogStdout, "done");
578 
579  SettingsBuilder builder;
580  UniquePtr<Publisher> publisher;
581 
582  if (options.Has("transaction")) {
584  "Starting a transaction inside the enter shell");
585 
586  if (options.Has("repo-config")) {
587  repo_config_ = options.GetString("repo-config");
589  "Parsing the external configuration for the repository at %s",
590  repo_config_.c_str());
591 
592  std::string config;
593  const std::string config_file =
594  repo_config_ + "/" + fqrn_ + "/server.conf";
595  const std::string folderpath = session_dir_ + "/" + fqrn_;
596  MkdirDeep(folderpath.c_str(), 0700, true /* veryfy_writable */);
597 
598  const std::string session_config_file = folderpath + "/server.conf";
599  const int fd_config = open(config_file.c_str(), O_RDONLY);
600  SafeReadToString(fd_config, &config);
601  SafeWriteToFile(config, session_config_file, 0600);
602 
603  builder.SetConfigPath(session_dir_);
604  }
605 
606  SettingsPublisher *settings_publisher = builder.CreateSettingsPublisher(
607  fqrn_, false);
608  publisher = new Publisher(*settings_publisher);
609  publisher->Transaction();
610  }
611 
612  // May fail if the working directory was invalid to begin with
613  rvi = chdir(cwd.c_str());
614  if (rvi != 0) {
615  LogCvmfs(kLogCvmfs, kLogStdout, "Warning: cannot chdir to %s",
616  cwd.c_str());
617  }
618 
619  LogCvmfs(
621  "\n"
622  "You can attach to this shell from another another terminal with\n");
623  if (options.Has("root")) {
625  " nsenter --preserve-credentials -U -p -m -t %u\n"
626  " chroot %s\n",
627  anchor_pid.init_pid, rootfs_dir_.c_str());
628  } else {
630  " nsenter --preserve-credentials -U -m -t %u\n"
631  " chroot %s\n"
632  " nsenter --preserve-credentials -S %u -G %u -U -p -t 1\n",
633  anchor_pid.parent_pid, rootfs_dir_.c_str(), uid, gid);
634  }
635 
636  std::vector<std::string> cmdline;
637  // options.plain_args()[0] is the repository name
638  for (unsigned i = 1; i < options.plain_args().size(); ++i) {
639  cmdline.push_back(options.plain_args()[i].value_str);
640  }
641  if (cmdline.empty())
642  cmdline.push_back(GetShell());
643  std::set<int> preserved_fds;
644  preserved_fds.insert(0);
645  preserved_fds.insert(1);
646  preserved_fds.insert(2);
647  pid_t pid_child = 0;
648  rvb = ManagedExec(cmdline, preserved_fds, std::map<int, int>(),
649  false /* drop_credentials */, false /* clear_env */,
650  false /* double_fork */, &pid_child);
651  const std::string s = StringifyInt(pid_child);
652  SafeWriteToFile(s, session_dir_ + "/session_pid", 0600);
653 
654  std::vector<int> sigs;
655  sigs.push_back(SIGUSR1);
656  exit_code = WaitForChild(pid_child, sigs);
657 
658  LogCvmfs(kLogCvmfs, kLogStdout, "Closing CernVM-FS shell...");
659  if (options.Has("transaction")
660  && !FileExists(session_dir_ + "/shellaction.marker")) {
661  LogCvmfs(kLogCvmfs, kLogStdout, "Closing current transaction...");
662  publisher->session()->SetKeepAlive(false);
663  }
664 
665  return exit_code;
666  }
667 
668  exit_code = WaitForChild(pid);
669  LogCvmfs(kLogCvmfs, kLogStdout, "Leaving CernVM-FS shell...");
670 
671  if (!options.Has("keep-session"))
672  CleanupSession(options.Has("keep-logs"), new_paths);
673 
674  return exit_code;
675 }
676 
677 } // namespace publish
std::string GetStringDefault(const std::string &key, const std::string &default_value) const
Definition: command.h:118
struct stat64 platform_stat64
bool SymlinkForced(const std::string &src, const std::string &dest)
Definition: posix.cc:842
static void RemoveSingle(const std::string &path)
Definition: cmd_enter.cc:86
SettingsPublisher * CreateSettingsPublisher(const std::string &ident, bool needs_managed=false)
Definition: settings.cc:573
bool CreateMountNamespace()
Definition: namespace.cc:126
static void EnsureDirectory(const std::string &path)
Definition: cmd_enter.cc:80
bool Has(const std::string &key) const
Definition: command.h:108
void CreateFile(const std::string &path, const int mode, const bool ignore_failure)
Definition: posix.cc:278
std::string GetString(const std::string &key) const
Definition: command.h:115
CVMFS_EXPORT const LogSource source
Definition: exception.h:33
string JoinStrings(const vector< string > &strings, const string &joint)
Definition: string.cc:356
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)
Definition: posix.cc:1896
assert((mem||(size==0))&&"Out Of Memory")
static void RemoveUnderlay(const std::string &path, const std::vector< std::string > &new_paths)
Definition: cmd_enter.cc:109
bool SafeWriteToFile(const std::string &content, const std::string &path, int mode)
Definition: posix.cc:2137
void SetConfigPath(const std::string &config_path)
Definition: settings.h:525
std::string FindExecutable(const std::string &exe)
Definition: posix.cc:1242
const std::vector< Argument > & plain_args() const
Definition: command.h:128
bool platform_umount_lazy(const char *mountpoint)
int WaitForChild(pid_t pid, const std::vector< int > &sig_ok)
Definition: posix.cc:1601
void ParseDefault(const std::string &fqrn)
Definition: options.cc:281
void Throttle()
Definition: backoff.cc:48
bool IsValid(const std::string &input) const
Definition: sanitizer.cc:112
bool FileExists(const std::string &path)
Definition: posix.cc:803
void SetValue(const std::string &key, const std::string &value)
Definition: options.cc:472
const int kPrivateFileMode
Definition: posix.h:35
static AnchorPid EnterRootContainer()
Definition: cmd_enter.cc:51
ssize_t SafeRead(int fd, void *buf, size_t nbyte)
Definition: posix.cc:2094
bool platform_getxattr(const std::string &path, const std::string &name, std::string *value)
std::string Dump()
Definition: options.cc:447
NamespaceFailures CreateUserNamespace(uid_t map_uid_to, gid_t map_gid_to)
Definition: namespace.cc:61
std::vector< std::string > platform_mountlist()
int platform_lstat(const char *path, platform_stat64 *buf)
bool ProcMount(const std::string &to)
Definition: namespace.cc:116
bool MkdirDeep(const std::string &path, const mode_t mode, bool verify_writable)
Definition: posix.cc:855
std::string GetHomeDirectory()
Definition: posix.cc:1422
std::string GetShell()
Definition: posix.cc:1298
string StringifyInt(const int64_t value)
Definition: string.cc:77
std::string CreateTempDir(const std::string &path_prefix)
Definition: posix.cc:1055
bool HasPrefix(const string &str, const string &prefix, const bool ignore_case)
Definition: string.cc:279
bool CreatePidNamespace(int *fd_parent)
Definition: namespace.cc:162
bool DirectoryExists(const std::string &path)
Definition: posix.cc:824
bool RemoveTree(const std::string &path)
Definition: posix.cc:1101
bool SafeReadToString(int fd, std::string *final_result)
Definition: posix.cc:2117
void ParsePath(const std::string &config_file, const bool external)
Definition: options.cc:144
uint64_t String2Uint64(const string &value)
Definition: string.cc:240
PathString GetParentPath(const PathString &path)
Definition: shortstring.cc:14
bool BindMount(const std::string &from, const std::string &to)
Definition: namespace.cc:106
bool ListDirectory(const std::string &directory, std::vector< std::string > *names, std::vector< mode_t > *modes)
Definition: posix.cc:1207
Publisher(const SettingsPublisher &settings, const bool exists=true)
Definition: repository.cc:619
bool ProcessExists(pid_t pid)
Definition: posix.cc:1559
std::string GetCurrentWorkingDirectory()
Definition: posix.cc:1068
NamespaceFailures
Definition: namespace.h:22
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)
Definition: logging.cc:545