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