]>
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; | |
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(); | |
11fdf7f2 | 67 | int open(const ConfigProxy& conf); |
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; | |
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 | ||
11fdf7f2 | 135 | int pidfh::open(const ConfigProxy& conf) |
7c673cae FG |
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; | |
91327a77 | 144 | fd = ::open(pf_path, O_CREAT|O_RDWR|O_CLOEXEC, 0644); |
7c673cae FG |
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) { | |
11fdf7f2 TL |
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 | } | |
7c673cae FG |
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 | ||
11fdf7f2 | 218 | int pidfile_write(const ConfigProxy& conf) |
7c673cae FG |
219 | { |
220 | if (conf->pid_file.empty()) { | |
221 | dout(0) << __func__ << ": ignore empty --pid-file" << dendl; | |
222 | return 0; | |
223 | } | |
224 | ||
11fdf7f2 | 225 | ceph_assert(pfh == nullptr); |
7c673cae FG |
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 | } |