]> git.proxmox.com Git - ceph.git/blame - ceph/src/global/pidfile.cc
import 15.2.0 Octopus source
[ceph.git] / ceph / src / global / pidfile.cc
CommitLineData
7c673cae
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3/*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2011 New Dream Network
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14
15#include "common/debug.h"
16#include "common/errno.h"
17#include "common/safe_io.h"
18#include "global/pidfile.h"
19
20#include <errno.h>
21#include <fcntl.h>
22#include <sys/stat.h>
23#include <sys/types.h>
24#include <unistd.h>
25
26#if defined(__FreeBSD__)
27#include <sys/param.h>
28#endif
29
30#include "include/compat.h"
31
32//
33// derr can be used for functions exclusively called from pidfile_write
34//
35// cerr must be used for functions called by pidfile_remove because
36// logging is not functional when it is called. cerr output is lost
37// when the caller is daemonized but it will show if not (-f)
38//
39#define dout_context g_ceph_context
40#define dout_prefix *_dout
41#define dout_subsys ceph_subsys_
42
43struct pidfh {
44 int pf_fd;
9f95a23c 45 string pf_path;
7c673cae
FG
46 dev_t pf_dev;
47 ino_t pf_ino;
48
49 pidfh() {
50 reset();
51 }
52 ~pidfh() {
53 remove();
54 }
55
9f95a23c
TL
56 bool is_open() const {
57 return !pf_path.empty() && pf_fd != -1;
7c673cae
FG
58 }
59 void reset() {
60 pf_fd = -1;
9f95a23c 61 pf_path.clear();
7c673cae
FG
62 pf_dev = 0;
63 pf_ino = 0;
64 }
65 int verify();
66 int remove();
9f95a23c 67 int open(std::string_view pid_file);
7c673cae
FG
68 int write();
69};
70
71static pidfh *pfh = nullptr;
72
73int pidfh::verify() {
74 // check that the file we opened still is the same
75 if (pf_fd == -1)
76 return -EINVAL;
77 struct stat st;
9f95a23c 78 if (stat(pf_path.c_str(), &st) == -1)
7c673cae
FG
79 return -errno;
80 if (st.st_dev != pf_dev || st.st_ino != pf_ino)
81 return -ESTALE;
82 return 0;
83}
84
85int pidfh::remove()
86{
9f95a23c 87 if (pf_path.empty())
7c673cae
FG
88 return 0;
89
90 int ret;
91 if ((ret = verify()) < 0) {
92 if (pf_fd != -1) {
93 ::close(pf_fd);
94 reset();
95 }
96 return ret;
97 }
98
99 // seek to the beginning of the file before reading
100 ret = ::lseek(pf_fd, 0, SEEK_SET);
101 if (ret < 0) {
102 std::cerr << __func__ << " lseek failed "
103 << cpp_strerror(errno) << std::endl;
104 return -errno;
105 }
106
107 // check that the pid file still has our pid in it
108 char buf[32];
109 memset(buf, 0, sizeof(buf));
110 ssize_t res = safe_read(pf_fd, buf, sizeof(buf));
111 ::close(pf_fd);
112 if (res < 0) {
113 std::cerr << __func__ << " safe_read failed "
114 << cpp_strerror(-res) << std::endl;
115 return res;
116 }
117
118 int a = atoi(buf);
119 if (a != getpid()) {
120 std::cerr << __func__ << " the pid found in the file is "
121 << a << " which is different from getpid() "
122 << getpid() << std::endl;
123 return -EDOM;
124 }
9f95a23c 125 ret = ::unlink(pf_path.c_str());
7c673cae 126 if (ret < 0) {
9f95a23c 127 std::cerr << __func__ << " unlink " << pf_path.c_str() << " failed "
7c673cae
FG
128 << cpp_strerror(errno) << std::endl;
129 return -errno;
130 }
131 reset();
132 return 0;
133}
134
9f95a23c 135int pidfh::open(std::string_view pid_file)
7c673cae 136{
9f95a23c 137 pf_path = pid_file;
7c673cae
FG
138
139 int fd;
9f95a23c 140 fd = ::open(pf_path.c_str(), O_CREAT|O_RDWR|O_CLOEXEC, 0644);
7c673cae
FG
141 if (fd < 0) {
142 int err = errno;
143 derr << __func__ << ": failed to open pid file '"
144 << pf_path << "': " << cpp_strerror(err) << dendl;
145 reset();
146 return -err;
147 }
148 struct stat st;
149 if (fstat(fd, &st) == -1) {
150 int err = errno;
151 derr << __func__ << ": failed to fstat pid file '"
152 << pf_path << "': " << cpp_strerror(err) << dendl;
153 ::close(fd);
154 reset();
155 return -err;
156 }
157
158 pf_fd = fd;
159 pf_dev = st.st_dev;
160 pf_ino = st.st_ino;
161
162 struct flock l = {
163 .l_type = F_WRLCK,
164 .l_whence = SEEK_SET,
165 .l_start = 0,
166 .l_len = 0
167 };
168 int r = ::fcntl(pf_fd, F_SETLK, &l);
169 if (r < 0) {
11fdf7f2
TL
170 if (errno == EAGAIN || errno == EACCES) {
171 derr << __func__ << ": failed to lock pidfile "
172 << pf_path << " because another process locked it"
173 << "': " << cpp_strerror(errno) << dendl;
174 } else {
175 derr << __func__ << ": failed to lock pidfile "
176 << pf_path << "': " << cpp_strerror(errno) << dendl;
177 }
7c673cae
FG
178 ::close(pf_fd);
179 reset();
180 return -errno;
181 }
182 return 0;
183}
184
185int pidfh::write()
186{
187 if (!is_open())
188 return 0;
189
190 char buf[32];
191 int len = snprintf(buf, sizeof(buf), "%d\n", getpid());
192 if (::ftruncate(pf_fd, 0) < 0) {
193 int err = errno;
194 derr << __func__ << ": failed to ftruncate the pid file '"
195 << pf_path << "': " << cpp_strerror(err) << dendl;
196 return -err;
197 }
198 ssize_t res = safe_write(pf_fd, buf, len);
199 if (res < 0) {
200 derr << __func__ << ": failed to write to pid file '"
201 << pf_path << "': " << cpp_strerror(-res) << dendl;
202 return res;
203 }
204 return 0;
205}
206
207void pidfile_remove()
208{
209 if (pfh != nullptr)
210 delete pfh;
211 pfh = nullptr;
212}
213
9f95a23c 214int pidfile_write(std::string_view pid_file)
7c673cae 215{
9f95a23c 216 if (pid_file.empty()) {
7c673cae
FG
217 dout(0) << __func__ << ": ignore empty --pid-file" << dendl;
218 return 0;
219 }
220
11fdf7f2 221 ceph_assert(pfh == nullptr);
7c673cae
FG
222
223 pfh = new pidfh();
224 if (atexit(pidfile_remove)) {
225 derr << __func__ << ": failed to set pidfile_remove function "
226 << "to run at exit." << dendl;
227 return -EINVAL;
228 }
229
9f95a23c 230 int r = pfh->open(pid_file);
7c673cae
FG
231 if (r != 0) {
232 pidfile_remove();
233 return r;
234 }
235
236 r = pfh->write();
237 if (r != 0) {
238 pidfile_remove();
239 return r;
240 }
241
242 return 0;
243}