Skip to content

Commit

Permalink
global/pidfile: do not start two daemons with a single pid-file
Browse files Browse the repository at this point in the history
add functions named pidfile_open and pidfile_verify to avoid starting two daemons by a single pid-file

Fixes: #13422
Signed-off-by: shun song <song.shun3@zte.com.cn>
  • Loading branch information
shun-s committed Jan 7, 2016
1 parent c485d29 commit a4eae40
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 43 deletions.
15 changes: 11 additions & 4 deletions src/global/global_init.cc
Expand Up @@ -261,14 +261,17 @@ int global_init_prefork(CephContext *cct)
{
if (g_code_env != CODE_ENVIRONMENT_DAEMON)
return -1;
if (pidfile_open(g_conf) < 0) {
exit(1);
}

const md_config_t *conf = cct->_conf;
if (!conf->daemonize) {
if (atexit(pidfile_remove_void)) {
derr << "global_init_daemonize: failed to set pidfile_remove function "
<< "to run at exit." << dendl;
}

pidfile_write(g_conf);
pidfile_write();

return -1;
}
Expand All @@ -292,7 +295,11 @@ void global_init_daemonize(CephContext *cct)
<< cpp_strerror(ret) << dendl;
exit(1);
}


if (pidfile_open(g_conf) < 0) {
exit(1);
}

global_init_postfork_start(cct);
global_init_postfork_finish(cct);
#else
Expand Down Expand Up @@ -333,7 +340,7 @@ void global_init_postfork_start(CephContext *cct)
exit(1);
}

pidfile_write(g_conf);
pidfile_write();
}

void global_init_postfork_finish(CephContext *cct)
Expand Down
139 changes: 101 additions & 38 deletions src/global/pidfile.cc
Expand Up @@ -31,68 +31,131 @@

#define dout_prefix *_dout

static char pid_file[PATH_MAX] = "";
struct pidfh {
int pf_fd;
char pf_path[PATH_MAX + 1];
dev_t pf_dev;
ino_t pf_ino;

pidfh() : pf_fd(-1), pf_dev(0), pf_ino(0) {
memset(pf_path, 0, sizeof(pf_path));
}

void close() {
pf_fd = -1;
pf_path[0] = '\0';
pf_dev = 0;
pf_ino = 0;
}
};
static struct pidfh pfh;

int pidfile_write(const md_config_t *conf)
{
int ret, fd;
static int pidfile_verify() {
struct stat sb;

if (conf->pid_file.empty()) {
return pidfile_remove();
}
snprintf(pid_file, PATH_MAX, "%s", conf->pid_file.c_str());
if (pfh.pf_fd == -1)
return -EINVAL;
/*
* Check remembered descriptor
*/
if (fstat(pfh.pf_fd, &sb) == -1)
return -errno;
if (sb.st_dev != pfh.pf_dev || sb.st_ino != pfh.pf_ino)
return -ESTALE;
return 0;
}

fd = TEMP_FAILURE_RETRY(::open(pid_file,
O_CREAT|O_TRUNC|O_WRONLY, 0644));
if (fd < 0) {
int err = errno;
derr << "write_pid_file: failed to open pid file '"
<< pid_file << "': " << cpp_strerror(err) << dendl;
return err;
}
int pidfile_write()
{
int ret;
if (!pfh.pf_path[0])
return 0;

char buf[20];
if ((ret=pidfile_verify()) < 0) {
return ret;
}
char buf[32];
int len = snprintf(buf, sizeof(buf), "%d\n", getpid());
ret = safe_write(fd, buf, len);
ret = safe_write(pfh.pf_fd, buf, len);
if (ret < 0) {
derr << "write_pid_file: failed to write to pid file '"
<< pid_file << "': " << cpp_strerror(ret) << dendl;
VOID_TEMP_FAILURE_RETRY(::close(fd));
<< pfh.pf_path << "': " << cpp_strerror(ret) << dendl;
pfh.close();
return ret;
}
if (TEMP_FAILURE_RETRY(::close(fd))) {
ret = errno;
derr << "SimpleMessenger::write_pid_file: failed to close to pid file '"
<< pid_file << "': " << cpp_strerror(ret) << dendl;
return -ret;
}

return 0;
}

int pidfile_remove(void)
{
if (!pid_file[0])
int ret;

if (!pfh.pf_path[0])
return 0;
if ( (ret=pidfile_verify()) < 0) {
VOID_TEMP_FAILURE_RETRY(::close(pfh.pf_fd));
return ret;
}

// only remove it if it has OUR pid in it!
int fd = TEMP_FAILURE_RETRY(::open(pid_file, O_RDONLY));
if (fd < 0)
return -errno;
char buf[32];
memset(buf, 0, sizeof(buf));
ssize_t res = safe_read(fd, buf, sizeof(buf));
VOID_TEMP_FAILURE_RETRY(::close(fd));
ssize_t res = safe_read(pfh.pf_fd, buf, sizeof(buf));
VOID_TEMP_FAILURE_RETRY(::close(pfh.pf_fd));
if (res < 0)
return res;
return res;
int a = atoi(buf);
if (a != getpid())
return -EDOM;

res = ::unlink(pid_file);
res = ::unlink(pfh.pf_path);
if (res)
return res;

pid_file[0] = '\0';
return res;
pfh.close();
return 0;
}

int pidfile_open(const md_config_t *conf)
{
int fd;
struct stat sb;
if (conf->pid_file.empty()) {
return 0;
}
int len = snprintf(pfh.pf_path, sizeof(pfh.pf_path),
"%s", conf->pid_file.c_str());

if (len >= (int)sizeof(pfh.pf_path)) {
return -ENAMETOOLONG;
}

fd = ::open(pfh.pf_path, O_CREAT|O_WRONLY, 0644);
if (fd < 0) {
int err = errno;
derr << "write_pid_file: failed to open pid file '"
<< pfh.pf_path << "': " << cpp_strerror(err) << dendl;
pfh.close();
return -errno;
}

if (fstat(fd, &sb) == -1) {
close(fd);
pfh.close();
return -errno;
}
pfh.pf_fd = fd;
pfh.pf_dev = sb.st_dev;
pfh.pf_ino = sb.st_ino;

struct flock l = { F_WRLCK, SEEK_SET, 0, 0, 0 };
int r = ::fcntl(pfh.pf_fd, F_SETLK, &l);
if (r < 0) {
derr << "failed to lock" << pfh.pf_path << ".is there another process in using?" << dendl;
close(pfh.pf_fd);
pfh.close();
return -errno;
}
return 0;
}

5 changes: 4 additions & 1 deletion src/global/pidfile.h
Expand Up @@ -19,10 +19,13 @@ struct md_config_t;

// Write a pidfile with the current pid, using the configuration in the
// provided conf structure.
int pidfile_write(const md_config_t *conf);
int pidfile_write();

// Remove the pid file that was previously written by pidfile_write.
// This is safe to call in a signal handler context.
int pidfile_remove(void);

//test whether the pid_file is being used by another process
int pidfile_open(const md_config_t *conf);

#endif

0 comments on commit a4eae40

Please sign in to comment.