]>
Commit | Line | Data |
---|---|---|
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 | ||
43 | struct 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 | ||
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; | |
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 | ||
85 | int 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 | 135 | int 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 | ||
185 | int 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 | ||
207 | void pidfile_remove() | |
208 | { | |
209 | if (pfh != nullptr) | |
210 | delete pfh; | |
211 | pfh = nullptr; | |
212 | } | |
213 | ||
9f95a23c | 214 | int 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 | } |