CernVM-FS  2.9.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
platform_linux.h
Go to the documentation of this file.
1 
7 #ifndef CVMFS_PLATFORM_LINUX_H_
8 #define CVMFS_PLATFORM_LINUX_H_
9 
10 #include <sys/types.h> // contains ssize_t needed inside <attr/xattr.h>
11 #include <sys/xattr.h>
12 #include <dirent.h>
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <limits.h>
16 #include <mntent.h>
17 #include <pthread.h>
18 #include <signal.h>
19 #include <sys/file.h>
20 #include <sys/mount.h>
21 #include <sys/prctl.h>
22 #include <sys/select.h>
23 #include <sys/stat.h>
24 #include <sys/utsname.h>
25 #include <unistd.h>
26 
27 #include <cassert>
28 #include <cstdio>
29 #include <cstdlib>
30 #include <cstring>
31 #include <ctime>
32 #include <string>
33 #include <vector>
34 
35 #ifdef CVMFS_ENABLE_INOTIFY
36 #include "file_watcher_inotify.h"
37 #else // CVMFS_ENABLE_INOTIFY
38 #include "file_watcher.h"
39 #endif // CVMFS_ENABLE_INOTIFY
40 #include "smalloc.h"
41 
42 #ifdef CVMFS_NAMESPACE_GUARD
43 namespace CVMFS_NAMESPACE_GUARD {
44 #endif
45 
46 #define platform_sighandler_t sighandler_t
47 
48 inline std::vector<std::string> platform_mountlist() {
49  std::vector<std::string> result;
50  FILE *fmnt = setmntent("/proc/mounts", "r");
51  struct mntent *mntbuf; // Static buffer managed by libc!
52  while ((mntbuf = getmntent(fmnt)) != NULL) {
53  result.push_back(mntbuf->mnt_dir);
54  }
55  endmntent(fmnt);
56  return result;
57 }
58 
59 // glibc < 2.11
60 #ifndef MNT_DETACH
61 #define MNT_DETACH 0x00000002
62 #endif
63 inline bool platform_umount(const char *mountpoint, const bool lazy) {
64  struct stat64 mtab_info;
65  int retval = lstat64(_PATH_MOUNTED, &mtab_info);
66  // If /etc/mtab exists and is not a symlink to /proc/mounts
67  if ((retval == 0) && S_ISREG(mtab_info.st_mode)) {
68  // Lock the modification on /etc/mtab against concurrent
69  // crash unmount handlers (removing the lock file would result in a race)
70  std::string lockfile = std::string(_PATH_MOUNTED) + ".cvmfslock";
71  const int fd_lockfile = open(lockfile.c_str(), O_RDONLY | O_CREAT, 0600);
72  if (fd_lockfile < 0) return false;
73  int timeout = 10;
74  while ((flock(fd_lockfile, LOCK_EX | LOCK_NB) != 0) && (timeout > 0)) {
75  if (errno != EWOULDBLOCK) {
76  close(fd_lockfile);
77  return false;
78  }
79  struct timeval wait_for;
80  wait_for.tv_sec = 1;
81  wait_for.tv_usec = 0;
82  select(0, NULL, NULL, NULL, &wait_for);
83  timeout--;
84  }
85  if (timeout <= 0) {
86  close(fd_lockfile);
87  return false;
88  }
89 
90  // Remove entry from /etc/mtab (create new file without entry)
91  std::string mntnew = std::string(_PATH_MOUNTED) + ".cvmfstmp";
92  FILE *fmntold = setmntent(_PATH_MOUNTED, "r");
93  if (!fmntold) {
94  flock(fd_lockfile, LOCK_UN);
95  close(fd_lockfile);
96  return false;
97  }
98  FILE *fmntnew = setmntent(mntnew.c_str(), "w+");
99  if (!fmntnew && (chmod(mntnew.c_str(), mtab_info.st_mode) != 0) &&
100  (chown(mntnew.c_str(), mtab_info.st_uid, mtab_info.st_gid) != 0)) {
101  endmntent(fmntold);
102  flock(fd_lockfile, LOCK_UN);
103  close(fd_lockfile);
104  return false;
105  }
106  struct mntent *mntbuf; // Static buffer managed by libc!
107  while ((mntbuf = getmntent(fmntold)) != NULL) {
108  if (strcmp(mntbuf->mnt_dir, mountpoint) != 0) {
109  retval = addmntent(fmntnew, mntbuf);
110  if (retval != 0) {
111  endmntent(fmntold);
112  endmntent(fmntnew);
113  unlink(mntnew.c_str());
114  flock(fd_lockfile, LOCK_UN);
115  close(fd_lockfile);
116  return false;
117  }
118  }
119  }
120  endmntent(fmntold);
121  endmntent(fmntnew);
122  retval = rename(mntnew.c_str(), _PATH_MOUNTED);
123  flock(fd_lockfile, LOCK_UN);
124  close(fd_lockfile);
125  if (retval != 0) return false;
126  // Best effort
127  retval = chmod(_PATH_MOUNTED, mtab_info.st_mode);
128  (void) retval;
129  retval = chown(_PATH_MOUNTED, mtab_info.st_uid, mtab_info.st_gid);
130  (void) retval;
131  // We pickup these values only to silent warnings
132  }
133 
134  int flags = lazy ? MNT_DETACH : 0;
135  retval = umount2(mountpoint, flags);
136  return retval == 0;
137 }
138 
139 inline bool platform_umount_lazy(const char *mountpoint) {
140  int retval = umount2(mountpoint, MNT_DETACH);
141  return retval == 0;
142 }
143 
147 typedef pthread_spinlock_t platform_spinlock;
148 
149 inline int platform_spinlock_init(platform_spinlock *lock, int pshared) {
150  return pthread_spin_init(lock, pshared);
151 }
152 
154  return pthread_spin_destroy(lock);
155 }
156 
158  return pthread_spin_trylock(lock);
159 }
160 
162  pthread_spin_unlock(lock);
163 }
164 
168 inline pthread_t platform_gettid() { return pthread_self(); }
169 
170 inline int platform_sigwait(const int signum) {
171  sigset_t sigset;
172  int retval = sigemptyset(&sigset);
173  assert(retval == 0);
174  retval = sigaddset(&sigset, signum);
175  assert(retval == 0);
176  retval = sigwaitinfo(&sigset, NULL);
177  return retval;
178 }
179 
187 inline bool platform_allow_ptrace(const pid_t pid) {
188 #ifdef PR_SET_PTRACER
189  // On Ubuntu, yama prevents all processes from ptracing other processes, even
190  // when they are owned by the same user. Therefore the watchdog would not be
191  // able to create a stacktrace, without this extra permission.
192  const int retval = prctl(PR_SET_PTRACER, pid, 0, 0, 0);
193  // On some platforms (e.g. CentOS7), PR_SET_PTRACER is defined but not
194  // supported by the kernel. That's fine and we don't have to care about it
195  // when it happens.
196  return (retval == 0) || (errno == EINVAL);
197 #else
198  // On other platforms this is currently a no-op
199  return true;
200 #endif
201 }
202 
206 typedef struct dirent64 platform_dirent64;
207 
209  return readdir64(dirp);
210 }
211 
212 typedef struct stat64 platform_stat64;
213 
214 inline int platform_stat(const char *path, platform_stat64 *buf) {
215  return stat64(path, buf);
216 }
217 
218 inline int platform_lstat(const char *path, platform_stat64 *buf) {
219  return lstat64(path, buf);
220 }
221 
222 inline int platform_fstat(int filedes, platform_stat64 *buf) {
223  return fstat64(filedes, buf);
224 }
225 
226 // TODO(jblomer): the translation from C to C++ should be done elsewhere
227 inline bool platform_getxattr(const std::string &path, const std::string &name,
228  std::string *value) {
229  ssize_t size = 0;
230  void *buffer = NULL;
231  ssize_t retval;
232  retval = getxattr(path.c_str(), name.c_str(), buffer, size);
233  if (retval > 1) {
234  size = retval;
235  buffer = smalloc(size);
236  retval = getxattr(path.c_str(), name.c_str(), buffer, size);
237  }
238  if ((retval < 0) || (retval > size)) {
239  free(buffer);
240  return false;
241  }
242  if (retval > 0) {
243  value->assign(static_cast<const char *>(buffer), size);
244  free(buffer);
245  } else {
246  value->assign("");
247  }
248  return true;
249 }
250 
251 // TODO(jblomer): the translation from C to C++ should be done elsewhere
252 inline bool platform_setxattr(const std::string &path, const std::string &name,
253  const std::string &value) {
254  int retval =
255  setxattr(path.c_str(), name.c_str(), value.c_str(), value.size(), 0);
256  return retval == 0;
257 }
258 
259 inline bool platform_lsetxattr(const std::string &path, const std::string &name,
260  const std::string &value) {
261  int retval =
262  lsetxattr(path.c_str(), name.c_str(), value.c_str(), value.size(), 0);
263  return retval == 0;
264 }
265 
266 inline ssize_t platform_lgetxattr(const char *path, const char *name,
267  void *value, size_t size) {
268  return lgetxattr(path, name, value, size);
269 }
270 
271 inline ssize_t platform_llistxattr(const char *path, char *list, size_t size) {
272  return llistxattr(path, list, size);
273 }
274 
275 inline void platform_disable_kcache(int filedes) {
276  (void)posix_fadvise(filedes, 0, 0, POSIX_FADV_RANDOM | POSIX_FADV_NOREUSE);
277 }
278 
279 inline ssize_t platform_readahead(int filedes) {
280  return readahead(filedes, 0, static_cast<size_t>(-1));
281 }
282 
306 inline int platform_invalidate_kcache(const int fd, const off_t offset,
307  const off_t length) {
308  return posix_fadvise(fd, offset, length, POSIX_FADV_DONTNEED);
309 }
310 
311 inline std::string platform_libname(const std::string &base_name) {
312  return "lib" + base_name + ".so";
313 }
314 
315 inline std::string platform_getexepath() {
316  char buf[PATH_MAX + 1];
317  ssize_t ret = readlink("/proc/self/exe", buf, PATH_MAX);
318  if (ret > 0) {
319  buf[ret] = '\0';
320  return std::string(buf);
321  }
322  return "";
323 }
324 
325 inline struct timespec platform_time_with_clock(int clock) {
326  struct timespec tp;
327  int retval = clock_gettime(clock, &tp);
328  assert(retval == 0);
329  return tp;
330 }
331 
332 inline uint64_t platform_monotonic_time() {
333 #ifdef CLOCK_MONOTONIC_COARSE
334  struct timespec tp = platform_time_with_clock(CLOCK_MONOTONIC_COARSE);
335 #else
336  struct timespec tp = platform_time_with_clock(CLOCK_MONOTONIC);
337 #endif
338  return tp.tv_sec + (tp.tv_nsec >= 500000000);
339 }
340 
341 inline uint64_t platform_monotonic_time_ns() {
342  struct timespec tp = platform_time_with_clock(CLOCK_MONOTONIC);
343  return static_cast<uint64_t>(static_cast<double>(tp.tv_sec) * 1e9 +
344  static_cast<double>(tp.tv_nsec));
345 }
346 
347 inline uint64_t platform_realtime_ns() {
348  struct timespec tp = platform_time_with_clock(CLOCK_REALTIME);
349  return static_cast<uint64_t>(static_cast<double>(tp.tv_sec) * 1e9 +
350  static_cast<double>(tp.tv_nsec));
351 }
352 
353 inline uint64_t platform_memsize() {
354  return static_cast<uint64_t>(sysconf(_SC_PHYS_PAGES)) *
355  static_cast<uint64_t>(sysconf(_SC_PAGE_SIZE));
356 }
357 
359 #ifdef CVMFS_ENABLE_INOTIFY
361 #else // CVMFS_ENABLE_INOTIFY
362  return NULL;
363 #endif // CVMFS_ENABLE_INOTIFY
364 }
365 
366 #ifdef CVMFS_NAMESPACE_GUARD
367 } // namespace CVMFS_NAMESPACE_GUARD
368 #endif
369 
370 #endif // CVMFS_PLATFORM_LINUX_H_
struct stat64 platform_stat64
#define MNT_DETACH
struct timespec platform_time_with_clock(int clock)
pthread_t platform_gettid()
int platform_spinlock_init(platform_spinlock *lock, int pshared)
assert((mem||(size==0))&&"Out Of Memory")
uint64_t platform_monotonic_time_ns()
bool platform_umount_lazy(const char *mountpoint)
bool platform_umount(const char *mountpoint, const bool lazy)
int platform_stat(const char *path, platform_stat64 *buf)
std::string platform_getexepath()
file_watcher::FileWatcher * platform_file_watcher()
ssize_t platform_llistxattr(const char *path, char *list, size_t size)
bool platform_allow_ptrace(const pid_t pid)
uint64_t platform_memsize()
pthread_spinlock_t platform_spinlock
int platform_spinlock_trylock(platform_spinlock *lock)
int platform_invalidate_kcache(const int fd, const off_t offset, const off_t length)
bool platform_getxattr(const std::string &path, const std::string &name, std::string *value)
bool platform_setxattr(const std::string &path, const std::string &name, const std::string &value)
ssize_t platform_readahead(int filedes)
uint64_t platform_realtime_ns()
int platform_sigwait(const int signum)
ssize_t platform_lgetxattr(const char *path, const char *name, void *value, size_t size)
std::vector< std::string > platform_mountlist()
int platform_lstat(const char *path, platform_stat64 *buf)
uint64_t platform_monotonic_time()
std::string platform_libname(const std::string &base_name)
void platform_disable_kcache(int filedes)
void platform_spinlock_unlock(platform_spinlock *lock)
platform_dirent64 * platform_readdir(DIR *dirp)
int platform_spinlock_destroy(platform_spinlock *lock)
int platform_fstat(int filedes, platform_stat64 *buf)
static void size_t size
Definition: smalloc.h:47
bool platform_lsetxattr(const std::string &path, const std::string &name, const std::string &value)
struct dirent64 platform_dirent64