CernVM-FS  2.12.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 {
52  mount_options->push_back(option);
53 }
54 
55 
56 static string MkFqrn(const string &repository) {
57  const string::size_type idx = repository.find_last_of('.');
58  if (idx == string::npos) {
59  string domain;
60  bool retval = 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) / sizeof(fuseTComponentsPaths[0]);
78  platform_stat64 info;
79  for (int idx = 0; idx < pathsNumber; ++idx) {
80  bzero(&info, sizeof(platform_stat64));
81  int retval = platform_stat(fuseTComponentsPaths[idx].c_str(), &info);
82  if ((retval != 0) || !S_ISREG(info.st_mode)) {
83  return false;
84  }
85  }
86  return true;
87 }
88 #endif
89 
90 static bool CheckFuse() {
91 #if defined(__APPLE__) && !defined(USE_MACFUSE_KEXT)
92  bool is_fuse_t_installed = IsFuseTInstalled();
93  if (!is_fuse_t_installed) {
94  LogCvmfs(kLogCvmfs, kLogStderr, "FUSE-T installation check failed. FUSE not loaded");
95  }
96  return is_fuse_t_installed;
97 #else
98  string fuse_device;
99  int retval;
100 #ifdef __APPLE__
101  fuse_device = "/dev/macfuse0";
102 #else
103  fuse_device = "/dev/fuse";
104 #endif
105  platform_stat64 info;
106  retval = platform_stat(fuse_device.c_str(), &info);
107  if ((retval != 0) || !S_ISCHR(info.st_mode)) {
108  LogCvmfs(kLogCvmfs, kLogStderr, "Fuse not loaded");
109  return false;
110  }
111  return true;
112 #endif
113 }
114 
115 
116 static bool CheckStrictMount(const string &fqrn) {
117  string param_strict_mount;
118  if (options_manager_.GetValue("CVMFS_STRICT_MOUNT", &param_strict_mount) &&
119  options_manager_.IsOn(param_strict_mount))
120  {
121  string repository_list;
122  bool retval =
123  options_manager_.GetValue("CVMFS_REPOSITORIES", &repository_list);
124  if (!retval) {
125  LogCvmfs(kLogCvmfs, kLogStderr, "CVMFS_REPOSITORIES missing");
126  return false;
127  }
128  vector<string> repositories = SplitString(repository_list, ',');
129  for (unsigned i = 0; i < repositories.size(); ++i) {
130  if (MkFqrn(repositories[i]) == fqrn)
131  return true;
132  }
133  string config_repository;
134  retval =
135  options_manager_.GetValue("CVMFS_CONFIG_REPOSITORY", &config_repository);
136  if (retval && (config_repository == fqrn))
137  return true;
138  LogCvmfs(kLogCvmfs, kLogStderr, "Not allowed to mount %s, "
139  "add it to CVMFS_REPOSITORIES", fqrn.c_str());
140  return false;
141  }
142  return true;
143 }
144 
145 
146 static bool CheckProxy() {
147  string param;
148  int retval = options_manager_.GetValue("CVMFS_HTTP_PROXY", &param);
149  if (!retval || param.empty()) {
150  LogCvmfs(kLogCvmfs, kLogStderr, "CVMFS_HTTP_PROXY required");
151  return false;
152  }
153  return true;
154 }
155 
156 
157 static bool CheckConcurrentMount(const string &fqrn,
158  const string &workspace,
159  string *mountpointp) {
160  LockFile(workspace + "/cvmfs_io." + fqrn + ".mountcheck.lock");
161  // Try connecting to cvmfs_io socket
162  int socket_fd = ConnectSocket(workspace + "/cvmfs_io." + fqrn);
163  if (socket_fd < 0)
164  return false;
165 
166  // There is a repository mounted, find out the mount point
167  SendMsg2Socket(socket_fd, "mountpoint");
168  string mountpoint;
169  char buf;
170  while (read(socket_fd, &buf, 1) == 1) {
171  if (buf != '\n')
172  mountpoint.push_back(buf);
173  }
174  *mountpointp = mountpoint;
175  close(socket_fd);
176  return true;
177 }
178 
179 static int GetExistingFuseFd(
180  const string &fqrn, const string &workspace, uid_t cvmfs_uid)
181 {
182  // Try connecting to cvmfs_io socket
183  int talk_fd = ConnectSocket(workspace + "/cvmfs_io." + fqrn);
184  if (talk_fd < 0)
185  return -1;
186 
187  // Create temporary socket
188  std::string recv_sock_dir = CreateTempDir(workspace + "/fusefd");
189  if (recv_sock_dir.empty() || (chmod(recv_sock_dir.c_str(), 0755) != 0)) {
190  close(talk_fd);
191  return -1;
192  }
193  std::string recv_sock_path = recv_sock_dir + "/sock";
194  int recv_sock_fd = MakeSocket(recv_sock_path, 0660);
195  if ((recv_sock_fd < 0) ||
196  (chown(recv_sock_path.c_str(), cvmfs_uid, getegid()) != 0))
197  {
198  if (recv_sock_fd >= 0)
199  close(recv_sock_fd);
200  close(talk_fd);
201  unlink(recv_sock_path.c_str());
202  rmdir(recv_sock_dir.c_str());
203  return -1;
204  }
205  listen(recv_sock_fd, 1);
206 
207  // Trigger fd transfer
208  SendMsg2Socket(talk_fd, "send mount fd " + recv_sock_path);
209  string result;
210  char buf;
211  while (read(talk_fd, &buf, 1) == 1) {
212  if (buf != '\n')
213  result.push_back(buf);
214  }
215  close(talk_fd);
216 
217  int fuse_fd = -1;
218  if (result == "OK") {
219  struct sockaddr_un addr;
220  unsigned int len = sizeof(addr);
221  int con_fd =
222  accept(recv_sock_fd, reinterpret_cast<struct sockaddr *>(&addr), &len);
223  fuse_fd = RecvFdFromSocket(con_fd);
224  close(con_fd);
225  }
226  close(recv_sock_fd);
227  unlink(recv_sock_path.c_str());
228  rmdir(recv_sock_dir.c_str());
229 
230  return fuse_fd;
231 }
232 
233 
234 static bool GetCacheDir(const string &fqrn, string *cachedir) {
235  string param;
236  bool retval = options_manager_.GetValue("CVMFS_CACHE_DIR", &param);
237  if (retval) {
238  *cachedir = MakeCanonicalPath(param);
239  return true;
240  }
241 
242  retval = options_manager_.GetValue("CVMFS_CACHE_BASE", &param);
243  if (!retval)
244  return false;
245 
246  *cachedir = MakeCanonicalPath(param);
247  if (options_manager_.GetValue("CVMFS_SHARED_CACHE", &param) &&
248  options_manager_.IsOn(param))
249  {
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  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  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  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 {
345 #ifdef __APPLE__
346  (void) mountpoint;
347  (void) fqrn;
348  (void) fuse_fd;
349  return 1;
350 #else
351  platform_stat64 info;
352  int retval = platform_stat(mountpoint.c_str(), &info);
353  if (retval != 0)
354  return 1;
355 
356  char mntopt[100];
357  snprintf(mntopt, sizeof(mntopt),
358  "ro,fd=%i,rootmode=%o,user_id=%d,group_id=%d",
359  fuse_fd, info.st_mode & S_IFMT, geteuid(), getegid());
360  // TODO(jblomer): remove NOSUID according to options
361  retval = mount("cvmfs2", mountpoint.c_str(), "fuse",
362  MS_NODEV | MS_RDONLY | MS_NOSUID, mntopt);
363  if (retval != 0) {
365  "Cannot attach to existing fuse module (%d)", errno);
366  return 1;
367  }
369  "CernVM-FS: linking %s to repository %s (attaching)",
370  mountpoint.c_str(), fqrn.c_str());
371  return 0;
372 #endif
373 }
374 
375 
376 int main(int argc, char **argv) {
377  bool dry_run = false;
378  bool remount = false;
379  vector<string> mount_options;
380 
381  // Option parsing
382  vector<string> option_tokens;
383  int c;
384  while ((c = getopt(argc, argv, "vfnho:")) != -1) {
385  switch (c) {
386  case 'f':
387  dry_run = true;
388  break;
389  case 'n':
390  LogCvmfs(kLogCvmfs, kLogStdout, "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  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  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) return 1;
454  retval = GetWorkspace(fqrn, &workspace);
455  if (!retval) return 1;
456  retval = GetCacheDir(fqrn, &cachedir);
457  dedicated_cachedir = (retval && (cachedir != workspace));
458  retval = GetCvmfsUser(&cvmfs_user);
459  if (!retval) return 1;
460  retval = CheckFuse();
461  if (!retval) return 1;
462  retval = CheckStrictMount(fqrn);
463  if (!retval) return 1;
464  retval = CheckProxy();
465  if (!retval) return 1;
466 
467  // Retrieve cvmfs uid/gid and fuse gid if exists
468  uid_t uid_cvmfs;
469  gid_t gid_cvmfs;
470  gid_t gid_fuse;
471  bool has_fuse_group = false;
472  retval = GetUidOf(cvmfs_user, &uid_cvmfs, &gid_cvmfs);
473  if (!retval) {
474  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to find user %s in passwd database",
475  cvmfs_user.c_str());
476  return 1;
477  }
478  has_fuse_group = GetGidOf("fuse", &gid_fuse);
479 
480  // Prepare workspace
481  retval = MkdirDeep(workspace, 0755, false);
482  if (!retval) {
483  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to create workspace %s",
484  workspace.c_str());
485  return 1;
486  }
487 
488  // This is not a sure thing. When the CVMFS_CACHE_BASE parameter is changed
489  // two repositories can get mounted concurrently (but that should not hurt).
490  // If the same repository is mounted multiple times at the same time, there
491  // is a race here. Eventually, only one repository will be mounted while the
492  // other cvmfs processes block on a file lock in the cache.
493  string prev_mountpoint;
494  retval = CheckConcurrentMount(fqrn, workspace, &prev_mountpoint);
495  if (retval) {
496  if (remount && (mountpoint == prev_mountpoint)) {
497  // Actually remounting is too hard, but pretend that it worked
498  return 0;
499  }
500  // Identify zombie fuse processes that are held open by other mount
501  // namespaces
502  if ((mountpoint == prev_mountpoint) && !IsMountPoint(mountpoint)) {
503  // Allow for group access to the socket receiving the fuse fd
504  umask(007);
505  int fuse_fd = GetExistingFuseFd(fqrn, workspace, uid_cvmfs);
506  if (fuse_fd < 0) {
508  "Cannot connect to existing fuse module");
509  return 1;
510  }
511  return AttachMount(mountpoint, fqrn, fuse_fd);
512  }
513  LogCvmfs(kLogCvmfs, kLogStderr, "Repository %s is already mounted on %s",
514  fqrn.c_str(), prev_mountpoint.c_str());
515  return 1;
516  } else {
517  // No double mount
518  if (remount) {
519  LogCvmfs(kLogCvmfs, kLogStderr, "Repository %s is not mounted on %s",
520  fqrn.c_str(), mountpoint.c_str());
521  return 1;
522  }
523  }
524 
525  // Prepare cache directory
526  if (dedicated_cachedir) {
527  retval = MkdirDeep(cachedir, 0755, false);
528  if (!retval) {
529  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to create cache directory %s",
530  cachedir.c_str());
531  return 1;
532  }
533  }
534  retval = MkdirDeep("/var/run/cvmfs", 0755);
535  if (!retval) {
536  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to create socket directory");
537  return 1;
538  }
539  sysret = chown(workspace.c_str(), uid_cvmfs, getegid());
540  if (sysret != 0) {
541  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to transfer ownership of %s to %s",
542  workspace.c_str(), cvmfs_user.c_str());
543  return 1;
544  }
545  if (dedicated_cachedir) {
546  sysret = chown(cachedir.c_str(), uid_cvmfs, getegid());
547  if (sysret != 0) {
549  "Failed to transfer ownership of %s to %s",
550  cachedir.c_str(), cvmfs_user.c_str());
551  return 1;
552  }
553  }
554  sysret = chown("/var/run/cvmfs", uid_cvmfs, getegid());
555  if (sysret != 0) {
556  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to transfer ownership of %s to %s",
557  "/var/run/cvmfs", cvmfs_user.c_str());
558  return 1;
559  }
560 
561  // Set maximum number of files
562 #ifdef __APPLE__
563  string param;
564  if (options_manager_.GetValue("CVMFS_NFILES", &param)) {
565  sanitizer::IntegerSanitizer integer_sanitizer;
566  if (!integer_sanitizer.IsValid(param)) {
567  LogCvmfs(kLogCvmfs, kLogStderr, "Invalid CVMFS_NFILES: %s",
568  param.c_str());
569  return 1;
570  }
571  int nfiles = String2Uint64(param);
572  if ((nfiles < 128) || (nfiles > 524288)) {
573  LogCvmfs(kLogCvmfs, kLogStderr, "Invalid CVMFS_NFILES: %s",
574  param.c_str());
575  return 1;
576  }
577  int nfiles_all = nfiles + 512;
578  int sys_nfiles, sys_nfiles_all;
579  size_t len = sizeof(sys_nfiles);
580  int mib[2];
581  mib[0] = CTL_KERN;
582  mib[1] = KERN_MAXFILESPERPROC;
583  retval = sysctl(mib, 2, &sys_nfiles, &len, NULL, 0);
584  if ((retval != 0) || (sys_nfiles < 0)) {
585  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to get KERN_MAXFILESPERPROC");
586  return 1;
587  }
588  if (sys_nfiles < nfiles) {
589  mib[1] = KERN_MAXFILES;
590  retval = sysctl(mib, 2, &sys_nfiles_all, &len, NULL, 0);
591  if ((retval != 0) || (sys_nfiles_all < 0)) {
592  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to get KERN_MAXFILES");
593  return 1;
594  }
595  if (sys_nfiles_all < nfiles_all) {
596  retval = sysctl(mib, 2, NULL, NULL, &nfiles_all, sizeof(nfiles_all));
597  if (retval != 0) {
598  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to set KERN_MAXFILES");
599  return 1;
600  }
601  }
602  mib[1] = KERN_MAXFILESPERPROC;
603  retval = sysctl(mib, 2, NULL, NULL, &nfiles_all, sizeof(nfiles_all));
604  if (retval != 0) {
605  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to set KERN_MAXFILESPERPROC");
606  return 1;
607  }
608  }
609  }
610 #endif
611 
612  AddMountOption("system_mount", &mount_options);
613  AddMountOption("fsname=cvmfs2", &mount_options);
614  AddMountOption("allow_other", &mount_options);
615  AddMountOption("grab_mountpoint", &mount_options);
616  AddMountOption("uid=" + StringifyInt(uid_cvmfs), &mount_options);
617  AddMountOption("gid=" + StringifyInt(gid_cvmfs), &mount_options);
618  if (options_manager_.IsDefined("CVMFS_DEBUGLOG"))
619  AddMountOption("debug", &mount_options);
620 
621  const string cvmfs_binary = GetCvmfsBinary();
622  if (cvmfs_binary.empty()) {
623  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to locate the cvmfs2 binary");
624  return 1;
625  }
626 
627  // Dry run early exit
628  if (dry_run) {
629  string cmd = cvmfs_binary + " -o " + JoinStrings(mount_options, ",") +
630  " " + fqrn + " " + mountpoint;
631  if (has_fuse_group) {
632  cmd = "sg fuse -c \"" + cmd + "\"";
633  }
634  LogCvmfs(kLogCvmfs, kLogStdout, "%s", cmd.c_str());
635  return 0;
636  }
637 
638  // Real mount, add supplementary fuse group
639  if (has_fuse_group) {
640  retval = AddGroup2Persona(gid_fuse);
641  if (!retval) {
642  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to add fuse to "
643  " the list of supplementary groups");
644  return 1;
645  }
646  }
647 
648  // Exec cvmfs2, from here on errors return 32 (mount error)
649  int fd_stdin, fd_stdout, fd_stderr;
650  pid_t pid_cvmfs;
651  vector<string> cvmfs_args;
652  cvmfs_args.push_back("-o");
653  cvmfs_args.push_back(JoinStrings(mount_options, ","));
654  cvmfs_args.push_back(fqrn);
655  cvmfs_args.push_back(mountpoint);
656  retval = ExecuteBinary(&fd_stdin, &fd_stdout, &fd_stderr,
657  cvmfs_binary, cvmfs_args, false, &pid_cvmfs);
658  if (!retval) {
659  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to launch %s",
660  cvmfs_binary.c_str());
661  return 32;
662  }
663  close(fd_stdin);
664 
665  // Print stdout / stderr and collect exit code
666  int nfds = fd_stdout > fd_stderr ? fd_stdout + 1 : fd_stderr + 1;
667  fd_set readfds;
668  bool stdout_open = true;
669  bool stderr_open = true;
670  int status = 0;
671  int ended = false;
672 
673  do {
674  FD_ZERO(&readfds);
675  if (stdout_open) FD_SET(fd_stdout, &readfds);
676  if (stderr_open) FD_SET(fd_stderr, &readfds);
677 
678  struct timeval timeout;
679  timeout.tv_sec = 0;
680  timeout.tv_usec = 100;
681  retval = select(nfds, &readfds, NULL, NULL, &timeout);
682  if ((retval == -1) && (errno != EINTR)) {
683  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to pipe stdout/stderr");
684  return 32;
685  }
686 
687  char buf;
688  ssize_t num_bytes;
689  if (FD_ISSET(fd_stdout, &readfds)) {
690  num_bytes = read(fd_stdout, &buf, 1);
691  switch (num_bytes) {
692  case 0:
693  stdout_open = false;
694  break;
695  case 1:
697  break;
698  default:
699  LogCvmfs(kLogCvmfs, kLogStderr, "Failed reading stdout");
700  return 32;
701  }
702  }
703  if (FD_ISSET(fd_stderr, &readfds)) {
704  num_bytes = read(fd_stderr, &buf, 1);
705  switch (num_bytes) {
706  case 0:
707  stderr_open = false;
708  break;
709  case 1:
711  break;
712  default:
713  LogCvmfs(kLogCvmfs, kLogStderr, "Failed reading stderr");
714  return 32;
715  }
716  }
717 
718  // TODO(vvolkl): This block has seen several rounds of iterations,
719  // that tried to adress races and incorrect return codes.
720  // Should be looked at once again to make sure the cause of the
721  // race is understood, and if the timeout is really needed.
722 
723  ended = (waitpid(pid_cvmfs, &status, WNOHANG) == pid_cvmfs);
724  } while ((stdout_open || stderr_open) && !ended);
725  if (!ended) {
726  waitpid(pid_cvmfs, &status, 0);
727  }
728 
729  close(fd_stdout);
730  close(fd_stderr);
731 
732  if (WIFEXITED(status) && (WEXITSTATUS(status) == 0))
733  return 0;
734 
735  return 32;
736 }
void SetLogSyslogFacility(const int local_facility)
Definition: logging.cc:183
void SetLogSyslogLevel(const int level)
Definition: logging.cc:151
int MakeSocket(const std::string &path, const int mode)
Definition: posix.cc:331
struct stat64 platform_stat64
static bool CheckFuse()
Definition: mount.cvmfs.cc:90
static bool CheckConcurrentMount(const string &fqrn, const string &workspace, string *mountpointp)
Definition: mount.cvmfs.cc:157
static bool GetCacheDir(const string &fqrn, string *cachedir)
Definition: mount.cvmfs.cc:234
NameString GetFileName(const PathString &path)
Definition: shortstring.cc:29
static void Usage(const char *progname)
static bool CheckProxy()
Definition: mount.cvmfs.cc:146
string JoinStrings(const vector< string > &strings, const string &joint)
Definition: string.cc:343
bool IsOn(const std::string &param_value) const
Definition: options.cc:409
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:670
static bool CheckStrictMount(const string &fqrn)
Definition: mount.cvmfs.cc:116
int platform_stat(const char *path, platform_stat64 *buf)
void SetLogMicroSyslog(const std::string &filename)
Definition: logging.cc:272
void ParseDefault(const std::string &fqrn)
Definition: options.cc:282
bool AddGroup2Persona(const gid_t gid)
Definition: posix.cc:1412
#define SetLogDebugFile(filename)
void SwitchTemplateManager(OptionsTemplateManager *opt_templ_mgr_param)
Definition: options.cc:92
bool SymlinkExists(const std::string &path)
Definition: posix.cc:834
bool IsValid(const std::string &input) const
Definition: sanitizer.cc:114
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:802
static void AddMountOption(const string &option, vector< string > *mount_options)
Definition: mount.cvmfs.cc:49
int64_t String2Int64(const string &value)
Definition: string.cc:240
vector< string > SplitString(const string &str, char delim)
Definition: string.cc:308
bool MkdirDeep(const std::string &path, const mode_t mode, bool verify_writable)
Definition: posix.cc:857
int LockFile(const std::string &path)
Definition: posix.cc:982
string StringifyInt(const int64_t value)
Definition: string.cc:78
bool GetGidOf(const std::string &groupname, gid_t *gid)
Definition: posix.cc:1378
static string MkFqrn(const string &repository)
Definition: mount.cvmfs.cc:56
std::string CreateTempDir(const std::string &path_prefix)
Definition: posix.cc:1058
bool HasPrefix(const string &str, const string &prefix, const bool ignore_case)
Definition: string.cc:285
bool GetValue(const std::string &key, std::string *value) const
Definition: options.cc:376
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:1733
static int GetExistingFuseFd(const string &fqrn, const string &workspace, uid_t cvmfs_uid)
Definition: mount.cvmfs.cc:179
static bool GetCvmfsUser(string *cvmfs_user)
Definition: mount.cvmfs.cc:305
int ConnectSocket(const std::string &path)
Definition: posix.cc:427
string Base64(const string &data)
Definition: string.cc:522
BashOptionsManager options_manager_
Definition: mount.cvmfs.cc:34
uint64_t String2Uint64(const string &value)
Definition: string.cc:246
bool GetUidOf(const std::string &username, uid_t *uid, gid_t *main_gid)
Definition: posix.cc:1355
static bool GetWorkspace(const string &fqrn, string *workspace)
Definition: mount.cvmfs.cc:258
void SafeSleepMs(const unsigned ms)
Definition: posix.cc:2049
void SetLogSyslogPrefix(const std::string &prefix)
Definition: logging.cc:241
std::string MakeCanonicalPath(const std::string &path)
Definition: posix.cc:98
bool IsDefined(const std::string &key)
Definition: options.cc:370
bool IsMountPoint(const std::string &path)
Definition: posix.cc:266
int RecvFdFromSocket(int msg_fd)
Definition: posix.cc:719
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)
Definition: logging.cc:528