]> git.proxmox.com Git - ceph.git/blob - ceph/src/global/pidfile.cc
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / global / pidfile.cc
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
43 struct pidfh {
44 int pf_fd;
45 char pf_path[PATH_MAX + 1];
46 dev_t pf_dev;
47 ino_t pf_ino;
48
49 pidfh() {
50 reset();
51 }
52 ~pidfh() {
53 remove();
54 }
55
56 bool is_open() {
57 return pf_path[0] != '\0' && pf_fd != -1;
58 }
59 void reset() {
60 pf_fd = -1;
61 memset(pf_path, 0, sizeof(pf_path));
62 pf_dev = 0;
63 pf_ino = 0;
64 }
65 int verify();
66 int remove();
67 int open(const ConfigProxy& conf);
68 int write();
69 };
70
71 static pidfh *pfh = nullptr;
72
73 int 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;
78 if (stat(pf_path, &st) == -1)
79 return -errno;
80 if (st.st_dev != pf_dev || st.st_ino != pf_ino)
81 return -ESTALE;
82 return 0;
83 }
84
85 int pidfh::remove()
86 {
87 if (!pf_path[0])
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 }
125 ret = ::unlink(pf_path);
126 if (ret < 0) {
127 std::cerr << __func__ << " unlink " << pf_path << " failed "
128 << cpp_strerror(errno) << std::endl;
129 return -errno;
130 }
131 reset();
132 return 0;
133 }
134
135 int pidfh::open(const ConfigProxy& conf)
136 {
137 int len = snprintf(pf_path, sizeof(pf_path),
138 "%s", conf->pid_file.c_str());
139
140 if (len >= (int)sizeof(pf_path))
141 return -ENAMETOOLONG;
142
143 int fd;
144 fd = ::open(pf_path, O_CREAT|O_RDWR|O_CLOEXEC, 0644);
145 if (fd < 0) {
146 int err = errno;
147 derr << __func__ << ": failed to open pid file '"
148 << pf_path << "': " << cpp_strerror(err) << dendl;
149 reset();
150 return -err;
151 }
152 struct stat st;
153 if (fstat(fd, &st) == -1) {
154 int err = errno;
155 derr << __func__ << ": failed to fstat pid file '"
156 << pf_path << "': " << cpp_strerror(err) << dendl;
157 ::close(fd);
158 reset();
159 return -err;
160 }
161
162 pf_fd = fd;
163 pf_dev = st.st_dev;
164 pf_ino = st.st_ino;
165
166 struct flock l = {
167 .l_type = F_WRLCK,
168 .l_whence = SEEK_SET,
169 .l_start = 0,
170 .l_len = 0
171 };
172 int r = ::fcntl(pf_fd, F_SETLK, &l);
173 if (r < 0) {
174 if (errno == EAGAIN || errno == EACCES) {
175 derr << __func__ << ": failed to lock pidfile "
176 << pf_path << " because another process locked it"
177 << "': " << cpp_strerror(errno) << dendl;
178 } else {
179 derr << __func__ << ": failed to lock pidfile "
180 << pf_path << "': " << cpp_strerror(errno) << dendl;
181 }
182 ::close(pf_fd);
183 reset();
184 return -errno;
185 }
186 return 0;
187 }
188
189 int pidfh::write()
190 {
191 if (!is_open())
192 return 0;
193
194 char buf[32];
195 int len = snprintf(buf, sizeof(buf), "%d\n", getpid());
196 if (::ftruncate(pf_fd, 0) < 0) {
197 int err = errno;
198 derr << __func__ << ": failed to ftruncate the pid file '"
199 << pf_path << "': " << cpp_strerror(err) << dendl;
200 return -err;
201 }
202 ssize_t res = safe_write(pf_fd, buf, len);
203 if (res < 0) {
204 derr << __func__ << ": failed to write to pid file '"
205 << pf_path << "': " << cpp_strerror(-res) << dendl;
206 return res;
207 }
208 return 0;
209 }
210
211 void pidfile_remove()
212 {
213 if (pfh != nullptr)
214 delete pfh;
215 pfh = nullptr;
216 }
217
218 int pidfile_write(const ConfigProxy& conf)
219 {
220 if (conf->pid_file.empty()) {
221 dout(0) << __func__ << ": ignore empty --pid-file" << dendl;
222 return 0;
223 }
224
225 ceph_assert(pfh == nullptr);
226
227 pfh = new pidfh();
228 if (atexit(pidfile_remove)) {
229 derr << __func__ << ": failed to set pidfile_remove function "
230 << "to run at exit." << dendl;
231 return -EINVAL;
232 }
233
234 int r = pfh->open(conf);
235 if (r != 0) {
236 pidfile_remove();
237 return r;
238 }
239
240 r = pfh->write();
241 if (r != 0) {
242 pidfile_remove();
243 return r;
244 }
245
246 return 0;
247 }