CernVM-FS  2.13.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
mount.cvmfs.cc
Go to the documentation of this file.
1 
8 #include <errno.h>
9 #include <sys/select.h>
10 #include <sys/socket.h>
11 #include <sys/types.h>
12 #include <sys/uio.h>
13 #include <sys/wait.h>
14 #ifdef __APPLE__
15 #include <sys/sysctl.h>
16 #endif
17 #include <sys/un.h>
18 #include <unistd.h>
19 
20 #include <cstdio>
21 #include <cstdlib>
22 #include <string>
23 #include <vector>
24 
25 #include "options.h"
26 #include "sanitizer.h"
27 #include "util/logging.h"
28 #include "util/platform.h"
29 #include "util/posix.h"
30 #include "util/string.h"
31 
32 using namespace std; // NOLINT
33 
35 
36 static void Usage(int output_dest) {
37  LogCvmfs(kLogCvmfs, output_dest,
38  "Mount helper for CernVM-FS. Used by mount(8)\n"
39  "Mandatory arguments:\n"
40  " repository name: <repository>\n"
41  " mountpoint of the repository: <mountpoint>\n"
42  "Options:\n"
43  " -o <mount options>\n"
44  " -f dry run, just prints out the mount command\n"
45  " -h print this help");
46 }
47 
48 
49 static void AddMountOption(const string &option,
50  vector<string> *mount_options) {
51  mount_options->push_back(option);
52 }
53 
54 
55 static string MkFqrn(const string &repository) {
56  const string::size_type idx = repository.find_last_of('.');
57  if (idx == string::npos) {
58  string domain;
59  const bool retval =
60  options_manager_.GetValue("CVMFS_DEFAULT_DOMAIN", &domain);
61  if (!retval) {
63  "CVMFS_DEFAULT_DOMAIN missing");
64  abort();
65  }
66  return repository + "." + domain;
67  }
68  return repository;
69 }
70 
71 #if defined(__APPLE__) && !defined(USE_MACFUSE_KEXT)
72 static bool IsFuseTInstalled() {
73  return true;
74  string fuseTComponentsPaths[] = {"/usr/local/bin/go-nfsv4",
75  "/usr/local/lib/libfuse-t.dylib",
76  "/usr/local/lib/libfuse-t.a"};
77  const int pathsNumber = sizeof(fuseTComponentsPaths)
78  / sizeof(fuseTComponentsPaths[0]);
79  platform_stat64 info;
80  for (int idx = 0; idx < pathsNumber; ++idx) {
81  bzero(&info, sizeof(platform_stat64));
82  int retval = platform_stat(fuseTComponentsPaths[idx].c_str(), &info);
83  if ((retval != 0) || !S_ISREG(info.st_mode)) {
84  return false;
85  }
86  }
87  return true;
88 }
89 #endif
90 
91 static bool CheckFuse() {
92 #if defined(__APPLE__) && !defined(USE_MACFUSE_KEXT)
93  bool is_fuse_t_installed = IsFuseTInstalled();
94  if (!is_fuse_t_installed) {
96  "FUSE-T installation check failed. FUSE not loaded");
97  }
98  return is_fuse_t_installed;
99 #else
100  string fuse_device;
101  int retval;
102 #ifdef __APPLE__
103  fuse_device = "/dev/macfuse0";
104 #else
105  fuse_device = "/dev/fuse";
106 #endif
107  platform_stat64 info;
108  retval = platform_stat(fuse_device.c_str(), &info);
109  if ((retval != 0) || !S_ISCHR(info.st_mode)) {
110  LogCvmfs(kLogCvmfs, kLogStderr, "Fuse not loaded");
111  return false;
112  }
113  return true;
114 #endif
115 }
116 
117 
118 static bool CheckStrictMount(const string &fqrn) {
119  string param_strict_mount;
120  if (options_manager_.GetValue("CVMFS_STRICT_MOUNT", &param_strict_mount)
121  && options_manager_.IsOn(param_strict_mount)) {
122  string repository_list;
123  bool retval = options_manager_.GetValue("CVMFS_REPOSITORIES",
124  &repository_list);
125  if (!retval) {
126  LogCvmfs(kLogCvmfs, kLogStderr, "CVMFS_REPOSITORIES missing");
127  return false;
128  }
129  vector<string> repositories = SplitString(repository_list, ',');
130  for (unsigned i = 0; i < repositories.size(); ++i) {
131  if (MkFqrn(repositories[i]) == fqrn)
132  return true;
133  }
134  string config_repository;
135  retval = options_manager_.GetValue("CVMFS_CONFIG_REPOSITORY",
136  &config_repository);
137  if (retval && (config_repository == fqrn))
138  return true;
140  "Not allowed to mount %s, "
141  "add it to CVMFS_REPOSITORIES",
142  fqrn.c_str());
143  return false;
144  }
145  return true;
146 }
147 
148 
149 static bool CheckProxy() {
150  string param;
151  const int retval = options_manager_.GetValue("CVMFS_HTTP_PROXY", &param);
152  if (!retval || param.empty()) {
153  LogCvmfs(kLogCvmfs, kLogStderr, "CVMFS_HTTP_PROXY required");
154  return false;
155  }
156  return true;
157 }
158 
159 
160 static bool CheckConcurrentMount(const string &fqrn,
161  const string &workspace,
162  string *mountpointp) {
163  LockFile(workspace + "/cvmfs_io." + fqrn + ".mountcheck.lock");
164  // Try connecting to cvmfs_io socket
165  const int socket_fd = ConnectSocket(workspace + "/cvmfs_io." + fqrn);
166  if (socket_fd < 0)
167  return false;
168 
169  // There is a repository mounted, find out the mount point
170  SendMsg2Socket(socket_fd, "mountpoint");
171  string mountpoint;
172  char buf;
173  while (read(socket_fd, &buf, 1) == 1) {
174  if (buf != '\n')
175  mountpoint.push_back(buf);
176  }
177  *mountpointp = mountpoint;
178  close(socket_fd);
179  return true;
180 }
181 
182 static int GetExistingFuseFd(const string &fqrn, const string &workspace,
183  uid_t cvmfs_uid) {
184  // Try connecting to cvmfs_io socket
185  const int talk_fd = ConnectSocket(workspace + "/cvmfs_io." + fqrn);
186  if (talk_fd < 0)
187  return -1;
188 
189  // Create temporary socket
190  const std::string recv_sock_dir = CreateTempDir(workspace + "/fusefd");
191  if (recv_sock_dir.empty() || (chmod(recv_sock_dir.c_str(), 0755) != 0)) {
192  close(talk_fd);
193  return -1;
194  }
195  const std::string recv_sock_path = recv_sock_dir + "/sock";
196  const int recv_sock_fd = MakeSocket(recv_sock_path, 0660);
197  if ((recv_sock_fd < 0)
198  || (chown(recv_sock_path.c_str(), cvmfs_uid, getegid()) != 0)) {
199  if (recv_sock_fd >= 0)
200  close(recv_sock_fd);
201  close(talk_fd);
202  unlink(recv_sock_path.c_str());
203  rmdir(recv_sock_dir.c_str());
204  return -1;
205  }
206  listen(recv_sock_fd, 1);
207 
208  // Trigger fd transfer
209  SendMsg2Socket(talk_fd, "send mount fd " + recv_sock_path);
210  string result;
211  char buf;
212  while (read(talk_fd, &buf, 1) == 1) {
213  if (buf != '\n')
214  result.push_back(buf);
215  }
216  close(talk_fd);
217 
218  int fuse_fd = -1;
219  if (result == "OK") {
220  struct sockaddr_un addr;
221  unsigned int len = sizeof(addr);
222  const int con_fd =
223  accept(recv_sock_fd, reinterpret_cast<struct sockaddr *>(&addr), &len);
224  fuse_fd = RecvFdFromSocket(con_fd);
225  close(con_fd);
226  }
227  close(recv_sock_fd);
228  unlink(recv_sock_path.c_str());
229  rmdir(recv_sock_dir.c_str());
230 
231  return fuse_fd;
232 }
233 
234 
235 static bool GetCacheDir(const string &fqrn, string *cachedir) {
236  string param;
237  bool retval = options_manager_.GetValue("CVMFS_CACHE_DIR", &param);
238  if (retval) {
239  *cachedir = MakeCanonicalPath(param);
240  return true;
241  }
242 
243  retval = options_manager_.GetValue("CVMFS_CACHE_BASE", &param);
244  if (!retval)
245  return false;
246 
247  *cachedir = MakeCanonicalPath(param);
248  if (options_manager_.GetValue("CVMFS_SHARED_CACHE", &param)
249  && options_manager_.IsOn(param)) {
250  *cachedir = *cachedir + "/shared";
251  } else {
252  *cachedir = *cachedir + "/" + fqrn;
253  }
254  return true;
255 }
256 
257 
258 static bool GetWorkspace(const string &fqrn, string *workspace) {
259  string param;
260  bool retval = options_manager_.GetValue("CVMFS_WORKSPACE", &param);
261  if (retval) {
262  *workspace = MakeCanonicalPath(param);
263  return true;
264  }
265 
266  retval = GetCacheDir(fqrn, workspace);
267  if (!retval) {
269  "CVMFS_WORKSPACE or CVMFS_CACHE_[BASE|DIR] required");
270  return false;
271  }
272  return true;
273 }
274 
275 
276 static bool WaitForReload(const std::string &mountpoint) {
277  string param;
278  const int retval = options_manager_.GetValue("CVMFS_RELOAD_SOCKETS", &param);
279  if (!retval) {
280  LogCvmfs(kLogCvmfs, kLogStderr, "CVMFS_RELOAD_SOCKETS required");
281  return false;
282  }
283  const string reload_guard = param + "/cvmfs.pause";
284  // Deprecated, now a directory
285  if (FileExists(reload_guard)) {
286  LogCvmfs(kLogCvmfs, kLogStdout, "Waiting for CernVM-FS reload...");
287  while (FileExists(reload_guard))
288  SafeSleepMs(250);
289  }
290  if (DirectoryExists(reload_guard)) {
291  LogCvmfs(kLogCvmfs, kLogStdout, "Waiting for CernVM-FS reload...");
292  const string mountpoint_base64 = Base64(mountpoint);
293  while (DirectoryExists(reload_guard)) {
294  // We are in paused state but automounter unmounted the repo.
295  // We need to allow to mount just to reload.
296  if (FileExists(reload_guard + "/" + mountpoint_base64))
297  break;
298  SafeSleepMs(250);
299  }
300  }
301  return true;
302 }
303 
304 
305 static bool GetCvmfsUser(string *cvmfs_user) {
306  string param;
307  const int retval = options_manager_.GetValue("CVMFS_USER", &param);
308  if (!retval) {
309  LogCvmfs(kLogCvmfs, kLogStderr, "CVMFS_USER required");
310  return false;
311  }
312  *cvmfs_user = param; // No sanitation; due to PAM, username can be anything
313  return true;
314 }
315 
316 
317 static std::string GetCvmfsBinary() {
318  std::string result;
319  vector<string> paths;
320  paths.push_back("/usr/bin");
321 
322 #ifdef __APPLE__
323  // OS X El Capitan came with SIP, forcing us to become relocatable. CVMFS
324  // 2.2.0+ installs into /usr/local always
325  paths.push_back("/usr/local/bin");
326 #endif
327 
328  // TODO(reneme): C++11 range based for loop
329  vector<string>::const_iterator i = paths.begin();
330  const vector<string>::const_iterator iend = paths.end();
331  for (; i != iend; ++i) {
332  const std::string cvmfs2 = *i + "/cvmfs2";
333  if (FileExists(cvmfs2) || SymlinkExists(cvmfs2)) {
334  result = cvmfs2;
335  break;
336  }
337  }
338 
339  return result;
340 }
341 
342 static int AttachMount(const std::string &mountpoint, const std::string &fqrn,
343  int fuse_fd) {
344 #ifdef __APPLE__
345  (void)mountpoint;
346  (void)fqrn;
347  (void)fuse_fd;
348  return 1;
349 #else
350  platform_stat64 info;
351  int retval = platform_stat(mountpoint.c_str(), &info);
352  if (retval != 0)
353  return 1;
354 
355  char mntopt[100];
356  snprintf(mntopt, sizeof(mntopt),
357  "ro,fd=%i,rootmode=%o,user_id=%d,group_id=%d", fuse_fd,
358  info.st_mode & S_IFMT, geteuid(), getegid());
359  // TODO(jblomer): remove NOSUID according to options
360  retval = mount("cvmfs2", mountpoint.c_str(), "fuse",
361  MS_NODEV | MS_RDONLY | MS_NOSUID, mntopt);
362  if (retval != 0) {
364  "Cannot attach to existing fuse module (%d)", errno);
365  return 1;
366  }
368  "CernVM-FS: linking %s to repository %s (attaching)",
369  mountpoint.c_str(), fqrn.c_str());
370  return 0;
371 #endif
372 }
373 
374 
375 int main(int argc, char **argv) {
376  bool dry_run = false;
377  bool remount = false;
378  vector<string> mount_options;
379 
380  // Option parsing
381  vector<string> option_tokens;
382  int c;
383  while ((c = getopt(argc, argv, "vfnho:")) != -1) {
384  switch (c) {
385  case 'f':
386  dry_run = true;
387  break;
388  case 'n':
390  "Note: fusermount _does_ modify "
391  "/etc/mtab in case it is writable.");
392  // Fall through
393  case 'v':
394  break;
395  case 'h':
396  Usage(kLogStdout);
397  return 0;
398  case 'o':
399  AddMountOption(optarg, &mount_options);
400  option_tokens = SplitString(optarg, ',');
401  for (unsigned i = 0; i < option_tokens.size(); ++i) {
402  if (option_tokens[i] == string("remount"))
403  remount = true;
404  }
405  break;
406  default:
407  Usage(kLogStderr);
408  return 1;
409  }
410  }
411  if (optind + 2 != argc) {
412  Usage(kLogStderr);
413  return 1;
414  }
415 
416  string device = argv[optind];
417  // Some mount versions expand the given device to a full path. Thus ignore
418  // everything before the last slash (/)/
419  if (HasPrefix(device, "/", false))
420  device = GetFileName(device);
421  const sanitizer::RepositorySanitizer repository_sanitizer;
422  if (device.empty() || !repository_sanitizer.IsValid(device)) {
423  LogCvmfs(kLogCvmfs, kLogStderr, "Invalid repository: %s", device.c_str());
424  return 1;
425  }
426  const string mountpoint = argv[optind + 1];
427 
429  const string fqrn = MkFqrn(device);
433 
434  string optarg;
435  if (options_manager_.GetValue("CVMFS_SYSLOG_LEVEL", &optarg))
437  if (options_manager_.GetValue("CVMFS_SYSLOG_FACILITY", &optarg))
439  if (options_manager_.GetValue("CVMFS_USYSLOG", &optarg))
440  SetLogMicroSyslog(optarg + ".mount");
441  if (options_manager_.GetValue("CVMFS_DEBUGLOG", &optarg))
442  SetLogDebugFile(optarg + ".mount");
443  SetLogSyslogPrefix(fqrn);
444 
445  int retval;
446  int sysret;
447  bool dedicated_cachedir = false;
448  string cvmfs_user;
449  string cachedir;
450  string workspace;
451  // Environment checks
452  retval = WaitForReload(mountpoint);
453  if (!retval)
454  return 1;
455  retval = GetWorkspace(fqrn, &workspace);
456  if (!retval)
457  return 1;
458  retval = GetCacheDir(fqrn, &cachedir);
459  dedicated_cachedir = (retval && (cachedir != workspace));
460  retval = GetCvmfsUser(&cvmfs_user);
461  if (!retval)
462  return 1;
463  retval = CheckFuse();
464  if (!retval)
465  return 1;
466  retval = CheckStrictMount(fqrn);
467  if (!retval)
468  return 1;
469  retval = CheckProxy();
470  if (!retval)
471  return 1;
472 
473  // Retrieve cvmfs uid/gid and fuse gid if exists
474  uid_t uid_cvmfs;
475  gid_t gid_cvmfs;
476  gid_t gid_fuse;
477  bool has_fuse_group = false;
478  retval = GetUidOf(cvmfs_user, &uid_cvmfs, &gid_cvmfs);
479  if (!retval) {
480  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to find user %s in passwd database",
481  cvmfs_user.c_str());
482  return 1;
483  }
484  has_fuse_group = GetGidOf("fuse", &gid_fuse);
485 
486  // Prepare workspace
487  retval = MkdirDeep(workspace, 0755, false);
488  if (!retval) {
489  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to create workspace %s",
490  workspace.c_str());
491  return 1;
492  }
493 
494  // This is not a sure thing. When the CVMFS_CACHE_BASE parameter is changed
495  // two repositories can get mounted concurrently (but that should not hurt).
496  // If the same repository is mounted multiple times at the same time, there
497  // is a race here. Eventually, only one repository will be mounted while the
498  // other cvmfs processes block on a file lock in the cache.
499  string prev_mountpoint;
500  retval = CheckConcurrentMount(fqrn, workspace, &prev_mountpoint);
501  if (retval) {
502  if (remount && (mountpoint == prev_mountpoint)) {
503  // Actually remounting is too hard, but pretend that it worked
504  return 0;
505  }
506  // Identify zombie fuse processes that are held open by other mount
507  // namespaces
508  if ((mountpoint == prev_mountpoint) && !IsMountPoint(mountpoint)) {
509  // Allow for group access to the socket receiving the fuse fd
510  umask(007);
511  const int fuse_fd = GetExistingFuseFd(fqrn, workspace, uid_cvmfs);
512  if (fuse_fd < 0) {
514  "Cannot connect to existing fuse module");
515  return 1;
516  }
517  return AttachMount(mountpoint, fqrn, fuse_fd);
518  }
519  LogCvmfs(kLogCvmfs, kLogStderr, "Repository %s is already mounted on %s",
520  fqrn.c_str(), prev_mountpoint.c_str());
521  return 1;
522  } else {
523  // No double mount
524  if (remount) {
525  LogCvmfs(kLogCvmfs, kLogStderr, "Repository %s is not mounted on %s",
526  fqrn.c_str(), mountpoint.c_str());
527  return 1;
528  }
529  }
530 
531  // Prepare cache directory
532  if (dedicated_cachedir) {
533  retval = MkdirDeep(cachedir, 0755, false);
534  if (!retval) {
535  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to create cache directory %s",
536  cachedir.c_str());
537  return 1;
538  }
539  }
540  retval = MkdirDeep("/var/run/cvmfs", 0755);
541  if (!retval) {
542  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to create socket directory");
543  return 1;
544  }
545  sysret = chown(workspace.c_str(), uid_cvmfs, getegid());
546  if (sysret != 0) {
547  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to transfer ownership of %s to %s",
548  workspace.c_str(), cvmfs_user.c_str());
549  return 1;
550  }
551  if (dedicated_cachedir) {
552  sysret = chown(cachedir.c_str(), uid_cvmfs, getegid());
553  if (sysret != 0) {
555  "Failed to transfer ownership of %s to %s", cachedir.c_str(),
556  cvmfs_user.c_str());
557  return 1;
558  }
559  }
560  sysret = chown("/var/run/cvmfs", uid_cvmfs, getegid());
561  if (sysret != 0) {
562  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to transfer ownership of %s to %s",
563  "/var/run/cvmfs", cvmfs_user.c_str());
564  return 1;
565  }
566 
567  // Set maximum number of files
568 #ifdef __APPLE__
569  string param;
570  if (options_manager_.GetValue("CVMFS_NFILES", &param)) {
571  sanitizer::IntegerSanitizer integer_sanitizer;
572  if (!integer_sanitizer.IsValid(param)) {
573  LogCvmfs(kLogCvmfs, kLogStderr, "Invalid CVMFS_NFILES: %s",
574  param.c_str());
575  return 1;
576  }
577  int nfiles = String2Uint64(param);
578  if ((nfiles < 128) || (nfiles > 524288)) {
579  LogCvmfs(kLogCvmfs, kLogStderr, "Invalid CVMFS_NFILES: %s",
580  param.c_str());
581  return 1;
582  }
583  int nfiles_all = nfiles + 512;
584  int sys_nfiles, sys_nfiles_all;
585  size_t len = sizeof(sys_nfiles);
586  int mib[2];
587  mib[0] = CTL_KERN;
588  mib[1] = KERN_MAXFILESPERPROC;
589  retval = sysctl(mib, 2, &sys_nfiles, &len, NULL, 0);
590  if ((retval != 0) || (sys_nfiles < 0)) {
591  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to get KERN_MAXFILESPERPROC");
592  return 1;
593  }
594  if (sys_nfiles < nfiles) {
595  mib[1] = KERN_MAXFILES;
596  retval = sysctl(mib, 2, &sys_nfiles_all, &len, NULL, 0);
597  if ((retval != 0) || (sys_nfiles_all < 0)) {
598  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to get KERN_MAXFILES");
599  return 1;
600  }
601  if (sys_nfiles_all < nfiles_all) {
602  retval = sysctl(mib, 2, NULL, NULL, &nfiles_all, sizeof(nfiles_all));
603  if (retval != 0) {
604  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to set KERN_MAXFILES");
605  return 1;
606  }
607  }
608  mib[1] = KERN_MAXFILESPERPROC;
609  retval = sysctl(mib, 2, NULL, NULL, &nfiles_all, sizeof(nfiles_all));
610  if (retval != 0) {
611  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to set KERN_MAXFILESPERPROC");
612  return 1;
613  }
614  }
615  }
616 #endif
617 
618  AddMountOption("system_mount", &mount_options);
619  AddMountOption("fsname=cvmfs2", &mount_options);
620  AddMountOption("allow_other", &mount_options);
621  AddMountOption("grab_mountpoint", &mount_options);
622  AddMountOption("uid=" + StringifyInt(uid_cvmfs), &mount_options);
623  AddMountOption("gid=" + StringifyInt(gid_cvmfs), &mount_options);
624  if (options_manager_.IsDefined("CVMFS_DEBUGLOG"))
625  AddMountOption("debug", &mount_options);
626 
627  const string cvmfs_binary = GetCvmfsBinary();
628  if (cvmfs_binary.empty()) {
629  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to locate the cvmfs2 binary");
630  return 1;
631  }
632 
633  // Dry run early exit
634  if (dry_run) {
635  string cmd = cvmfs_binary + " -o " + JoinStrings(mount_options, ",") + " "
636  + fqrn + " " + mountpoint;
637  if (has_fuse_group) {
638  cmd = "sg fuse -c \"" + cmd + "\"";
639  }
640  LogCvmfs(kLogCvmfs, kLogStdout, "%s", cmd.c_str());
641  return 0;
642  }
643 
644  // Real mount, add supplementary fuse group
645  if (has_fuse_group) {
646  retval = AddGroup2Persona(gid_fuse);
647  if (!retval) {
649  "Failed to add fuse to "
650  " the list of supplementary groups");
651  return 1;
652  }
653  }
654 
655  // Exec cvmfs2, from here on errors return 32 (mount error)
656  int fd_stdin, fd_stdout, fd_stderr;
657  pid_t pid_cvmfs;
658  vector<string> cvmfs_args;
659  cvmfs_args.push_back("-o");
660  cvmfs_args.push_back(JoinStrings(mount_options, ","));
661  cvmfs_args.push_back(fqrn);
662  cvmfs_args.push_back(mountpoint);
663  retval = ExecuteBinary(&fd_stdin, &fd_stdout, &fd_stderr, cvmfs_binary,
664  cvmfs_args, false, &pid_cvmfs);
665  if (!retval) {
666  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to launch %s",
667  cvmfs_binary.c_str());
668  return 32;
669  }
670  close(fd_stdin);
671 
672  // Print stdout / stderr and collect exit code
673  const int nfds = fd_stdout > fd_stderr ? fd_stdout + 1 : fd_stderr + 1;
674  fd_set readfds;
675  bool stdout_open = true;
676  bool stderr_open = true;
677  int status = 0;
678  int ended = false;
679 
680  do {
681  FD_ZERO(&readfds);
682  if (stdout_open)
683  FD_SET(fd_stdout, &readfds);
684  if (stderr_open)
685  FD_SET(fd_stderr, &readfds);
686 
687  struct timeval timeout;
688  timeout.tv_sec = 0;
689  timeout.tv_usec = 100;
690  retval = select(nfds, &readfds, NULL, NULL, &timeout);
691  if ((retval == -1) && (errno != EINTR)) {
692  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to pipe stdout/stderr");
693  return 32;
694  }
695 
696  char buf;
697  ssize_t num_bytes;
698  if (FD_ISSET(fd_stdout, &readfds)) {
699  num_bytes = read(fd_stdout, &buf, 1);
700  switch (num_bytes) {
701  case 0:
702  stdout_open = false;
703  break;
704  case 1:
706  break;
707  default:
708  LogCvmfs(kLogCvmfs, kLogStderr, "Failed reading stdout");
709  return 32;
710  }
711  }
712  if (FD_ISSET(fd_stderr, &readfds)) {
713  num_bytes = read(fd_stderr, &buf, 1);
714  switch (num_bytes) {
715  case 0:
716  stderr_open = false;
717  break;
718  case 1:
720  break;
721  default:
722  LogCvmfs(kLogCvmfs, kLogStderr, "Failed reading stderr");
723  return 32;
724  }
725  }
726 
727  // TODO(vvolkl): This block has seen several rounds of iterations,
728  // that tried to adress races and incorrect return codes.
729  // Should be looked at once again to make sure the cause of the
730  // race is understood, and if the timeout is really needed.
731 
732  ended = (waitpid(pid_cvmfs, &status, WNOHANG) == pid_cvmfs);
733  } while ((stdout_open || stderr_open) && !ended);
734  if (!ended) {
735  waitpid(pid_cvmfs, &status, 0);
736  }
737 
738  close(fd_stdout);
739  close(fd_stderr);
740 
741  if (WIFEXITED(status) && (WEXITSTATUS(status) == 0))
742  return 0;
743 
744  return 32;
745 }
void SetLogSyslogFacility(const int local_facility)
Definition: logging.cc:179
void SetLogSyslogLevel(const int level)
Definition: logging.cc:147
int MakeSocket(const std::string &path, const int mode)
Definition: posix.cc:327
struct stat64 platform_stat64
static bool CheckFuse()
Definition: mount.cvmfs.cc:91
static bool CheckConcurrentMount(const string &fqrn, const string &workspace, string *mountpointp)
Definition: mount.cvmfs.cc:160
static bool GetCacheDir(const string &fqrn, string *cachedir)
Definition: mount.cvmfs.cc:235
NameString GetFileName(const PathString &path)
Definition: shortstring.cc:28
static void Usage(const char *progname)
static bool CheckProxy()
Definition: mount.cvmfs.cc:149
string JoinStrings(const vector< string > &strings, const string &joint)
Definition: string.cc:356
bool IsOn(const std::string &param_value) const
Definition: options.cc:402
static bool WaitForReload(const std::string &mountpoint)
Definition: mount.cvmfs.cc:276
static std::string GetCvmfsBinary()
Definition: mount.cvmfs.cc:317
void SendMsg2Socket(const int fd, const std::string &msg)
Definition: posix.cc:671
static bool CheckStrictMount(const string &fqrn)
Definition: mount.cvmfs.cc:118
int platform_stat(const char *path, platform_stat64 *buf)
void SetLogMicroSyslog(const std::string &filename)
Definition: logging.cc:267
void ParseDefault(const std::string &fqrn)
Definition: options.cc:281
bool AddGroup2Persona(const gid_t gid)
Definition: posix.cc:1399
#define SetLogDebugFile(filename)
void SwitchTemplateManager(OptionsTemplateManager *opt_templ_mgr_param)
Definition: options.cc:91
bool SymlinkExists(const std::string &path)
Definition: posix.cc:833
bool IsValid(const std::string &input) const
Definition: sanitizer.cc:112
static int AttachMount(const std::string &mountpoint, const std::string &fqrn, int fuse_fd)
Definition: mount.cvmfs.cc:342
int main()
Definition: helper_allow.cc:16
bool FileExists(const std::string &path)
Definition: posix.cc:803
static void AddMountOption(const string &option, vector< string > *mount_options)
Definition: mount.cvmfs.cc:49
int64_t String2Int64(const string &value)
Definition: string.cc:234
vector< string > SplitString(const string &str, char delim)
Definition: string.cc:306
bool MkdirDeep(const std::string &path, const mode_t mode, bool verify_writable)
Definition: posix.cc:855
int LockFile(const std::string &path)
Definition: posix.cc:980
string StringifyInt(const int64_t value)
Definition: string.cc:77
bool GetGidOf(const std::string &groupname, gid_t *gid)
Definition: posix.cc:1365
static string MkFqrn(const string &repository)
Definition: mount.cvmfs.cc:55
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 GetValue(const std::string &key, std::string *value) const
Definition: options.cc:369
bool DirectoryExists(const std::string &path)
Definition: posix.cc:824
bool ExecuteBinary(int *fd_stdin, int *fd_stdout, int *fd_stderr, const std::string &binary_path, const std::vector< std::string > &argv, const bool double_fork, pid_t *child_pid)
Definition: posix.cc:1716
static int GetExistingFuseFd(const string &fqrn, const string &workspace, uid_t cvmfs_uid)
Definition: mount.cvmfs.cc:182
static bool GetCvmfsUser(string *cvmfs_user)
Definition: mount.cvmfs.cc:305
int ConnectSocket(const std::string &path)
Definition: posix.cc:422
string Base64(const string &data)
Definition: string.cc:537
BashOptionsManager options_manager_
Definition: mount.cvmfs.cc:34
uint64_t String2Uint64(const string &value)
Definition: string.cc:240
bool GetUidOf(const std::string &username, uid_t *uid, gid_t *main_gid)
Definition: posix.cc:1342
static bool GetWorkspace(const string &fqrn, string *workspace)
Definition: mount.cvmfs.cc:258
void SafeSleepMs(const unsigned ms)
Definition: posix.cc:2025
void SetLogSyslogPrefix(const std::string &prefix)
Definition: logging.cc:237
std::string MakeCanonicalPath(const std::string &path)
Definition: posix.cc:98
bool IsDefined(const std::string &key)
Definition: options.cc:363
bool IsMountPoint(const std::string &path)
Definition: posix.cc:264
int RecvFdFromSocket(int msg_fd)
Definition: posix.cc:720
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)
Definition: logging.cc:545