1 //
2 // Copyright (C) 2012-2018 Codership Oy <info@codership.com>
3 //
4
5 #include "saved_state.hpp"
6 #include <gu_dbug.h>
7 #include <gu_uuid.hpp>
8 #include "gu_inttypes.hpp"
9
10 #include <fstream>
11
12 #include <sys/file.h>
13 #include <fcntl.h>
14
15 namespace galera
16 {
17
18 #define VERSION "2.1"
19 #define MAX_SIZE 256
20
SavedState(const std::string & file)21 SavedState::SavedState (const std::string& file) :
22 fs_ (0),
23 filename_ (file),
24 uuid_ (WSREP_UUID_UNDEFINED),
25 seqno_ (WSREP_SEQNO_UNDEFINED),
26 safe_to_bootstrap_(true),
27 unsafe_ (0),
28 corrupt_ (false),
29 mtx_ (),
30 written_uuid_ (uuid_),
31 current_len_ (0),
32 total_marks_ (0),
33 total_locks_ (0),
34 total_writes_ (0)
35 {
36
37 GU_DBUG_EXECUTE("galera_init_invalidate_state",
38 unlink(file.c_str()););
39
40 std::ifstream ifs(file.c_str());
41
42 if (ifs.fail())
43 {
44 log_warn << "Could not open state file for reading: '" << file << '\'';
45 }
46
47 fs_ = fopen(file.c_str(), "a");
48
49 if (!fs_)
50 {
51 gu_throw_error(errno)
52 << "Could not open state file for writing: '" << file
53 << "'. Check permissions and/or disk space.";
54 }
55
56 // We take exclusive lock on state file in order to avoid possibility
57 // of two Galera replicators sharing the same state file.
58 struct flock flck;
59 flck.l_start = 0;
60 flck.l_len = 0;
61 flck.l_type = F_WRLCK;
62 flck.l_whence = SEEK_SET;
63
64 if (::fcntl(fileno(fs_), F_SETLK, &flck))
65 {
66 log_warn << "Could not get exclusive lock on state file: " << file
67 << ": " << ::strerror(errno);
68 return;
69 }
70
71 std::string version("0.8");
72 std::string line;
73
74 while (getline(ifs, line), ifs.good())
75 {
76 std::istringstream istr(line);
77 std::string param;
78
79 istr >> param;
80
81 if (param[0] == '#')
82 {
83 log_debug << "read comment: " << line;
84 }
85 else if (param == "version:")
86 {
87 istr >> version; // nothing to do with this yet
88 log_debug << "read version: " << version;
89 }
90 else if (param == "uuid:")
91 {
92 try
93 {
94 istr >> uuid_;
95 log_debug << "read saved state uuid: " << uuid_;
96 }
97 catch (gu::Exception& e)
98 {
99 log_error << e.what();
100 uuid_ = WSREP_UUID_UNDEFINED;
101 }
102 }
103 else if (param == "seqno:")
104 {
105 istr >> seqno_;
106 log_debug << "read saved state seqno: " << seqno_;
107 }
108 else if (param == "safe_to_bootstrap:")
109 {
110 istr >> safe_to_bootstrap_;
111 log_debug << "read safe_to_bootstrap: " << safe_to_bootstrap_;
112 }
113 }
114
115 log_info << "Found saved state: " << uuid_ << ':' << seqno_
116 << ", safe_to_bootstrap: " << safe_to_bootstrap_;
117
118 #if 0 // we'll probably have it legal
119 if (seqno_ < 0 && uuid_ != WSREP_UUID_UNDEFINED)
120 {
121 log_warn << "Negative seqno with valid UUID: "
122 << uuid_ << ':' << seqno_ << ". Discarding UUID.";
123 uuid_ = WSREP_UUID_UNDEFINED;
124 }
125 #endif
126
127 written_uuid_ = uuid_;
128
129 current_len_ = ftell (fs_);
130 log_debug << "Initialized current_len_ to " << current_len_;
131 if (current_len_ <= MAX_SIZE)
132 {
133 fs_ = freopen (file.c_str(), "r+", fs_);
134 }
135 else // normalize file contents
136 {
137 fs_ = freopen (file.c_str(), "w+", fs_); // truncate
138 current_len_ = 0;
139 set (uuid_, seqno_, safe_to_bootstrap_);
140 }
141 }
142
~SavedState()143 SavedState::~SavedState ()
144 {
145 if (fs_)
146 {
147 // Closing file descriptor should release the lock, but still...
148 struct flock flck;
149 flck.l_start = 0;
150 flck.l_len = 0;
151 flck.l_type = F_UNLCK;
152 flck.l_whence = SEEK_SET;
153
154 if (::fcntl(fileno(fs_), F_SETLK, &flck))
155 {
156 log_warn << "Could not unlock state file: " << ::strerror(errno);
157 }
158
159 fclose(fs_);
160 }
161 }
162
163 void
get(wsrep_uuid_t & u,wsrep_seqno_t & s,bool & safe_to_bootstrap)164 SavedState::get (wsrep_uuid_t& u, wsrep_seqno_t& s, bool& safe_to_bootstrap)
165 {
166 gu::Lock lock(mtx_);
167
168 u = uuid_;
169 s = seqno_;
170 safe_to_bootstrap = safe_to_bootstrap_;
171 }
172
173 void
set(const wsrep_uuid_t & u,wsrep_seqno_t s,bool safe_to_bootstrap)174 SavedState::set (const wsrep_uuid_t& u, wsrep_seqno_t s, bool safe_to_bootstrap)
175 {
176 gu::Lock lock(mtx_); ++total_locks_;
177
178 if (corrupt_) return;
179
180 uuid_ = u;
181 seqno_ = s;
182 safe_to_bootstrap_ = safe_to_bootstrap;
183
184 if (0 == unsafe_())
185 write_file (u, s, safe_to_bootstrap);
186 else
187 log_debug << "Not writing state: unsafe counter is " << unsafe_();
188 }
189
190 /* the goal of unsafe_, written_uuid_, current_len_ below is
191 * 1. avoid unnecessary mutex locks
192 * 2. if locked - avoid unnecessary file writes
193 * 3. if writing - avoid metadata operations, write over existing space */
194
195 void
mark_unsafe()196 SavedState::mark_unsafe()
197 {
198 ++total_marks_;
199
200 if (1 == unsafe_.add_and_fetch (1))
201 {
202 gu::Lock lock(mtx_); ++total_locks_;
203
204 assert (unsafe_() > 0);
205
206 if (written_uuid_ != WSREP_UUID_UNDEFINED)
207 {
208 write_file (WSREP_UUID_UNDEFINED, WSREP_SEQNO_UNDEFINED,
209 safe_to_bootstrap_);
210 }
211 }
212 }
213
214 void
mark_safe()215 SavedState::mark_safe()
216 {
217 ++total_marks_;
218
219 long count = unsafe_.sub_and_fetch (1);
220 assert (count >= 0);
221
222 if (0 == count)
223 {
224 gu::Lock lock(mtx_); ++total_locks_;
225
226 if (0 == unsafe_() && (written_uuid_ != uuid_ || seqno_ >= 0) &&
227 !corrupt_)
228 {
229 /* this will write down proper seqno if set() was called too early
230 * (in unsafe state) */
231 write_file (uuid_, seqno_, safe_to_bootstrap_);
232 }
233 }
234 }
235
236 void
mark_corrupt()237 SavedState::mark_corrupt()
238 {
239 gu::Lock lock(mtx_); ++total_locks_;
240
241 if (corrupt_) return;
242
243 uuid_ = WSREP_UUID_UNDEFINED;
244 seqno_ = WSREP_SEQNO_UNDEFINED;
245 corrupt_ = true;
246
247 write_file (WSREP_UUID_UNDEFINED, WSREP_SEQNO_UNDEFINED,
248 safe_to_bootstrap_);
249 }
250
251 void
mark_uncorrupt(const wsrep_uuid_t & u,wsrep_seqno_t s)252 SavedState::mark_uncorrupt(const wsrep_uuid_t& u, wsrep_seqno_t s)
253 {
254 gu::Lock lock(mtx_); ++total_locks_;
255
256 if (!corrupt_) return;
257
258 uuid_ = u;
259 seqno_ = s;
260 unsafe_ = 0;
261 corrupt_ = false;
262
263 write_file (u, s, safe_to_bootstrap_);
264 }
265
266 void
write_file(const wsrep_uuid_t & u,const wsrep_seqno_t s,bool safe_to_bootstrap)267 SavedState::write_file(const wsrep_uuid_t& u, const wsrep_seqno_t s,
268 bool safe_to_bootstrap)
269 {
270 assert (current_len_ <= MAX_SIZE);
271
272 if (fs_)
273 {
274 if (s >= 0) { log_debug << "Saving state: " << u << ':' << s; }
275
276 char buf[MAX_SIZE];
277 int state_len = snprintf (buf, MAX_SIZE - 1,
278 "# GALERA saved state"
279 "\nversion: " VERSION
280 "\nuuid: " GU_UUID_FORMAT
281 "\nseqno: %" PRId64
282 "\nsafe_to_bootstrap: %d\n",
283 GU_UUID_ARGS(&u), s, safe_to_bootstrap);
284
285 int write_size;
286 for (write_size = state_len; write_size < current_len_; ++write_size)
287 buf[write_size] = ' '; // overwrite whatever is there currently
288
289 rewind(fs_);
290
291 if (fwrite(buf, write_size, 1, fs_) == 0) {
292 log_warn << "write file(" << filename_ << ") failed("
293 << strerror(errno) << ")";
294 return;
295 }
296
297 if (fflush(fs_) != 0) {
298 log_warn << "fflush file(" << filename_ << ") failed("
299 << strerror(errno) << ")";
300 return;
301 }
302
303 if (fsync(fileno(fs_)) < 0) {
304 log_warn << "fsync file(" << filename_ << ") failed("
305 << strerror(errno) << ")";
306 return;
307 }
308
309 current_len_ = state_len;
310 written_uuid_ = u;
311 ++total_writes_;
312 }
313 else
314 {
315 log_debug << "Can't save state: output stream is not open.";
316 }
317 }
318
319 } /* namespace galera */
320
321