initial code repo
[stor4nfv.git] / src / ceph / src / global / pidfile.cc
diff --git a/src/ceph/src/global/pidfile.cc b/src/ceph/src/global/pidfile.cc
new file mode 100644 (file)
index 0000000..14e5871
--- /dev/null
@@ -0,0 +1,241 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation.  See file COPYING.
+ *
+ */
+
+#include "common/debug.h"
+#include "common/errno.h"
+#include "common/safe_io.h"
+#include "global/pidfile.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#if defined(__FreeBSD__)
+#include <sys/param.h>
+#endif
+
+#include "include/compat.h"
+
+//
+// derr can be used for functions exclusively called from pidfile_write
+//
+// cerr must be used for functions called by pidfile_remove because
+// logging is not functional when it is called. cerr output is lost
+// when the caller is daemonized but it will show if not (-f)
+//
+#define dout_context g_ceph_context
+#define dout_prefix *_dout
+#define dout_subsys ceph_subsys_
+
+struct pidfh {
+  int pf_fd;
+  char pf_path[PATH_MAX + 1];
+  dev_t pf_dev;
+  ino_t pf_ino;
+
+  pidfh() {
+    reset();
+  }
+  ~pidfh() {
+    remove();
+  }
+
+  bool is_open() {
+    return pf_path[0] != '\0' && pf_fd != -1;
+  }
+  void reset() {
+    pf_fd = -1;
+    memset(pf_path, 0, sizeof(pf_path));
+    pf_dev = 0;
+    pf_ino = 0;
+  }
+  int verify();
+  int remove();
+  int open(const md_config_t *conf);
+  int write();
+};
+
+static pidfh *pfh = nullptr;
+
+int pidfh::verify() {
+  // check that the file we opened still is the same
+  if (pf_fd == -1)
+    return -EINVAL;
+  struct stat st;
+  if (stat(pf_path, &st) == -1)
+    return -errno;
+  if (st.st_dev != pf_dev || st.st_ino != pf_ino)
+    return -ESTALE;
+  return 0;
+}
+
+int pidfh::remove()
+{
+  if (!pf_path[0])
+    return 0;
+
+  int ret;
+  if ((ret = verify()) < 0) {
+    if (pf_fd != -1) {
+      ::close(pf_fd);
+      reset();
+    }
+    return ret;
+  }
+
+  // seek to the beginning of the file before reading
+  ret = ::lseek(pf_fd, 0, SEEK_SET);
+  if (ret < 0) {
+    std::cerr << __func__ << " lseek failed "
+             << cpp_strerror(errno) << std::endl;
+    return -errno;
+  }
+
+  // check that the pid file still has our pid in it
+  char buf[32];
+  memset(buf, 0, sizeof(buf));
+  ssize_t res = safe_read(pf_fd, buf, sizeof(buf));
+  ::close(pf_fd);
+  if (res < 0) {
+    std::cerr << __func__ << " safe_read failed "
+             << cpp_strerror(-res) << std::endl;
+    return res;
+  }
+
+  int a = atoi(buf);
+  if (a != getpid()) {
+    std::cerr << __func__ << " the pid found in the file is "
+             << a << " which is different from getpid() "
+             << getpid() << std::endl;
+    return -EDOM;
+  }
+  ret = ::unlink(pf_path);
+  if (ret < 0) {
+    std::cerr << __func__ << " unlink " << pf_path << " failed "
+             << cpp_strerror(errno) << std::endl;
+    return -errno;
+  }
+  reset();
+  return 0;
+}
+
+int pidfh::open(const md_config_t *conf)
+{
+  int len = snprintf(pf_path, sizeof(pf_path),
+                   "%s", conf->pid_file.c_str());
+
+  if (len >= (int)sizeof(pf_path))
+    return -ENAMETOOLONG;
+
+  int fd;
+  fd = ::open(pf_path, O_CREAT|O_RDWR, 0644);
+  if (fd < 0) {
+    int err = errno;
+    derr << __func__ << ": failed to open pid file '"
+        << pf_path << "': " << cpp_strerror(err) << dendl;
+    reset();
+    return -err;
+  }
+  struct stat st;
+  if (fstat(fd, &st) == -1) {
+    int err = errno;
+    derr << __func__ << ": failed to fstat pid file '"
+        << pf_path << "': " << cpp_strerror(err) << dendl;
+    ::close(fd);
+    reset();
+    return -err;
+  }
+
+  pf_fd = fd;
+  pf_dev = st.st_dev;
+  pf_ino = st.st_ino;
+
+  struct flock l = {
+    .l_type = F_WRLCK,
+    .l_whence = SEEK_SET,
+    .l_start = 0,
+    .l_len = 0
+  };
+  int r = ::fcntl(pf_fd, F_SETLK, &l);
+  if (r < 0) {
+    derr << __func__ << ": failed to lock pidfile "
+        << pf_path << " because another process locked it." << dendl;
+    ::close(pf_fd);
+    reset();
+    return -errno;
+  }
+  return 0;
+}
+
+int pidfh::write()
+{
+  if (!is_open())
+    return 0;
+
+  char buf[32];
+  int len = snprintf(buf, sizeof(buf), "%d\n", getpid());
+  if (::ftruncate(pf_fd, 0) < 0) {
+    int err = errno;
+    derr << __func__ << ": failed to ftruncate the pid file '"
+        << pf_path << "': " << cpp_strerror(err) << dendl;
+    return -err;
+  }
+  ssize_t res = safe_write(pf_fd, buf, len);
+  if (res < 0) {
+    derr << __func__ << ": failed to write to pid file '"
+        << pf_path << "': " << cpp_strerror(-res) << dendl;
+    return res;
+  }
+  return 0;
+}
+
+void pidfile_remove()
+{
+  if (pfh != nullptr)
+    delete pfh;
+  pfh = nullptr;
+}
+
+int pidfile_write(const md_config_t *conf)
+{
+  if (conf->pid_file.empty()) {
+    dout(0) << __func__ << ": ignore empty --pid-file" << dendl;
+    return 0;
+  }
+
+  assert(pfh == nullptr);
+
+  pfh = new pidfh();
+  if (atexit(pidfile_remove)) {
+    derr << __func__ << ": failed to set pidfile_remove function "
+        << "to run at exit." << dendl;
+    return -EINVAL;
+  }
+
+  int r = pfh->open(conf);
+  if (r != 0) {
+    pidfile_remove();
+    return r;
+  }
+
+  r = pfh->write();
+  if (r != 0) {
+    pidfile_remove();
+    return r;
+  }
+
+  return 0;
+}