2 ctdb parallel database recovery
4 Copyright (C) Amitay Isaacs 2015
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, see <http://www.gnu.org/licenses/>.
21 #include "system/network.h"
22 #include "system/filesys.h"
29 #include "lib/tdb_wrap/tdb_wrap.h"
30 #include "lib/util/time.h"
31 #include "lib/util/tevent_unix.h"
33 #include "protocol/protocol.h"
34 #include "protocol/protocol_api.h"
35 #include "client/client.h"
37 static int recover_timeout
= 120;
39 #define TIMEOUT() timeval_current_ofs(recover_timeout, 0)
41 static void LOG(const char *fmt
, ...)
46 vfprintf(stderr
, fmt
, ap
);
54 static ssize_t
sys_write(int fd
, const void *buf
, size_t count
)
59 ret
= write(fd
, buf
, count
);
60 #if defined(EWOULDBLOCK)
61 } while (ret
== -1 && (errno
== EINTR
|| errno
== EAGAIN
|| errno
== EWOULDBLOCK
));
63 } while (ret
== -1 && (errno
== EINTR
|| errno
== EAGAIN
));
69 * Recovery database functions
72 struct recdb_context
{
80 static struct recdb_context
*recdb_create(TALLOC_CTX
*mem_ctx
, uint32_t db_id
,
83 uint32_t hash_size
, bool persistent
)
85 static char *db_dir_state
= NULL
;
86 struct recdb_context
*recdb
;
87 unsigned int tdb_flags
;
89 recdb
= talloc(mem_ctx
, struct recdb_context
);
94 if (db_dir_state
== NULL
) {
95 db_dir_state
= getenv("CTDB_DBDIR_STATE");
98 recdb
->db_name
= db_name
;
100 recdb
->db_path
= talloc_asprintf(recdb
, "%s/recdb.%s",
101 db_dir_state
!= NULL
?
103 dirname(discard_const(db_path
)),
105 if (recdb
->db_path
== NULL
) {
109 unlink(recdb
->db_path
);
111 tdb_flags
= TDB_NOLOCK
| TDB_INCOMPATIBLE_HASH
| TDB_DISALLOW_NESTING
;
112 recdb
->db
= tdb_wrap_open(mem_ctx
, recdb
->db_path
, hash_size
,
113 tdb_flags
, O_RDWR
|O_CREAT
|O_EXCL
, 0600);
114 if (recdb
->db
== NULL
) {
116 LOG("failed to create recovery db %s\n", recdb
->db_path
);
119 recdb
->persistent
= persistent
;
124 static const char *recdb_name(struct recdb_context
*recdb
)
126 return recdb
->db_name
;
129 struct recdb_add_traverse_state
{
130 struct recdb_context
*recdb
;
134 static int recdb_add_traverse(uint32_t reqid
, struct ctdb_ltdb_header
*header
,
135 TDB_DATA key
, TDB_DATA data
,
138 struct recdb_add_traverse_state
*state
=
139 (struct recdb_add_traverse_state
*)private_data
;
140 struct ctdb_ltdb_header
*hdr
;
144 /* header is not marshalled separately in the pulldb control */
145 if (data
.dsize
< sizeof(struct ctdb_ltdb_header
)) {
149 hdr
= (struct ctdb_ltdb_header
*)data
.dptr
;
151 /* fetch the existing record, if any */
152 prev_data
= tdb_fetch(state
->recdb
->db
->tdb
, key
);
154 if (prev_data
.dptr
!= NULL
) {
155 struct ctdb_ltdb_header prev_hdr
;
157 prev_hdr
= *(struct ctdb_ltdb_header
*)prev_data
.dptr
;
158 free(prev_data
.dptr
);
159 if (hdr
->rsn
< prev_hdr
.rsn
||
160 (hdr
->rsn
== prev_hdr
.rsn
&&
161 prev_hdr
.dmaster
!= state
->mypnn
)) {
166 ret
= tdb_store(state
->recdb
->db
->tdb
, key
, data
, TDB_REPLACE
);
173 static bool recdb_add(struct recdb_context
*recdb
, int mypnn
,
174 struct ctdb_rec_buffer
*recbuf
)
176 struct recdb_add_traverse_state state
;
182 ret
= ctdb_rec_buffer_traverse(recbuf
, recdb_add_traverse
, &state
);
190 struct recdb_traverse_state
{
191 struct ctdb_rec_buffer
*recbuf
;
198 static int recdb_traverse(struct tdb_context
*tdb
, TDB_DATA key
, TDB_DATA data
,
201 struct recdb_traverse_state
*state
=
202 (struct recdb_traverse_state
*)private_data
;
203 struct ctdb_ltdb_header
*header
;
207 * skip empty records - but NOT for persistent databases:
209 * The record-by-record mode of recovery deletes empty records.
210 * For persistent databases, this can lead to data corruption
211 * by deleting records that should be there:
213 * - Assume the cluster has been running for a while.
215 * - A record R in a persistent database has been created and
216 * deleted a couple of times, the last operation being deletion,
217 * leaving an empty record with a high RSN, say 10.
219 * - Now a node N is turned off.
221 * - This leaves the local database copy of D on N with the empty
222 * copy of R and RSN 10. On all other nodes, the recovery has deleted
223 * the copy of record R.
225 * - Now the record is created again while node N is turned off.
226 * This creates R with RSN = 1 on all nodes except for N.
228 * - Now node N is turned on again. The following recovery will chose
229 * the older empty copy of R due to RSN 10 > RSN 1.
231 * ==> Hence the record is gone after the recovery.
233 * On databases like Samba's registry, this can damage the higher-level
234 * data structures built from the various tdb-level records.
236 if (!state
->persistent
&&
237 data
.dsize
<= sizeof(struct ctdb_ltdb_header
)) {
241 /* update the dmaster field to point to us */
242 header
= (struct ctdb_ltdb_header
*)data
.dptr
;
243 if (!state
->persistent
) {
244 header
->dmaster
= state
->pnn
;
245 header
->flags
|= CTDB_REC_FLAG_MIGRATED_WITH_DATA
;
248 ret
= ctdb_rec_buffer_add(state
->recbuf
, state
->recbuf
, state
->reqid
,
251 state
->failed
= true;
258 static struct ctdb_rec_buffer
*recdb_records(struct recdb_context
*recdb
,
259 TALLOC_CTX
*mem_ctx
, uint32_t pnn
)
261 struct recdb_traverse_state state
;
264 state
.recbuf
= ctdb_rec_buffer_init(mem_ctx
, recdb
->db_id
);
265 if (state
.recbuf
== NULL
) {
270 state
.persistent
= recdb
->persistent
;
271 state
.failed
= false;
273 ret
= tdb_traverse_read(recdb
->db
->tdb
, recdb_traverse
, &state
);
274 if (ret
== -1 || state
.failed
) {
275 LOG("Failed to marshall recovery records for %s\n",
277 TALLOC_FREE(state
.recbuf
);
285 * Collect databases using highest sequence number
288 struct collect_highseqnum_db_state
{
289 struct tevent_context
*ev
;
290 struct ctdb_client_context
*client
;
294 struct recdb_context
*recdb
;
298 static void collect_highseqnum_db_seqnum_done(struct tevent_req
*subreq
);
299 static void collect_highseqnum_db_pulldb_done(struct tevent_req
*subreq
);
301 static struct tevent_req
*collect_highseqnum_db_send(
303 struct tevent_context
*ev
,
304 struct ctdb_client_context
*client
,
305 uint32_t *pnn_list
, int count
,
306 uint32_t db_id
, struct recdb_context
*recdb
)
308 struct tevent_req
*req
, *subreq
;
309 struct collect_highseqnum_db_state
*state
;
310 struct ctdb_req_control request
;
312 req
= tevent_req_create(mem_ctx
, &state
,
313 struct collect_highseqnum_db_state
);
319 state
->client
= client
;
320 state
->pnn_list
= pnn_list
;
321 state
->count
= count
;
322 state
->db_id
= db_id
;
323 state
->recdb
= recdb
;
325 ctdb_req_control_get_db_seqnum(&request
, db_id
);
326 subreq
= ctdb_client_control_multi_send(mem_ctx
, ev
, client
,
327 state
->pnn_list
, state
->count
,
328 TIMEOUT(), &request
);
329 if (tevent_req_nomem(subreq
, req
)) {
330 return tevent_req_post(req
, ev
);
332 tevent_req_set_callback(subreq
, collect_highseqnum_db_seqnum_done
,
338 static void collect_highseqnum_db_seqnum_done(struct tevent_req
*subreq
)
340 struct tevent_req
*req
= tevent_req_callback_data(
341 subreq
, struct tevent_req
);
342 struct collect_highseqnum_db_state
*state
= tevent_req_data(
343 req
, struct collect_highseqnum_db_state
);
344 struct ctdb_reply_control
**reply
;
345 struct ctdb_req_control request
;
346 struct ctdb_pulldb pulldb
;
350 uint64_t seqnum
, max_seqnum
;
352 status
= ctdb_client_control_multi_recv(subreq
, &ret
, state
,
359 ret2
= ctdb_client_control_multi_error(state
->pnn_list
,
360 state
->count
, err_list
,
363 LOG("control GET_DB_SEQNUM failed for %s on node %u,"
364 " ret=%d\n", recdb_name(state
->recdb
), pnn
, ret2
);
366 LOG("control GET_DB_SEQNUM failed for %s, ret=%d\n",
367 recdb_name(state
->recdb
), ret
);
369 tevent_req_error(req
, ret
);
374 state
->max_pnn
= state
->pnn_list
[0];
375 for (i
=0; i
<state
->count
; i
++) {
376 ret
= ctdb_reply_control_get_db_seqnum(reply
[i
], &seqnum
);
378 tevent_req_error(req
, EPROTO
);
382 if (max_seqnum
< seqnum
) {
384 state
->max_pnn
= state
->pnn_list
[i
];
390 LOG("Pull persistent db %s from node %d with seqnum 0x%"PRIx64
"\n",
391 recdb_name(state
->recdb
), state
->max_pnn
, max_seqnum
);
393 pulldb
.db_id
= state
->db_id
;
394 pulldb
.lmaster
= CTDB_LMASTER_ANY
;
396 ctdb_req_control_pull_db(&request
, &pulldb
);
397 subreq
= ctdb_client_control_send(state
, state
->ev
, state
->client
,
398 state
->max_pnn
, TIMEOUT(), &request
);
399 if (tevent_req_nomem(subreq
, req
)) {
402 tevent_req_set_callback(subreq
, collect_highseqnum_db_pulldb_done
,
406 static void collect_highseqnum_db_pulldb_done(struct tevent_req
*subreq
)
408 struct tevent_req
*req
= tevent_req_callback_data(
409 subreq
, struct tevent_req
);
410 struct collect_highseqnum_db_state
*state
= tevent_req_data(
411 req
, struct collect_highseqnum_db_state
);
412 struct ctdb_reply_control
*reply
;
413 struct ctdb_rec_buffer
*recbuf
;
417 status
= ctdb_client_control_recv(subreq
, &ret
, state
, &reply
);
420 LOG("control PULL_DB failed for %s on node %u, ret=%d\n",
421 recdb_name(state
->recdb
), state
->max_pnn
, ret
);
422 tevent_req_error(req
, ret
);
426 ret
= ctdb_reply_control_pull_db(reply
, state
, &recbuf
);
428 tevent_req_error(req
, EPROTO
);
434 ret
= recdb_add(state
->recdb
, ctdb_client_pnn(state
->client
), recbuf
);
437 tevent_req_error(req
, EIO
);
441 tevent_req_done(req
);
444 static bool collect_highseqnum_db_recv(struct tevent_req
*req
, int *perr
)
448 if (tevent_req_is_unix_error(req
, &err
)) {
459 * Collect all databases
462 struct collect_all_db_state
{
463 struct tevent_context
*ev
;
464 struct ctdb_client_context
*client
;
468 struct recdb_context
*recdb
;
469 struct ctdb_pulldb pulldb
;
473 static void collect_all_db_pulldb_done(struct tevent_req
*subreq
);
475 static struct tevent_req
*collect_all_db_send(
477 struct tevent_context
*ev
,
478 struct ctdb_client_context
*client
,
479 uint32_t *pnn_list
, int count
,
480 uint32_t db_id
, struct recdb_context
*recdb
)
482 struct tevent_req
*req
, *subreq
;
483 struct collect_all_db_state
*state
;
484 struct ctdb_req_control request
;
486 req
= tevent_req_create(mem_ctx
, &state
,
487 struct collect_all_db_state
);
493 state
->client
= client
;
494 state
->pnn_list
= pnn_list
;
495 state
->count
= count
;
496 state
->db_id
= db_id
;
497 state
->recdb
= recdb
;
499 state
->pulldb
.db_id
= db_id
;
500 state
->pulldb
.lmaster
= CTDB_LMASTER_ANY
;
504 ctdb_req_control_pull_db(&request
, &state
->pulldb
);
505 subreq
= ctdb_client_control_send(state
, ev
, client
,
506 state
->pnn_list
[state
->index
],
507 TIMEOUT(), &request
);
508 if (tevent_req_nomem(subreq
, req
)) {
509 return tevent_req_post(req
, ev
);
511 tevent_req_set_callback(subreq
, collect_all_db_pulldb_done
, req
);
516 static void collect_all_db_pulldb_done(struct tevent_req
*subreq
)
518 struct tevent_req
*req
= tevent_req_callback_data(
519 subreq
, struct tevent_req
);
520 struct collect_all_db_state
*state
= tevent_req_data(
521 req
, struct collect_all_db_state
);
522 struct ctdb_reply_control
*reply
;
523 struct ctdb_req_control request
;
524 struct ctdb_rec_buffer
*recbuf
;
528 status
= ctdb_client_control_recv(subreq
, &ret
, state
, &reply
);
531 LOG("control PULL_DB failed for %s from node %u, ret=%d\n",
532 recdb_name(state
->recdb
), state
->pnn_list
[state
->index
],
534 tevent_req_error(req
, ret
);
538 ret
= ctdb_reply_control_pull_db(reply
, state
, &recbuf
);
540 LOG("control PULL_DB failed for %s, ret=%d\n",
541 recdb_name(state
->recdb
), ret
);
542 tevent_req_error(req
, EPROTO
);
548 status
= recdb_add(state
->recdb
, ctdb_client_pnn(state
->client
), recbuf
);
551 tevent_req_error(req
, EIO
);
556 if (state
->index
== state
->count
) {
557 tevent_req_done(req
);
561 ctdb_req_control_pull_db(&request
, &state
->pulldb
);
562 subreq
= ctdb_client_control_send(state
, state
->ev
, state
->client
,
563 state
->pnn_list
[state
->index
],
564 TIMEOUT(), &request
);
565 if (tevent_req_nomem(subreq
, req
)) {
568 tevent_req_set_callback(subreq
, collect_all_db_pulldb_done
, req
);
571 static bool collect_all_db_recv(struct tevent_req
*req
, int *perr
)
575 if (tevent_req_is_unix_error(req
, &err
)) {
587 * For each database do the following:
590 * - Freeze database on all nodes
591 * - Start transaction on all nodes
592 * - Collect database from all nodes
593 * - Wipe database on all nodes
594 * - Push database to all nodes
595 * - Commit transaction on all nodes
596 * - Thaw database on all nodes
599 struct recover_db_state
{
600 struct tevent_context
*ev
;
601 struct ctdb_client_context
*client
;
602 struct ctdb_tunable_list
*tun_list
;
609 struct ctdb_transdb transdb
;
611 const char *db_name
, *db_path
;
612 struct recdb_context
*recdb
;
613 struct ctdb_rec_buffer
*recbuf
;
617 static void recover_db_name_done(struct tevent_req
*subreq
);
618 static void recover_db_path_done(struct tevent_req
*subreq
);
619 static void recover_db_freeze_done(struct tevent_req
*subreq
);
620 static void recover_db_transaction_started(struct tevent_req
*subreq
);
621 static void recover_db_collect_done(struct tevent_req
*subreq
);
622 static void recover_db_wipedb_done(struct tevent_req
*subreq
);
623 static void recover_db_pushdb_done(struct tevent_req
*subreq
);
624 static void recover_db_transaction_committed(struct tevent_req
*subreq
);
625 static void recover_db_thaw_done(struct tevent_req
*subreq
);
627 static struct tevent_req
*recover_db_send(TALLOC_CTX
*mem_ctx
,
628 struct tevent_context
*ev
,
629 struct ctdb_client_context
*client
,
630 struct ctdb_tunable_list
*tun_list
,
631 uint32_t *pnn_list
, int count
,
633 uint32_t db_id
, bool persistent
)
635 struct tevent_req
*req
, *subreq
;
636 struct recover_db_state
*state
;
637 struct ctdb_req_control request
;
639 req
= tevent_req_create(mem_ctx
, &state
, struct recover_db_state
);
645 state
->client
= client
;
646 state
->tun_list
= tun_list
;
647 state
->pnn_list
= pnn_list
;
648 state
->count
= count
;
649 state
->db_id
= db_id
;
650 state
->persistent
= persistent
;
652 state
->destnode
= ctdb_client_pnn(client
);
653 state
->transdb
.db_id
= db_id
;
654 state
->transdb
.tid
= generation
;
656 ctdb_req_control_get_dbname(&request
, db_id
);
657 subreq
= ctdb_client_control_send(state
, ev
, client
, state
->destnode
,
658 TIMEOUT(), &request
);
659 if (tevent_req_nomem(subreq
, req
)) {
660 return tevent_req_post(req
, ev
);
662 tevent_req_set_callback(subreq
, recover_db_name_done
, req
);
667 static void recover_db_name_done(struct tevent_req
*subreq
)
669 struct tevent_req
*req
= tevent_req_callback_data(
670 subreq
, struct tevent_req
);
671 struct recover_db_state
*state
= tevent_req_data(
672 req
, struct recover_db_state
);
673 struct ctdb_reply_control
*reply
;
674 struct ctdb_req_control request
;
678 status
= ctdb_client_control_recv(subreq
, &ret
, state
, &reply
);
681 LOG("control GET_DBNAME failed for db=0x%x, ret=%d\n",
683 tevent_req_error(req
, ret
);
687 ret
= ctdb_reply_control_get_dbname(reply
, state
, &state
->db_name
);
689 LOG("control GET_DBNAME failed for db=0x%x, ret=%d\n",
691 tevent_req_error(req
, EPROTO
);
697 ctdb_req_control_getdbpath(&request
, state
->db_id
);
698 subreq
= ctdb_client_control_send(state
, state
->ev
, state
->client
,
699 state
->destnode
, TIMEOUT(),
701 if (tevent_req_nomem(subreq
, req
)) {
704 tevent_req_set_callback(subreq
, recover_db_path_done
, req
);
707 static void recover_db_path_done(struct tevent_req
*subreq
)
709 struct tevent_req
*req
= tevent_req_callback_data(
710 subreq
, struct tevent_req
);
711 struct recover_db_state
*state
= tevent_req_data(
712 req
, struct recover_db_state
);
713 struct ctdb_reply_control
*reply
;
714 struct ctdb_req_control request
;
718 status
= ctdb_client_control_recv(subreq
, &ret
, state
, &reply
);
721 LOG("control GETDBPATH failed for db %s, ret=%d\n",
722 state
->db_name
, ret
);
723 tevent_req_error(req
, ret
);
727 ret
= ctdb_reply_control_getdbpath(reply
, state
, &state
->db_path
);
729 LOG("control GETDBPATH failed for db %s, ret=%d\n",
730 state
->db_name
, ret
);
731 tevent_req_error(req
, EPROTO
);
737 ctdb_req_control_db_freeze(&request
, state
->db_id
);
738 subreq
= ctdb_client_control_multi_send(state
, state
->ev
,
740 state
->pnn_list
, state
->count
,
741 TIMEOUT(), &request
);
742 if (tevent_req_nomem(subreq
, req
)) {
745 tevent_req_set_callback(subreq
, recover_db_freeze_done
, req
);
748 static void recover_db_freeze_done(struct tevent_req
*subreq
)
750 struct tevent_req
*req
= tevent_req_callback_data(
751 subreq
, struct tevent_req
);
752 struct recover_db_state
*state
= tevent_req_data(
753 req
, struct recover_db_state
);
754 struct ctdb_req_control request
;
759 status
= ctdb_client_control_multi_recv(subreq
, &ret
, NULL
, &err_list
,
766 ret2
= ctdb_client_control_multi_error(state
->pnn_list
,
767 state
->count
, err_list
,
770 LOG("control FREEZE_DB failed for db %s on node %u,"
771 " ret=%d\n", state
->db_name
, pnn
, ret2
);
773 LOG("control FREEZE_DB failed for db %s, ret=%d\n",
774 state
->db_name
, ret
);
776 tevent_req_error(req
, ret
);
780 ctdb_req_control_db_transaction_start(&request
, &state
->transdb
);
781 subreq
= ctdb_client_control_multi_send(state
, state
->ev
,
783 state
->pnn_list
, state
->count
,
784 TIMEOUT(), &request
);
785 if (tevent_req_nomem(subreq
, req
)) {
788 tevent_req_set_callback(subreq
, recover_db_transaction_started
, req
);
791 static void recover_db_transaction_started(struct tevent_req
*subreq
)
793 struct tevent_req
*req
= tevent_req_callback_data(
794 subreq
, struct tevent_req
);
795 struct recover_db_state
*state
= tevent_req_data(
796 req
, struct recover_db_state
);
801 status
= ctdb_client_control_multi_recv(subreq
, &ret
, NULL
, &err_list
,
808 ret2
= ctdb_client_control_multi_error(state
->pnn_list
,
812 LOG("control TRANSACTION_DB failed for db=%s,"
813 " ret=%d\n", state
->db_name
, pnn
, ret2
);
815 LOG("control TRANSACTION_DB failed for db=%s,"
816 " ret=%d\n", state
->db_name
, ret
);
818 tevent_req_error(req
, ret
);
822 state
->recdb
= recdb_create(state
, state
->db_id
, state
->db_name
,
824 state
->tun_list
->database_hash_size
,
826 if (tevent_req_nomem(state
->recdb
, req
)) {
830 if (state
->persistent
&& state
->tun_list
->recover_pdb_by_seqnum
!= 0) {
831 subreq
= collect_highseqnum_db_send(
832 state
, state
->ev
, state
->client
,
833 state
->pnn_list
, state
->count
,
834 state
->db_id
, state
->recdb
);
836 subreq
= collect_all_db_send(
837 state
, state
->ev
, state
->client
,
838 state
->pnn_list
, state
->count
,
839 state
->db_id
, state
->recdb
);
841 if (tevent_req_nomem(subreq
, req
)) {
844 tevent_req_set_callback(subreq
, recover_db_collect_done
, req
);
847 static void recover_db_collect_done(struct tevent_req
*subreq
)
849 struct tevent_req
*req
= tevent_req_callback_data(
850 subreq
, struct tevent_req
);
851 struct recover_db_state
*state
= tevent_req_data(
852 req
, struct recover_db_state
);
853 struct ctdb_req_control request
;
857 if (state
->persistent
&& state
->tun_list
->recover_pdb_by_seqnum
!= 0) {
858 status
= collect_highseqnum_db_recv(subreq
, &ret
);
860 status
= collect_all_db_recv(subreq
, &ret
);
864 tevent_req_error(req
, ret
);
868 ctdb_req_control_wipe_database(&request
, &state
->transdb
);
869 subreq
= ctdb_client_control_multi_send(state
, state
->ev
,
871 state
->pnn_list
, state
->count
,
872 TIMEOUT(), &request
);
873 if (tevent_req_nomem(subreq
, req
)) {
876 tevent_req_set_callback(subreq
, recover_db_wipedb_done
, req
);
879 static void recover_db_wipedb_done(struct tevent_req
*subreq
)
881 struct tevent_req
*req
= tevent_req_callback_data(
882 subreq
, struct tevent_req
);
883 struct recover_db_state
*state
= tevent_req_data(
884 req
, struct recover_db_state
);
885 struct ctdb_req_control request
;
890 status
= ctdb_client_control_multi_recv(subreq
, &ret
, NULL
, &err_list
,
897 ret2
= ctdb_client_control_multi_error(state
->pnn_list
,
901 LOG("control WIPEDB failed for db %s on node %u,"
902 " ret=%d\n", state
->db_name
, pnn
, ret2
);
904 LOG("control WIPEDB failed for db %s, ret=%d\n",
905 state
->db_name
, pnn
, ret
);
907 tevent_req_error(req
, ret
);
911 state
->recbuf
= recdb_records(state
->recdb
, state
, state
->destnode
);
912 if (tevent_req_nomem(state
->recbuf
, req
)) {
916 TALLOC_FREE(state
->recdb
);
918 ctdb_req_control_push_db(&request
, state
->recbuf
);
919 subreq
= ctdb_client_control_multi_send(state
, state
->ev
,
921 state
->pnn_list
, state
->count
,
922 TIMEOUT(), &request
);
923 if (tevent_req_nomem(subreq
, req
)) {
926 tevent_req_set_callback(subreq
, recover_db_pushdb_done
, req
);
929 static void recover_db_pushdb_done(struct tevent_req
*subreq
)
931 struct tevent_req
*req
= tevent_req_callback_data(
932 subreq
, struct tevent_req
);
933 struct recover_db_state
*state
= tevent_req_data(
934 req
, struct recover_db_state
);
935 struct ctdb_req_control request
;
940 status
= ctdb_client_control_multi_recv(subreq
, &ret
, NULL
, &err_list
,
947 ret2
= ctdb_client_control_multi_error(state
->pnn_list
,
951 LOG("control PUSHDB failed for db %s on node %u,"
952 " ret=%d\n", state
->db_name
, pnn
, ret2
);
954 LOG("control PUSHDB failed for db %s, ret=%d\n",
955 state
->db_name
, ret
);
957 tevent_req_error(req
, ret
);
961 TALLOC_FREE(state
->recbuf
);
963 ctdb_req_control_db_transaction_commit(&request
, &state
->transdb
);
964 subreq
= ctdb_client_control_multi_send(state
, state
->ev
,
966 state
->pnn_list
, state
->count
,
967 TIMEOUT(), &request
);
968 if (tevent_req_nomem(subreq
, req
)) {
971 tevent_req_set_callback(subreq
, recover_db_transaction_committed
, req
);
974 static void recover_db_transaction_committed(struct tevent_req
*subreq
)
976 struct tevent_req
*req
= tevent_req_callback_data(
977 subreq
, struct tevent_req
);
978 struct recover_db_state
*state
= tevent_req_data(
979 req
, struct recover_db_state
);
980 struct ctdb_req_control request
;
985 status
= ctdb_client_control_multi_recv(subreq
, &ret
, NULL
, &err_list
,
992 ret2
= ctdb_client_control_multi_error(state
->pnn_list
,
996 LOG("control DB_TRANSACTION_COMMIT failed for db %s"
997 " on node %u, ret=%d\n", state
->db_name
, pnn
, ret2
);
999 LOG("control DB_TRANSACTION_COMMIT failed for db %s,"
1000 " ret=%d\n", state
->db_name
, ret
);
1002 tevent_req_error(req
, ret
);
1006 ctdb_req_control_db_thaw(&request
, state
->db_id
);
1007 subreq
= ctdb_client_control_multi_send(state
, state
->ev
,
1009 state
->pnn_list
, state
->count
,
1010 TIMEOUT(), &request
);
1011 if (tevent_req_nomem(subreq
, req
)) {
1014 tevent_req_set_callback(subreq
, recover_db_thaw_done
, req
);
1017 static void recover_db_thaw_done(struct tevent_req
*subreq
)
1019 struct tevent_req
*req
= tevent_req_callback_data(
1020 subreq
, struct tevent_req
);
1021 struct recover_db_state
*state
= tevent_req_data(
1022 req
, struct recover_db_state
);
1027 status
= ctdb_client_control_multi_recv(subreq
, &ret
, NULL
, &err_list
,
1029 TALLOC_FREE(subreq
);
1034 ret2
= ctdb_client_control_multi_error(state
->pnn_list
,
1038 LOG("control DB_THAW failed for db %s on node %u,"
1039 " ret=%d\n", state
->db_name
, pnn
, ret2
);
1041 LOG("control DB_THAW failed for db %s, ret=%d\n",
1042 state
->db_name
, ret
);
1044 tevent_req_error(req
, ret
);
1048 tevent_req_done(req
);
1051 static bool recover_db_recv(struct tevent_req
*req
)
1055 if (tevent_req_is_unix_error(req
, &err
)) {
1064 * Start database recovery for each database
1066 * Try to recover each database 5 times before failing recovery.
1069 struct db_recovery_state
{
1070 struct tevent_context
*ev
;
1071 struct ctdb_dbid_map
*dbmap
;
1076 struct db_recovery_one_state
{
1077 struct tevent_req
*req
;
1078 struct ctdb_client_context
*client
;
1079 struct ctdb_dbid_map
*dbmap
;
1080 struct ctdb_tunable_list
*tun_list
;
1083 uint32_t generation
;
1089 static void db_recovery_one_done(struct tevent_req
*subreq
);
1091 static struct tevent_req
*db_recovery_send(TALLOC_CTX
*mem_ctx
,
1092 struct tevent_context
*ev
,
1093 struct ctdb_client_context
*client
,
1094 struct ctdb_dbid_map
*dbmap
,
1095 struct ctdb_tunable_list
*tun_list
,
1096 uint32_t *pnn_list
, int count
,
1097 uint32_t generation
)
1099 struct tevent_req
*req
, *subreq
;
1100 struct db_recovery_state
*state
;
1103 req
= tevent_req_create(mem_ctx
, &state
, struct db_recovery_state
);
1109 state
->dbmap
= dbmap
;
1110 state
->num_replies
= 0;
1111 state
->num_failed
= 0;
1113 if (dbmap
->num
== 0) {
1114 tevent_req_done(req
);
1115 return tevent_req_post(req
, ev
);
1118 for (i
=0; i
<dbmap
->num
; i
++) {
1119 struct db_recovery_one_state
*substate
;
1121 substate
= talloc_zero(state
, struct db_recovery_one_state
);
1122 if (tevent_req_nomem(substate
, req
)) {
1123 return tevent_req_post(req
, ev
);
1126 substate
->req
= req
;
1127 substate
->client
= client
;
1128 substate
->dbmap
= dbmap
;
1129 substate
->tun_list
= tun_list
;
1130 substate
->pnn_list
= pnn_list
;
1131 substate
->count
= count
;
1132 substate
->generation
= generation
;
1133 substate
->db_id
= dbmap
->dbs
[i
].db_id
;
1134 substate
->persistent
= dbmap
->dbs
[i
].flags
&
1135 CTDB_DB_FLAGS_PERSISTENT
;
1137 subreq
= recover_db_send(state
, ev
, client
, tun_list
,
1138 pnn_list
, count
, generation
,
1140 substate
->persistent
);
1141 if (tevent_req_nomem(subreq
, req
)) {
1142 return tevent_req_post(req
, ev
);
1144 tevent_req_set_callback(subreq
, db_recovery_one_done
,
1146 LOG("recover database 0x%08x\n", substate
->db_id
);
1152 static void db_recovery_one_done(struct tevent_req
*subreq
)
1154 struct db_recovery_one_state
*substate
= tevent_req_callback_data(
1155 subreq
, struct db_recovery_one_state
);
1156 struct tevent_req
*req
= substate
->req
;
1157 struct db_recovery_state
*state
= tevent_req_data(
1158 req
, struct db_recovery_state
);
1161 status
= recover_db_recv(subreq
);
1162 TALLOC_FREE(subreq
);
1165 talloc_free(substate
);
1169 substate
->num_fails
+= 1;
1170 if (substate
->num_fails
< 5) {
1171 subreq
= recover_db_send(state
, state
->ev
, substate
->client
,
1173 substate
->pnn_list
, substate
->count
,
1174 substate
->generation
, substate
->db_id
,
1175 substate
->persistent
);
1176 if (tevent_req_nomem(subreq
, req
)) {
1179 tevent_req_set_callback(subreq
, db_recovery_one_done
, substate
);
1180 LOG("recover database 0x%08x, attempt %d\n", substate
->db_id
,
1181 substate
->num_fails
+1);
1186 state
->num_failed
+= 1;
1189 state
->num_replies
+= 1;
1191 if (state
->num_replies
== state
->dbmap
->num
) {
1192 tevent_req_done(req
);
1196 static bool db_recovery_recv(struct tevent_req
*req
, int *count
)
1198 struct db_recovery_state
*state
= tevent_req_data(
1199 req
, struct db_recovery_state
);
1202 if (tevent_req_is_unix_error(req
, &err
)) {
1207 *count
= state
->num_replies
- state
->num_failed
;
1209 if (state
->num_failed
> 0) {
1218 * Run the parallel database recovery
1223 * - Get capabilities from all nodes
1225 * - Set RECOVERY_ACTIVE
1226 * - Send START_RECOVERY
1227 * - Update vnnmap on all nodes
1228 * - Run database recovery
1229 * - Send END_RECOVERY
1230 * - Set RECOVERY_NORMAL
1233 struct recovery_state
{
1234 struct tevent_context
*ev
;
1235 struct ctdb_client_context
*client
;
1236 uint32_t generation
;
1240 struct ctdb_node_map
*nodemap
;
1242 struct ctdb_tunable_list
*tun_list
;
1243 struct ctdb_vnn_map
*vnnmap
;
1244 struct ctdb_dbid_map
*dbmap
;
1247 static void recovery_tunables_done(struct tevent_req
*subreq
);
1248 static void recovery_nodemap_done(struct tevent_req
*subreq
);
1249 static void recovery_vnnmap_done(struct tevent_req
*subreq
);
1250 static void recovery_capabilities_done(struct tevent_req
*subreq
);
1251 static void recovery_dbmap_done(struct tevent_req
*subreq
);
1252 static void recovery_active_done(struct tevent_req
*subreq
);
1253 static void recovery_start_recovery_done(struct tevent_req
*subreq
);
1254 static void recovery_vnnmap_update_done(struct tevent_req
*subreq
);
1255 static void recovery_db_recovery_done(struct tevent_req
*subreq
);
1256 static void recovery_normal_done(struct tevent_req
*subreq
);
1257 static void recovery_end_recovery_done(struct tevent_req
*subreq
);
1259 static struct tevent_req
*recovery_send(TALLOC_CTX
*mem_ctx
,
1260 struct tevent_context
*ev
,
1261 struct ctdb_client_context
*client
,
1262 uint32_t generation
)
1264 struct tevent_req
*req
, *subreq
;
1265 struct recovery_state
*state
;
1266 struct ctdb_req_control request
;
1268 req
= tevent_req_create(mem_ctx
, &state
, struct recovery_state
);
1274 state
->client
= client
;
1275 state
->generation
= generation
;
1276 state
->destnode
= ctdb_client_pnn(client
);
1278 ctdb_req_control_get_all_tunables(&request
);
1279 subreq
= ctdb_client_control_send(state
, state
->ev
, state
->client
,
1280 state
->destnode
, TIMEOUT(),
1282 if (tevent_req_nomem(subreq
, req
)) {
1283 return tevent_req_post(req
, ev
);
1285 tevent_req_set_callback(subreq
, recovery_tunables_done
, req
);
1290 static void recovery_tunables_done(struct tevent_req
*subreq
)
1292 struct tevent_req
*req
= tevent_req_callback_data(
1293 subreq
, struct tevent_req
);
1294 struct recovery_state
*state
= tevent_req_data(
1295 req
, struct recovery_state
);
1296 struct ctdb_reply_control
*reply
;
1297 struct ctdb_req_control request
;
1301 status
= ctdb_client_control_recv(subreq
, &ret
, state
, &reply
);
1302 TALLOC_FREE(subreq
);
1304 LOG("control GET_ALL_TUNABLES failed, ret=%d\n", ret
);
1305 tevent_req_error(req
, ret
);
1309 ret
= ctdb_reply_control_get_all_tunables(reply
, state
,
1312 LOG("control GET_ALL_TUNABLES failed, ret=%d\n", ret
);
1313 tevent_req_error(req
, EPROTO
);
1319 recover_timeout
= state
->tun_list
->recover_timeout
;
1321 ctdb_req_control_get_nodemap(&request
);
1322 subreq
= ctdb_client_control_send(state
, state
->ev
, state
->client
,
1323 state
->destnode
, TIMEOUT(),
1325 if (tevent_req_nomem(subreq
, req
)) {
1328 tevent_req_set_callback(subreq
, recovery_nodemap_done
, req
);
1331 static void recovery_nodemap_done(struct tevent_req
*subreq
)
1333 struct tevent_req
*req
= tevent_req_callback_data(
1334 subreq
, struct tevent_req
);
1335 struct recovery_state
*state
= tevent_req_data(
1336 req
, struct recovery_state
);
1337 struct ctdb_reply_control
*reply
;
1338 struct ctdb_req_control request
;
1342 status
= ctdb_client_control_recv(subreq
, &ret
, state
, &reply
);
1343 TALLOC_FREE(subreq
);
1345 LOG("control GET_NODEMAP failed to node %u, ret=%d\n",
1346 state
->destnode
, ret
);
1347 tevent_req_error(req
, ret
);
1351 ret
= ctdb_reply_control_get_nodemap(reply
, state
, &state
->nodemap
);
1353 LOG("control GET_NODEMAP failed, ret=%d\n", ret
);
1354 tevent_req_error(req
, ret
);
1358 state
->count
= list_of_active_nodes(state
->nodemap
, CTDB_UNKNOWN_PNN
,
1359 state
, &state
->pnn_list
);
1360 if (state
->count
<= 0) {
1361 tevent_req_error(req
, ENOMEM
);
1365 ctdb_req_control_getvnnmap(&request
);
1366 subreq
= ctdb_client_control_send(state
, state
->ev
, state
->client
,
1367 state
->destnode
, TIMEOUT(),
1369 if (tevent_req_nomem(subreq
, req
)) {
1372 tevent_req_set_callback(subreq
, recovery_vnnmap_done
, req
);
1375 static void recovery_vnnmap_done(struct tevent_req
*subreq
)
1377 struct tevent_req
*req
= tevent_req_callback_data(
1378 subreq
, struct tevent_req
);
1379 struct recovery_state
*state
= tevent_req_data(
1380 req
, struct recovery_state
);
1381 struct ctdb_reply_control
*reply
;
1382 struct ctdb_req_control request
;
1386 status
= ctdb_client_control_recv(subreq
, &ret
, state
, &reply
);
1387 TALLOC_FREE(subreq
);
1389 LOG("control GETVNNMAP failed to node %u, ret=%d\n",
1390 state
->destnode
, ret
);
1391 tevent_req_error(req
, ret
);
1395 ret
= ctdb_reply_control_getvnnmap(reply
, state
, &state
->vnnmap
);
1397 LOG("control GETVNNMAP failed, ret=%d\n", ret
);
1398 tevent_req_error(req
, ret
);
1402 ctdb_req_control_get_capabilities(&request
);
1403 subreq
= ctdb_client_control_multi_send(state
, state
->ev
,
1405 state
->pnn_list
, state
->count
,
1406 TIMEOUT(), &request
);
1407 if (tevent_req_nomem(subreq
, req
)) {
1410 tevent_req_set_callback(subreq
, recovery_capabilities_done
, req
);
1413 static void recovery_capabilities_done(struct tevent_req
*subreq
)
1415 struct tevent_req
*req
= tevent_req_callback_data(
1416 subreq
, struct tevent_req
);
1417 struct recovery_state
*state
= tevent_req_data(
1418 req
, struct recovery_state
);
1419 struct ctdb_reply_control
**reply
;
1420 struct ctdb_req_control request
;
1425 status
= ctdb_client_control_multi_recv(subreq
, &ret
, state
, &err_list
,
1427 TALLOC_FREE(subreq
);
1432 ret2
= ctdb_client_control_multi_error(state
->pnn_list
,
1436 LOG("control GET_CAPABILITIES failed on node %u,"
1437 " ret=%d\n", pnn
, ret2
);
1439 LOG("control GET_CAPABILITIES failed, ret=%d\n", ret
);
1441 tevent_req_error(req
, ret
);
1445 /* Make the array size same as nodemap */
1446 state
->caps
= talloc_zero_array(state
, uint32_t,
1447 state
->nodemap
->num
);
1448 if (tevent_req_nomem(state
->caps
, req
)) {
1452 for (i
=0; i
<state
->count
; i
++) {
1455 pnn
= state
->pnn_list
[i
];
1456 ret
= ctdb_reply_control_get_capabilities(reply
[i
],
1459 LOG("control GET_CAPABILITIES failed on node %u\n", pnn
);
1460 tevent_req_error(req
, EPROTO
);
1467 ctdb_req_control_get_dbmap(&request
);
1468 subreq
= ctdb_client_control_send(state
, state
->ev
, state
->client
,
1469 state
->destnode
, TIMEOUT(),
1471 if (tevent_req_nomem(subreq
, req
)) {
1474 tevent_req_set_callback(subreq
, recovery_dbmap_done
, req
);
1477 static void recovery_dbmap_done(struct tevent_req
*subreq
)
1479 struct tevent_req
*req
= tevent_req_callback_data(
1480 subreq
, struct tevent_req
);
1481 struct recovery_state
*state
= tevent_req_data(
1482 req
, struct recovery_state
);
1483 struct ctdb_reply_control
*reply
;
1484 struct ctdb_req_control request
;
1488 status
= ctdb_client_control_recv(subreq
, &ret
, state
, &reply
);
1489 TALLOC_FREE(subreq
);
1491 LOG("control GET_DBMAP failed to node %u, ret=%d\n",
1492 state
->destnode
, ret
);
1493 tevent_req_error(req
, ret
);
1497 ret
= ctdb_reply_control_get_dbmap(reply
, state
, &state
->dbmap
);
1499 LOG("control GET_DBMAP failed, ret=%d\n", ret
);
1500 tevent_req_error(req
, ret
);
1504 ctdb_req_control_set_recmode(&request
, CTDB_RECOVERY_ACTIVE
);
1505 subreq
= ctdb_client_control_multi_send(state
, state
->ev
,
1507 state
->pnn_list
, state
->count
,
1508 TIMEOUT(), &request
);
1509 if (tevent_req_nomem(subreq
, req
)) {
1512 tevent_req_set_callback(subreq
, recovery_active_done
, req
);
1515 static void recovery_active_done(struct tevent_req
*subreq
)
1517 struct tevent_req
*req
= tevent_req_callback_data(
1518 subreq
, struct tevent_req
);
1519 struct recovery_state
*state
= tevent_req_data(
1520 req
, struct recovery_state
);
1521 struct ctdb_req_control request
;
1522 struct ctdb_vnn_map
*vnnmap
;
1527 status
= ctdb_client_control_multi_recv(subreq
, &ret
, NULL
, &err_list
,
1529 TALLOC_FREE(subreq
);
1534 ret2
= ctdb_client_control_multi_error(state
->pnn_list
,
1538 LOG("failed to set recovery mode to ACTIVE on node %u,"
1539 " ret=%d\n", pnn
, ret2
);
1541 LOG("failed to set recovery mode to ACTIVE, ret=%d\n",
1544 tevent_req_error(req
, ret
);
1548 LOG("set recovery mode to ACTIVE\n");
1550 /* Calculate new VNNMAP */
1552 for (i
=0; i
<state
->nodemap
->num
; i
++) {
1553 if (state
->nodemap
->node
[i
].flags
& NODE_FLAGS_INACTIVE
) {
1556 if (!(state
->caps
[i
] & CTDB_CAP_LMASTER
)) {
1563 LOG("no active lmasters found. Adding recmaster anyway\n");
1566 vnnmap
= talloc_zero(state
, struct ctdb_vnn_map
);
1567 if (tevent_req_nomem(vnnmap
, req
)) {
1571 vnnmap
->size
= (count
== 0 ? 1 : count
);
1572 vnnmap
->map
= talloc_array(vnnmap
, uint32_t, vnnmap
->size
);
1573 if (tevent_req_nomem(vnnmap
->map
, req
)) {
1578 vnnmap
->map
[0] = state
->destnode
;
1581 for (i
=0; i
<state
->nodemap
->num
; i
++) {
1582 if (state
->nodemap
->node
[i
].flags
&
1583 NODE_FLAGS_INACTIVE
) {
1586 if (!(state
->caps
[i
] & CTDB_CAP_LMASTER
)) {
1590 vnnmap
->map
[count
] = state
->nodemap
->node
[i
].pnn
;
1595 vnnmap
->generation
= state
->generation
;
1597 talloc_free(state
->vnnmap
);
1598 state
->vnnmap
= vnnmap
;
1600 ctdb_req_control_start_recovery(&request
);
1601 subreq
= ctdb_client_control_multi_send(state
, state
->ev
,
1603 state
->pnn_list
, state
->count
,
1604 TIMEOUT(), &request
);
1605 if (tevent_req_nomem(subreq
, req
)) {
1608 tevent_req_set_callback(subreq
, recovery_start_recovery_done
, req
);
1611 static void recovery_start_recovery_done(struct tevent_req
*subreq
)
1613 struct tevent_req
*req
= tevent_req_callback_data(
1614 subreq
, struct tevent_req
);
1615 struct recovery_state
*state
= tevent_req_data(
1616 req
, struct recovery_state
);
1617 struct ctdb_req_control request
;
1622 status
= ctdb_client_control_multi_recv(subreq
, &ret
, NULL
, &err_list
,
1624 TALLOC_FREE(subreq
);
1629 ret2
= ctdb_client_control_multi_error(state
->pnn_list
,
1633 LOG("failed to run start_recovery event on node %u,"
1634 " ret=%d\n", pnn
, ret2
);
1636 LOG("failed to run start_recovery event, ret=%d\n",
1639 tevent_req_error(req
, ret
);
1643 LOG("start_recovery event finished\n");
1645 ctdb_req_control_setvnnmap(&request
, state
->vnnmap
);
1646 subreq
= ctdb_client_control_multi_send(state
, state
->ev
,
1648 state
->pnn_list
, state
->count
,
1649 TIMEOUT(), &request
);
1650 if (tevent_req_nomem(subreq
, req
)) {
1653 tevent_req_set_callback(subreq
, recovery_vnnmap_update_done
, req
);
1656 static void recovery_vnnmap_update_done(struct tevent_req
*subreq
)
1658 struct tevent_req
*req
= tevent_req_callback_data(
1659 subreq
, struct tevent_req
);
1660 struct recovery_state
*state
= tevent_req_data(
1661 req
, struct recovery_state
);
1666 status
= ctdb_client_control_multi_recv(subreq
, &ret
, NULL
, &err_list
,
1668 TALLOC_FREE(subreq
);
1673 ret2
= ctdb_client_control_multi_error(state
->pnn_list
,
1677 LOG("failed to update VNNMAP on node %u, ret=%d\n",
1680 LOG("failed to update VNNMAP, ret=%d\n", ret
);
1682 tevent_req_error(req
, ret
);
1686 LOG("updated VNNMAP\n");
1688 subreq
= db_recovery_send(state
, state
->ev
, state
->client
,
1689 state
->dbmap
, state
->tun_list
,
1690 state
->pnn_list
, state
->count
,
1691 state
->vnnmap
->generation
);
1692 if (tevent_req_nomem(subreq
, req
)) {
1695 tevent_req_set_callback(subreq
, recovery_db_recovery_done
, req
);
1698 static void recovery_db_recovery_done(struct tevent_req
*subreq
)
1700 struct tevent_req
*req
= tevent_req_callback_data(
1701 subreq
, struct tevent_req
);
1702 struct recovery_state
*state
= tevent_req_data(
1703 req
, struct recovery_state
);
1704 struct ctdb_req_control request
;
1708 status
= db_recovery_recv(subreq
, &count
);
1709 TALLOC_FREE(subreq
);
1711 LOG("%d databases recovered\n", count
);
1714 tevent_req_error(req
, EIO
);
1718 ctdb_req_control_set_recmode(&request
, CTDB_RECOVERY_NORMAL
);
1719 subreq
= ctdb_client_control_multi_send(state
, state
->ev
,
1721 state
->pnn_list
, state
->count
,
1722 TIMEOUT(), &request
);
1723 if (tevent_req_nomem(subreq
, req
)) {
1726 tevent_req_set_callback(subreq
, recovery_normal_done
, req
);
1729 static void recovery_normal_done(struct tevent_req
*subreq
)
1731 struct tevent_req
*req
= tevent_req_callback_data(
1732 subreq
, struct tevent_req
);
1733 struct recovery_state
*state
= tevent_req_data(
1734 req
, struct recovery_state
);
1735 struct ctdb_req_control request
;
1740 status
= ctdb_client_control_multi_recv(subreq
, &ret
, state
, &err_list
,
1742 TALLOC_FREE(subreq
);
1747 ret2
= ctdb_client_control_multi_error(state
->pnn_list
,
1751 LOG("failed to set recovery mode to NORMAL on node %u,"
1752 " ret=%d\n", pnn
, ret2
);
1754 LOG("failed to set recovery mode to NORMAL, ret=%d\n",
1757 tevent_req_error(req
, ret
);
1761 LOG("set recovery mode to NORMAL\n");
1763 ctdb_req_control_end_recovery(&request
);
1764 subreq
= ctdb_client_control_multi_send(state
, state
->ev
,
1766 state
->pnn_list
, state
->count
,
1767 TIMEOUT(), &request
);
1768 if (tevent_req_nomem(subreq
, req
)) {
1771 tevent_req_set_callback(subreq
, recovery_end_recovery_done
, req
);
1774 static void recovery_end_recovery_done(struct tevent_req
*subreq
)
1776 struct tevent_req
*req
= tevent_req_callback_data(
1777 subreq
, struct tevent_req
);
1778 struct recovery_state
*state
= tevent_req_data(
1779 req
, struct recovery_state
);
1784 status
= ctdb_client_control_multi_recv(subreq
, &ret
, state
, &err_list
,
1786 TALLOC_FREE(subreq
);
1791 ret2
= ctdb_client_control_multi_error(state
->pnn_list
,
1795 LOG("failed to run recovered event on node %u,"
1796 " ret=%d\n", pnn
, ret2
);
1798 LOG("failed to run recovered event, ret=%d\n", ret
);
1800 tevent_req_error(req
, ret
);
1804 LOG("recovered event finished\n");
1806 tevent_req_done(req
);
1809 static void recovery_recv(struct tevent_req
*req
, int *perr
)
1813 if (tevent_req_is_unix_error(req
, &err
)) {
1821 static void usage(const char *progname
)
1823 fprintf(stderr
, "\nUsage: %s <log-fd> <output-fd> <ctdb-socket-path> <generation>\n",
1829 * Arguments - log fd, write fd, socket path, generation
1831 int main(int argc
, char *argv
[])
1833 int log_fd
, write_fd
;
1834 const char *sockpath
;
1835 TALLOC_CTX
*mem_ctx
;
1836 struct tevent_context
*ev
;
1837 struct ctdb_client_context
*client
;
1839 struct tevent_req
*req
;
1840 uint32_t generation
;
1847 log_fd
= atoi(argv
[1]);
1848 if (log_fd
!= STDOUT_FILENO
&& log_fd
!= STDERR_FILENO
) {
1849 close(STDOUT_FILENO
);
1850 close(STDERR_FILENO
);
1851 dup2(log_fd
, STDOUT_FILENO
);
1852 dup2(log_fd
, STDERR_FILENO
);
1856 write_fd
= atoi(argv
[2]);
1858 generation
= (uint32_t)strtoul(argv
[4], NULL
, 0);
1860 mem_ctx
= talloc_new(NULL
);
1861 if (mem_ctx
== NULL
) {
1862 LOG("talloc_new() failed\n");
1866 ev
= tevent_context_init(mem_ctx
);
1868 LOG("tevent_context_init() failed\n");
1872 ret
= ctdb_client_init(mem_ctx
, ev
, sockpath
, &client
);
1874 LOG("ctdb_client_init() failed, ret=%d\n", ret
);
1878 req
= recovery_send(mem_ctx
, ev
, client
, generation
);
1880 LOG("database_recover_send() failed\n");
1884 if (! tevent_req_poll(req
, ev
)) {
1885 LOG("tevent_req_poll() failed\n");
1889 recovery_recv(req
, &ret
);
1892 LOG("database recovery failed, ret=%d\n", ret
);
1896 sys_write(write_fd
, &ret
, sizeof(ret
));
1900 talloc_free(mem_ctx
);