CernVM-FS  2.10.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
cmd_enter.cc
Go to the documentation of this file.
1 
5 #include "cvmfs_config.h"
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 <unistd.h>
17 
18 
19 #include <sys/wait.h>
20 
21 #include <algorithm>
22 #include <cassert>
23 #include <cstdio>
24 #include <map>
25 #include <set>
26 #include <string>
27 #include <vector>
28 
29 #include "backoff.h"
30 #include "logging.h"
31 #include "options.h"
32 #include "platform.h"
33 #include "publish/except.h"
34 #include "publish/repository.h"
35 #include "publish/settings.h"
36 #include "sanitizer.h"
37 #include "util/namespace.h"
38 #include "util/posix.h"
39 #include "util/string.h"
40 
41 using namespace std; // NOLINT
42 
43 namespace {
44 
48 struct AnchorPid {
49  pid_t parent_pid;
50  pid_t init_pid;
51 };
52 
54  uid_t euid = geteuid();
55  uid_t egid = getegid();
56  NamespaceFailures failure = CreateUserNamespace(0, 0);
57  if (failure != kFailNsOk) {
58  throw publish::EPublish("cannot create root user namespace (" +
59  StringifyInt(failure) + " / " + StringifyInt(errno) + ") [euid=" +
60  StringifyInt(euid) + ", egid=" + StringifyInt(egid) + "]");
61  }
62 
63  bool rvb = CreateMountNamespace();
64  if (!rvb) throw publish::EPublish("cannot create mount namespace");
65  int fd;
66  rvb = CreatePidNamespace(&fd);
67  if (!rvb) throw publish::EPublish("cannot create pid namespace");
68  AnchorPid anchor_pid;
69  int rvi = SafeRead(fd, &anchor_pid.parent_pid, sizeof(pid_t));
70  if (rvi != sizeof(pid_t))
71  throw publish::EPublish("cannot initialize pid namespace");
72  rvi = SafeRead(fd, &anchor_pid.init_pid, sizeof(pid_t));
73  if (rvi != sizeof(pid_t))
74  throw publish::EPublish("cannot initialize pid namespace");
75  close(fd);
76  return anchor_pid;
77 }
78 
79 static void EnsureDirectory(const std::string &path) {
80  bool rv = MkdirDeep(path, 0700, true /* veryfy_writable */);
81  if (!rv)
82  throw publish::EPublish("cannot create directory " + path);
83 }
84 
85 static void RemoveSingle(const std::string &path) {
86  platform_stat64 info;
87  int retval = platform_lstat(path.c_str(), &info);
88  if (retval != 0) {
89  if (errno == ENOENT)
90  return;
91  throw publish::EPublish("cannot remove " + path);
92  }
93 
94  int rv = 0;
95  if (S_ISDIR(info.st_mode))
96  rv = rmdir(path.c_str());
97  else
98  rv = unlink(path.c_str());
99 
100  if (rv == 0 || errno == ENOENT)
101  return;
102 
103  throw publish::EPublish(
104  "cannot remove " + path + " (" + StringifyInt(errno) + ")");
105 }
106 
107 
108 static void RemoveUnderlay(
109  const std::string &path, const std::vector<std::string> &new_paths)
110 {
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  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(
126  "cannot unmount " + *iter + " (" + 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(
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)
161 {
162  LogCvmfs(kLogCvmfs, kLogDebug, "underlay: entry %s --> %s",
163  source_dir.c_str(), dest_dir.c_str());
164 
165  // For an empty directory /cvmfs/atlas.cern.ch, we are going to store "/cvmfs"
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];
169  while (!GetParentPath(toplevel_dir).empty())
170  toplevel_dir = GetParentPath(toplevel_dir);
171  empty_toplevel_dirs.push_back(toplevel_dir);
172 
173  // We create $DEST/cvmfs (top-level dir)
174  std::string dest_empty_dir = dest_dir + toplevel_dir;
175  LogCvmfs(kLogCvmfs, kLogDebug, "underlay: mkdir %s",
176  dest_empty_dir.c_str());
177  EnsureDirectory(dest_empty_dir);
178  new_paths->push_back(dest_empty_dir);
179  }
180 
181  std::vector<std::string> names;
182  std::vector<mode_t> modes;
183  // In a recursive call, the source directory might not exist, which is fine
184  std::string d = source_dir.empty() ? "/" : source_dir;
185  if (DirectoryExists(d)) {
186  bool rv = ListDirectory(d, &names, &modes);
187  if (!rv)
188  throw EPublish("cannot list directory " + d);
189  }
190  // We need to order creating the bindmounts such that the destination itself
191  // is handled first; otherwise, the recursive bind mount to the session dir
192  // creates all other sub bind mounts again.
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] + "/";
196  if (HasPrefix(dest, source, false /* ignore_case */)) {
197  iter_swap(names.begin(), names.begin() + i);
198  iter_swap(modes.begin(), modes.begin() + i);
199  break;
200  }
201  }
202 
203  // List the contents of the source directory
204  // 1. Symlinks are created as they are
205  // 2. Directories become empty directories and are bind-mounted
206  // 3. File become empty regular files and are bind-mounted
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())
210  {
211  continue;
212  }
213 
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);
220  if (nchars < 0)
221  throw EPublish("cannot read symlink " + source);
222  buf[nchars] = '\0';
223  SymlinkForced(std::string(buf), dest);
224  } else {
225  if (S_ISDIR(modes[i])) {
226  EnsureDirectory(dest);
227  } else {
228  CreateFile(dest, 0600, false /* ignore_failure */);
229  }
230  LogCvmfs(kLogCvmfs, kLogDebug, "underlay: %s --> %s",
231  source.c_str(), dest.c_str());
232  bool rv = BindMount(source, dest);
233  if (!rv) {
234  throw EPublish("cannot bind mount " + source + " --> " + dest +
235  " (" + StringifyInt(errno) + ")");
236  }
237  }
238  }
239 
240  // Recurse into the directory trees containing empty directories
241  // CreateUnderlay($SOURCE/cvmfs, $DEST/cvmfs, /atlas.cern.ch)
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,
249  empty_sub_dir,
250  new_paths);
251  }
252  }
253 }
254 
255 void CmdEnter::WriteCvmfsConfig(const std::string &extra_config) {
256  BashOptionsManager options_manager(
257  new DefaultOptionsTemplateManager(fqrn_));
258  options_manager.ParseDefault(fqrn_);
259  if (!extra_config.empty())
260  options_manager.ParsePath(extra_config, false /* external */);
261 
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");
279 
280  bool rv = SafeWriteToFile(options_manager.Dump(),
281  settings_spool_area_.client_config(),
283  if (!rv) {
284  throw EPublish("cannot write client config to " +
285  settings_spool_area_.client_config());
286  }
287 }
288 
289 
290 void CmdEnter::MountCvmfs() {
291  if (FindExecutable(cvmfs2_binary_).empty()) {
292  throw EPublish("cannot find executable " + cvmfs2_binary_,
293  EPublish::kFailMissingDependency);
294  }
295 
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,
300 
301  std::string cvmfs_config = settings_spool_area_.client_config();
302 
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;
316  pid_t pid_child;
317  bool rvb = ManagedExec(cmdline, preserved_fds, map_fds,
318  false /* drop_credentials */, false /* clear_env */,
319  false /* double_fork */,
320  &pid_child);
321  if (!rvb) {
322  close(fd_stdout);
323  close(fd_stderr);
324  throw EPublish("cannot run " + cvmfs2_binary_);
325  }
326  int exit_code = WaitForChild(pid_child);
327  close(fd_stdout);
328  close(fd_stderr);
329  if (exit_code != 0) {
330  throw EPublish("cannot mount cvmfs read-only branch (" +
331  StringifyInt(exit_code) + ")\n" +
332  " command: `" + JoinStrings(cmdline, " ").c_str() + "`");
333  }
334 
335  rvb = BindMount(settings_spool_area_.readonly_mnt(),
336  rootfs_dir_ + settings_spool_area_.readonly_mnt());
337  if (!rvb)
338  throw EPublish("cannot map CernVM-FS mount point");
339 }
340 
341 
342 void CmdEnter::MountOverlayfs() {
343  if (FindExecutable(overlayfs_binary_).empty()) {
344  throw EPublish("cannot find executable " + overlayfs_binary_,
345  EPublish::kFailMissingDependency);
346  }
347 
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,
352 
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;
367  pid_t pid_child;
368  bool rvb = ManagedExec(cmdline, preserved_fds, map_fds,
369  true /* drop_credentials */, false /* clear_env */,
370  false /* double_fork */,
371  &pid_child);
372  if (!rvb) {
373  close(fd_stdout);
374  close(fd_stderr);
375  throw EPublish("cannot run " + overlayfs_binary_);
376  }
377  int exit_code = WaitForChild(pid_child);
378  close(fd_stdout);
379  close(fd_stderr);
380  if (exit_code != 0) {
381  throw EPublish("cannot mount overlay file system (" +
382  StringifyInt(exit_code) + ")\n" +
383  " command: `" + JoinStrings(cmdline, " ").c_str() + "`");
384  }
385 }
386 
387 
388 std::string CmdEnter::GetCvmfsXattr(const std::string &name) {
389  std::string xattr;
390  bool rvb = platform_getxattr(settings_spool_area_.readonly_mnt(),
391  std::string("user.") + name, &xattr);
392  if (!rvb) {
393  throw EPublish("cannot get extrended attribute " + name + " from " +
394  settings_spool_area_.readonly_mnt());
395  }
396  return xattr;
397 }
398 
399 
400 void CmdEnter::CleanupSession(
401  bool keep_logs, const std::vector<std::string> &new_paths)
402 {
404  "Cleaning out session directory... ");
405 
406  std::string pid_xattr;
407  bool rvb = platform_getxattr(settings_spool_area_.readonly_mnt(),
408  "user.pid", &pid_xattr);
409  if (!rvb)
410  throw EPublish("cannot find CernVM-FS process");
411  pid_t pid_cvmfs = String2Uint64(GetCvmfsXattr("pid"));
412 
413  const std::string union_mnt = rootfs_dir_ + settings_spool_area_.union_mnt();
414  rvb = platform_umount_lazy(union_mnt.c_str());
415  if (!rvb)
416  throw EPublish("cannot unmount overlayfs on " + union_mnt);
417  rvb = platform_umount_lazy(
418  (rootfs_dir_ + settings_spool_area_.readonly_mnt()).c_str());
419  if (!rvb) {
420  throw EPublish("cannot unmount mapped CernVM-FS on " +
421  rootfs_dir_ + settings_spool_area_.readonly_mnt());
422  }
423  rvb = platform_umount_lazy(settings_spool_area_.readonly_mnt().c_str());
424  if (!rvb) {
425  throw EPublish("cannot unmount CernVM-FS on " +
426  settings_spool_area_.readonly_mnt());
427  }
428 
429  BackoffThrottle backoff;
430  while (ProcessExists(pid_cvmfs)) {
431  backoff.Throttle();
432  }
433 
434  RemoveSingle(settings_spool_area_.readonly_mnt());
435  RemoveSingle(settings_spool_area_.client_config());
436  RemoveSingle(env_conf_);
437  rvb = RemoveTree(settings_spool_area_.ovl_work_dir());
438  if (!rvb)
439  throw EPublish("cannot remove " + settings_spool_area_.ovl_work_dir());
440  rvb = RemoveTree(settings_spool_area_.scratch_base());
441  if (!rvb)
442  throw EPublish("cannot remove " + settings_spool_area_.scratch_base());
443  rvb = RemoveTree(settings_spool_area_.cache_dir());
444  if (!rvb)
445  throw EPublish("cannot remove " + settings_spool_area_.cache_dir());
446  RemoveUnderlay(rootfs_dir_, new_paths);
447  rvb = RemoveTree(settings_spool_area_.tmp_dir());
448  if (!rvb)
449  throw EPublish("cannot remove " + settings_spool_area_.tmp_dir());
450 
451  if (keep_logs) {
452  LogCvmfs(kLogCvmfs, kLogStdout, "[logs available in %s]",
453  settings_spool_area_.log_dir().c_str());
454  return;
455  }
456 
457  rvb = RemoveTree(settings_spool_area_.log_dir());
458  RemoveSingle(session_dir_ + "/session_pid");
459  RemoveSingle(session_dir_ + "/" + fqrn_ + "/server.conf");
460  RemoveSingle(session_dir_ + "/" + fqrn_);
461  RemoveSingle(session_dir_ + "/session_token");
462  RemoveSingle(session_dir_ + "/in_transaction.lock");
463  RemoveSingle(session_dir_ + "/shellaction.marker");
464  RemoveSingle(session_dir_);
465  LogCvmfs(kLogCvmfs, kLogStdout, "[done]");
466 }
467 
468 
469 int CmdEnter::Main(const Options &options) {
470  fqrn_ = options.plain_args()[0].value_str;
472  if (!sanitizer.IsValid(fqrn_)) {
473  throw EPublish("malformed repository name: " + fqrn_);
474  }
475 
476  bool rvb;
477 
478  // We cannot have any capabilities or else we are not allowed to write
479  // to /proc/self/setgroups anc /proc/self/[u|g]id_map when creating a user
480  // namespace
481  Env::DropCapabilities();
482 
483  if (options.Has("cvmfs2")) {
484  cvmfs2_binary_ = options.GetString("cvmfs2");
485  // Lucky guess: library in the same directory than the binary,
486  // but don't overwrite an explicit setting
487  setenv("CVMFS_LIBRARY_PATH", GetParentPath(cvmfs2_binary_).c_str(), 0);
488  }
489 
490  // Prepare the session directory
491  std::string workspace = GetHomeDirectory() + "/.cvmfs/" + fqrn_;
492  EnsureDirectory(workspace);
493  session_dir_ = CreateTempDir(workspace + "/session");
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";
500  EnsureDirectory(rootfs_dir_);
501  stdout_path_ = settings_spool_area_.log_dir() + "/stdout.log";
502  stderr_path_ = settings_spool_area_.log_dir() + "/stderr.log";
503 
504  // Save process context information before switching namespaces
505  string cwd = GetCurrentWorkingDirectory();
506  uid_t uid = geteuid();
507  gid_t gid = getegid();
508 
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());
514 
515  // Create root user namespace and rootfs underlay
516  AnchorPid anchor_pid = EnterRootContainer();
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);
522  // The .cvmfsenter marker file helps when attaching to the mount namespace
523  // to distinguish it from the system root
524  rvb = SymlinkForced(session_dir_, rootfs_dir_ + "/.cvmfsenter");
525  if (!rvb)
526  throw EPublish("cannot create marker file " + rootfs_dir_ + "/.cvmfsenter");
527  new_paths.push_back(rootfs_dir_ + "/.cvmfsenter");
528  rvb = ProcMount(rootfs_dir_ + "/proc");
529  if (!rvb)
530  throw EPublish("cannot mount " + rootfs_dir_ + "/proc");
531  setenv("CVMFS_ENTER_SESSION_DIR", session_dir_.c_str(), 1 /* overwrite */);
532 
534  "Mounting CernVM-FS read-only layer... ");
535  // We mount in a sub process in order to avoid tainting the main process
536  // with the CVMFS_* environment variables
537  pid_t pid = fork();
538  if (pid < 0)
539  throw EPublish("cannot fork");
540  if (pid == 0) {
541  WriteCvmfsConfig(options.GetStringDefault("cvmfs-config", ""));
542  MountCvmfs();
543  _exit(0);
544  }
545  int exit_code = WaitForChild(pid);
546  if (exit_code != 0)
547  _exit(exit_code);
548  LogCvmfs(kLogCvmfs, kLogStdout, "done");
549 
550  env_conf_ = session_dir_ + "/env.conf";
551  rvb = SafeWriteToFile(std::string("CVMFS_FQRN=") + fqrn_ + "\n",
552  env_conf_, kPrivateFileMode);
553  if (!rvb)
554  throw EPublish("cannot create session environment file");
555 
557  "Mounting union file system... ");
558  MountOverlayfs();
559  LogCvmfs(kLogCvmfs, kLogStdout, "done");
560 
561  // Fork the inner process, the outer one is used later for cleanup
562  pid = fork();
563  if (pid < 0)
564  throw EPublish("cannot create subshell");
565 
566  if (pid == 0) {
567  if (!options.Has("root")) {
568  NamespaceFailures failure = CreateUserNamespace(uid, gid);
569  if (failure != kFailNsOk) {
570  throw publish::EPublish("cannot create user namespace for " +
571  StringifyInt(uid) + ":" + StringifyInt(gid) + " (" +
572  StringifyInt(failure) + " / " + StringifyInt(errno) + ") [euid=" +
573  StringifyInt(geteuid()) + ", egid=" + StringifyInt(getegid()) + "]");
574  }
575  }
576 
578  "Switching to %s... ", rootfs_dir_.c_str());
579  int rvi = chroot(rootfs_dir_.c_str());
580  if (rvi != 0)
581  throw EPublish("cannot chroot to " + rootfs_dir_);
582  LogCvmfs(kLogCvmfs, kLogStdout, "done");
583 
584  SettingsBuilder builder;
585  UniquePtr<Publisher> publisher;
586 
587  if (options.Has("transaction")) {
589  "Starting a transaction inside the enter shell");
590 
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());
596 
597  std::string config;
598  std::string config_file = repo_config_ + "/" + fqrn_ + "/server.conf";
599  std::string folderpath = session_dir_ + "/" + fqrn_;
600  MkdirDeep(folderpath.c_str(), 0700, true /* veryfy_writable */);
601 
602  std::string session_config_file = folderpath + "/server.conf";
603  int fd_config = open(config_file.c_str(), O_RDONLY);
604  SafeReadToString(fd_config, &config);
605  SafeWriteToFile(config, session_config_file, 0600);
606 
607  builder.SetConfigPath(session_dir_);
608  }
609 
610  SettingsPublisher *settings_publisher =
611  builder.CreateSettingsPublisher(fqrn_, false);
612  publisher = new Publisher(*settings_publisher);
613  publisher->Transaction();
614  }
615 
616  // May fail if the working directory was invalid to begin with
617  rvi = chdir(cwd.c_str());
618  if (rvi != 0) {
619  LogCvmfs(kLogCvmfs, kLogStdout, "Warning: cannot chdir to %s",
620  cwd.c_str());
621  }
622 
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"
628  " chroot %s\n",
629  anchor_pid.init_pid, rootfs_dir_.c_str());
630  } else {
632  " nsenter --preserve-credentials -U -m -t %u\n"
633  " chroot %s\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);
636  }
637 
638  std::vector<std::string> cmdline;
639  // options.plain_args()[0] is the repository name
640  for (unsigned i = 1; i < options.plain_args().size(); ++i) {
641  cmdline.push_back(options.plain_args()[i].value_str);
642  }
643  if (cmdline.empty())
644  cmdline.push_back(GetShell());
645  std::set<int> preserved_fds;
646  preserved_fds.insert(0);
647  preserved_fds.insert(1);
648  preserved_fds.insert(2);
649  pid_t pid_child;
650  rvb = ManagedExec(cmdline, preserved_fds, std::map<int, int>(),
651  false /* drop_credentials */, false /* clear_env */,
652  false /* double_fork */,
653  &pid_child);
654  std::string s = StringifyInt(pid_child);
655  SafeWriteToFile(s, session_dir_ + "/session_pid", 0600);
656 
657  std::vector<int> sigs;
658  sigs.push_back(SIGUSR1);
659  exit_code = WaitForChild(pid_child, sigs);
660 
661  if (options.Has("transaction") &&
662  !FileExists(session_dir_ + "/shellaction.marker")) {
663  LogCvmfs(kLogCvmfs, kLogStdout, "Closing current transaction...");
664  publisher->session()->SetKeepAlive(false);
665  }
666 
667  return exit_code;
668  }
669 
670  exit_code = WaitForChild(pid);
671  LogCvmfs(kLogCvmfs, kLogStdout, "Leaving CernVM-FS shell...");
672 
673  if (!options.Has("keep-session"))
674  CleanupSession(options.Has("keep-logs"), new_paths);
675 
676  return exit_code;
677 }
678 
679 } // namespace publish
#define LogCvmfs(source, mask,...)
Definition: logging.h:20
std::string GetStringDefault(const std::string &key, const std::string &default_value) const
Definition: command.h:126
struct stat64 platform_stat64
bool SymlinkForced(const std::string &src, const std::string &dest)
Definition: posix.cc:858
static void RemoveSingle(const std::string &path)
Definition: cmd_enter.cc:85
SettingsPublisher * CreateSettingsPublisher(const std::string &ident, bool needs_managed=false)
Definition: settings.cc:581
bool CreateMountNamespace()
Definition: namespace.cc:118
static void EnsureDirectory(const std::string &path)
Definition: cmd_enter.cc:79
bool Has(const std::string &key) const
Definition: command.h:114
void CreateFile(const std::string &path, const int mode, const bool ignore_failure)
Definition: posix.cc:313
std::string GetString(const std::string &key) const
Definition: command.h:123
string JoinStrings(const vector< string > &strings, const string &joint)
Definition: string.cc:318
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:1784
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:108
bool SafeWriteToFile(const std::string &content, const std::string &path, int mode)
Definition: posix.cc:2027
void SetConfigPath(const std::string &config_path)
Definition: settings.h:521
std::string FindExecutable(const std::string &exe)
Definition: posix.cc:1269
const std::vector< Argument > & plain_args() const
Definition: command.h:137
bool platform_umount_lazy(const char *mountpoint)
int WaitForChild(pid_t pid, const std::vector< int > &sig_ok)
Definition: posix.cc:1617
std::string GetParentPath(const std::string &path)
Definition: posix.cc:131
void ParseDefault(const std::string &fqrn)
Definition: options.cc:282
void Throttle()
Definition: backoff.cc:50
bool IsValid(const std::string &input) const
Definition: sanitizer.cc:114
bool FileExists(const std::string &path)
Definition: posix.cc:816
void SetValue(const std::string &key, const std::string &value)
Definition: options.cc:466
const int kPrivateFileMode
Definition: posix.h:34
static AnchorPid EnterRootContainer()
Definition: cmd_enter.cc:53
ssize_t SafeRead(int fd, void *buf, size_t nbyte)
Definition: posix.cc:1988
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:58
std::vector< std::string > platform_mountlist()
int platform_lstat(const char *path, platform_stat64 *buf)
bool ProcMount(const std::string &to)
Definition: namespace.cc:108
bool MkdirDeep(const std::string &path, const mode_t mode, bool verify_writable)
Definition: posix.cc:871
std::string GetHomeDirectory()
Definition: posix.cc:1449
std::string GetShell()
Definition: posix.cc:1325
string StringifyInt(const int64_t value)
Definition: string.cc:78
std::string CreateTempDir(const std::string &path_prefix)
Definition: posix.cc:1072
bool HasPrefix(const string &str, const string &prefix, const bool ignore_case)
Definition: string.cc:265
bool CreatePidNamespace(int *fd_parent)
Definition: namespace.cc:151
bool DirectoryExists(const std::string &path)
Definition: posix.cc:838
bool RemoveTree(const std::string &path)
Definition: posix.cc:1120
bool SafeReadToString(int fd, std::string *final_result)
Definition: posix.cc:2011
void ParsePath(const std::string &config_file, const bool external)
Definition: options.cc:146
Publisher(const SettingsPublisher &settings)
Definition: repository.cc:634
uint64_t String2Uint64(const string &value)
Definition: string.cc:228
bool BindMount(const std::string &from, const std::string &to)
Definition: namespace.cc:98
bool ListDirectory(const std::string &directory, std::vector< std::string > *names, std::vector< mode_t > *modes)
Definition: posix.cc:1233
bool ProcessExists(pid_t pid)
Definition: posix.cc:1576
std::string GetCurrentWorkingDirectory()
Definition: posix.cc:1085
NamespaceFailures
Definition: namespace.h:20