ctdb-client: Add new client API implementation
authorAmitay Isaacs <amitay@gmail.com>
Tue, 14 Apr 2015 14:14:25 +0000 (00:14 +1000)
committerAmitay Isaacs <amitay@samba.org>
Wed, 7 Oct 2015 12:53:29 +0000 (14:53 +0200)
Signed-off-by: Amitay Isaacs <amitay@gmail.com>
Reviewed-by: Martin Schwenke <martin@meltin.net>
ctdb/client/client.h [new file with mode: 0644]
ctdb/client/client_call.c [new file with mode: 0644]
ctdb/client/client_connect.c [new file with mode: 0644]
ctdb/client/client_control.c [new file with mode: 0644]
ctdb/client/client_control_sync.c [new file with mode: 0644]
ctdb/client/client_db.c [new file with mode: 0644]
ctdb/client/client_message.c [new file with mode: 0644]
ctdb/client/client_message_sync.c [new file with mode: 0644]
ctdb/client/client_private.h [new file with mode: 0644]
ctdb/client/client_util.c [new file with mode: 0644]
ctdb/wscript

diff --git a/ctdb/client/client.h b/ctdb/client/client.h
new file mode 100644 (file)
index 0000000..ba40202
--- /dev/null
@@ -0,0 +1,807 @@
+/*
+   CTDB client code
+
+   Copyright (C) Amitay Isaacs  2015
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __CTDB_CLIENT_H__
+#define __CTDB_CLIENT_H__
+
+#include "protocol/protocol.h"
+#include "common/srvid.h"
+
+struct ctdb_client_context;
+struct ctdb_db_context;
+struct ctdb_record_handle;
+
+/* from client/client_connect.c */
+
+int ctdb_client_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                    const char *sockpath, struct ctdb_client_context **ret);
+
+uint32_t ctdb_client_pnn(struct ctdb_client_context *client);
+
+void ctdb_client_wait(struct tevent_context *ev, bool *done);
+
+struct tevent_req *ctdb_recovery_wait_send(TALLOC_CTX *mem_ctx,
+                                          struct tevent_context *ev,
+                                          struct ctdb_client_context *client);
+
+bool ctdb_recovery_wait_recv(struct tevent_req *req, int *perr);
+
+/* from client/client_call.c */
+
+struct tevent_req *ctdb_client_call_send(TALLOC_CTX *mem_ctx,
+                                        struct tevent_context *ev,
+                                        struct ctdb_client_context *client,
+                                        struct ctdb_req_call *request);
+
+bool ctdb_client_call_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+                          struct ctdb_reply_call **reply, int *perr);
+
+
+/* from client/client_message.c */
+
+struct tevent_req *ctdb_client_message_send(TALLOC_CTX *mem_ctx,
+                                           struct tevent_context *ev,
+                                           struct ctdb_client_context *client,
+                                           uint32_t destnode,
+                                           struct ctdb_req_message *message);
+
+bool ctdb_client_message_recv(struct tevent_req *req, int *perr);
+
+int ctdb_client_message(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                       struct ctdb_client_context *client,
+                       uint32_t destnode, struct ctdb_req_message *message);
+
+int ctdb_client_set_message_handler(TALLOC_CTX *mem_ctx,
+                                   struct tevent_context *ev,
+                                   struct ctdb_client_context *client,
+                                   uint64_t srvid, srvid_handler_fn handler,
+                                   void *private_data);
+
+int ctdb_client_remove_message_handler(TALLOC_CTX *mem_ctx,
+                                      struct tevent_context *ev,
+                                      struct ctdb_client_context *client,
+                                      uint64_t srvid, void *private_data);
+
+/* from client/client_message_sync.c */
+
+int ctdb_message_recd_update_ip(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                               struct ctdb_client_context *client,
+                               int destnode, struct ctdb_public_ip *pubip);
+
+int ctdb_message_mem_dump(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                         struct ctdb_client_context *client,
+                         int destnode, struct ctdb_srvid_message *msg);
+
+int ctdb_message_reload_nodes(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                             struct ctdb_client_context *client,
+                             int destnode);
+
+int ctdb_message_takeover_run(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                             struct ctdb_client_context *client,
+                             int destnode, struct ctdb_srvid_message *msg);
+
+int ctdb_message_rebalance_node(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                               struct ctdb_client_context *client,
+                               int destnode, uint32_t pnn);
+
+int ctdb_message_disable_takeover_runs(TALLOC_CTX *mem_ctx,
+                                      struct tevent_context *ev,
+                                      struct ctdb_client_context *client,
+                                      int destnode,
+                                      struct ctdb_disable_message *disable);
+
+int ctdb_message_disable_recoveries(TALLOC_CTX *mem_ctx,
+                                   struct tevent_context *ev,
+                                   struct ctdb_client_context *client,
+                                   int destnode,
+                                   struct ctdb_disable_message *disable);
+
+int ctdb_message_disable_ip_check(TALLOC_CTX *mem_ctx,
+                                 struct tevent_context *ev,
+                                 struct ctdb_client_context *client,
+                                 int destnode, uint32_t timeout);
+
+/* from client/client_control.c */
+
+struct tevent_req *ctdb_client_control_send(TALLOC_CTX *mem_ctx,
+                                           struct tevent_context *ev,
+                                           struct ctdb_client_context *client,
+                                           uint32_t destnode,
+                                           struct timeval timeout,
+                                           struct ctdb_req_control *request);
+
+bool ctdb_client_control_recv(struct tevent_req *req, int *perr,
+                             TALLOC_CTX *mem_ctx,
+                             struct ctdb_reply_control **preply);
+
+struct tevent_req *ctdb_client_control_multi_send(
+                               TALLOC_CTX *mem_ctx,
+                               struct tevent_context *ev,
+                               struct ctdb_client_context *client,
+                               uint32_t *pnn_list, int count,
+                               struct timeval timeout,
+                               struct ctdb_req_control *request);
+
+bool ctdb_client_control_multi_recv(struct tevent_req *req, int *perr,
+                                   TALLOC_CTX *mem_ctx, int **perr_list,
+                                   struct ctdb_reply_control ***preply);
+
+int ctdb_client_control_multi_error(uint32_t *pnn_list, int count,
+                                   int *err_list, uint32_t *pnn);
+
+int ctdb_client_control(TALLOC_CTX *mem_ctx,
+                       struct tevent_context *ev,
+                       struct ctdb_client_context *client,
+                       uint32_t destnode,
+                       struct timeval timeout,
+                       struct ctdb_req_control *c,
+                       struct ctdb_reply_control **preply);
+
+int ctdb_client_control_multi(TALLOC_CTX *mem_ctx,
+                             struct tevent_context *ev,
+                             struct ctdb_client_context *client,
+                             uint32_t *pnn_list, int count,
+                             struct timeval timeout,
+                             struct ctdb_req_control *request,
+                             int **perr,
+                             struct ctdb_reply_control ***preply);
+
+/* from client/client_control_sync.c */
+
+int ctdb_ctrl_process_exists(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                            struct ctdb_client_context *client,
+                            int destnode, struct timeval timeout,
+                            pid_t pid, int *status);
+
+int ctdb_ctrl_statistics(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                        struct ctdb_client_context *client,
+                        int destnode, struct timeval timeout,
+                        struct ctdb_statistics **stats);
+
+int ctdb_ctrl_ping(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                  struct ctdb_client_context *client,
+                  int destnode, struct timeval timeout,
+                  int *num_clients);
+
+int ctdb_ctrl_getdbpath(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                       struct ctdb_client_context *client,
+                       int destnode, struct timeval timeout,
+                       uint32_t db_id, const char **db_path);
+
+int ctdb_ctrl_getvnnmap(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                       struct ctdb_client_context *client,
+                       int destnode, struct timeval timeout,
+                       struct ctdb_vnn_map **vnnmap);
+
+int ctdb_ctrl_getdebug(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                      struct ctdb_client_context *client,
+                      int destnode, struct timeval timeout,
+                      uint32_t *loglevel);
+
+int ctdb_ctrl_setdebug(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                      struct ctdb_client_context *client,
+                      int destnode, struct timeval timeout,
+                      uint32_t loglevel);
+
+int ctdb_ctrl_get_dbmap(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                       struct ctdb_client_context *client,
+                       int destnode, struct timeval timeout,
+                       struct ctdb_dbid_map **dbmap);
+
+int ctdb_ctrl_pull_db(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                     struct ctdb_client_context *client, int destnode,
+                     struct timeval timeout, struct ctdb_pulldb *pulldb,
+                     struct ctdb_rec_buffer **recbuf);
+
+int ctdb_ctrl_push_db(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                     struct ctdb_client_context *client, int destnode,
+                     struct timeval timeout, struct ctdb_rec_buffer *recbuf);
+
+int ctdb_ctrl_get_recmode(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                         struct ctdb_client_context *client,
+                         int destnode, struct timeval timeout,
+                         int *recmode);
+
+int ctdb_ctrl_set_recmode(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                         struct ctdb_client_context *client,
+                         int destnode, struct timeval timeout,
+                         int recmode);
+
+int ctdb_ctrl_statistics_reset(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                              struct ctdb_client_context *client,
+                              int destnode, struct timeval timeout);
+
+int ctdb_ctrl_db_attach(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                       struct ctdb_client_context *client,
+                       int destnode, struct timeval timeout,
+                       const char *db_name, uint32_t tdb_flags,
+                       uint32_t *db_id);
+
+int ctdb_ctrl_traverse_start(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                            struct ctdb_client_context *client,
+                            int destnode, struct timeval timeout,
+                            struct ctdb_traverse_start *traverse);
+
+int ctdb_ctrl_register_srvid(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                            struct ctdb_client_context *client,
+                            int destnode, struct timeval timeout,
+                            uint64_t srvid);
+
+int ctdb_ctrl_deregister_srvid(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                              struct ctdb_client_context *client,
+                              int destnode, struct timeval timeout,
+                              uint64_t srvid);
+
+int ctdb_ctrl_get_dbname(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                        struct ctdb_client_context *client,
+                        int destnode, struct timeval timeout,
+                        uint32_t db_id, const char **db_name);
+
+int ctdb_ctrl_enable_seqnum(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           uint32_t db_id);
+
+int ctdb_ctrl_update_seqnum(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           uint32_t db_id);
+
+int ctdb_ctrl_dump_memory(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                         struct ctdb_client_context *client,
+                         int destnode, struct timeval timeout,
+                         const char **mem_str);
+
+int ctdb_ctrl_get_pid(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                     struct ctdb_client_context *client,
+                     int destnode, struct timeval timeout,
+                     pid_t *pid);
+
+int ctdb_ctrl_get_recmaster(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           uint32_t *recmaster);
+
+int ctdb_ctrl_set_recmaster(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           uint32_t recmaster);
+
+int ctdb_ctrl_freeze(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                    struct ctdb_client_context *client,
+                    int destnode, struct timeval timeout,
+                    int priority);
+
+int ctdb_ctrl_thaw(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                  struct ctdb_client_context *client,
+                  int destnode, struct timeval timeout,
+                  int priority);
+
+int ctdb_ctrl_get_pnn(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                     struct ctdb_client_context *client,
+                     int destnode, struct timeval timeout,
+                     uint32_t *pnn);
+
+int ctdb_ctrl_shutdown(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                      struct ctdb_client_context *client,
+                      int destnode, struct timeval timeout);
+
+int ctdb_ctrl_get_monmode(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                         struct ctdb_client_context *client,
+                         int destnode, struct timeval timeout,
+                         int *mon_mode);
+
+int ctdb_ctrl_tcp_add(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                     struct ctdb_client_context *client,
+                     int destnode, struct timeval timeout,
+                     struct ctdb_connection *conn);
+
+int ctdb_ctrl_tcp_remove(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                        struct ctdb_client_context *client,
+                        int destnode, struct timeval timeout,
+                        struct ctdb_connection *conn);
+
+int ctdb_ctrl_set_tunable(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                         struct ctdb_client_context *client,
+                         int destnode, struct timeval timeout,
+                         struct ctdb_tunable *tunable);
+
+int ctdb_ctrl_get_tunable(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                         struct ctdb_client_context *client,
+                         int destnode, struct timeval timeout,
+                         const char *var, uint32_t *value);
+
+int ctdb_ctrl_list_tunables(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           struct ctdb_var_list **var_list);
+
+int ctdb_ctrl_modify_flags(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                          struct ctdb_client_context *client,
+                          int destnode, struct timeval timeout,
+                          uint32_t pnn, uint32_t old_flags,
+                          uint32_t new_flags);
+
+int ctdb_ctrl_get_all_tunables(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                              struct ctdb_client_context *client,
+                              int destnode, struct timeval timeout,
+                              struct ctdb_tunable_list **tun_list);
+
+int ctdb_ctrl_kill_tcp(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                      struct ctdb_client_context *client,
+                      int destnode, struct timeval timeout,
+                      struct ctdb_connection *conn);
+
+int ctdb_ctrl_get_tcp_tickle_list(TALLOC_CTX *mem_ctx,
+                                 struct tevent_context *ev,
+                                 struct ctdb_client_context *client,
+                                 int destnode, struct timeval timeout,
+                                 ctdb_sock_addr *addr,
+                                 struct ctdb_tickle_list **tickles);
+
+int ctdb_ctrl_set_tcp_tickle_list(TALLOC_CTX *mem_ctx,
+                                 struct tevent_context *ev,
+                                 struct ctdb_client_context *client,
+                                 int destnode, struct timeval timeout,
+                                 struct ctdb_tickle_list *tickles);
+
+int ctdb_ctrl_register_server_id(TALLOC_CTX *mem_ctx,
+                                struct tevent_context *ev,
+                                struct ctdb_client_context *client,
+                                int destnode, struct timeval timeout,
+                                struct ctdb_client_id *cid);
+
+int ctdb_ctrl_unregister_server_id(TALLOC_CTX *mem_ctx,
+                                  struct tevent_context *ev,
+                                  struct ctdb_client_context *client,
+                                  int destnode, struct timeval timeout,
+                                  struct ctdb_client_id *cid);
+
+int ctdb_ctrl_check_server_id(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                             struct ctdb_client_context *client,
+                             int destnode, struct timeval timeout,
+                             struct ctdb_client_id *cid);
+
+int ctdb_ctrl_get_server_id_list(TALLOC_CTX *mem_ctx,
+                                struct tevent_context *ev,
+                                struct ctdb_client_context *client,
+                                int destnode, struct timeval timeout,
+                                struct ctdb_client_id_map **cid_map);
+
+int ctdb_ctrl_db_attach_persistent(TALLOC_CTX *mem_ctx,
+                                  struct tevent_context *ev,
+                                  struct ctdb_client_context *client,
+                                  int destnode, struct timeval timeout,
+                                  const char *db_name, int tdb_flags,
+                                  uint32_t *db_id);
+
+int ctdb_ctrl_send_gratuitous_arp(TALLOC_CTX *mem_ctx,
+                                 struct tevent_context *ev,
+                                 struct ctdb_client_context *client,
+                                 int destnode, struct timeval timeout,
+                                 struct ctdb_addr_info *addr_info);
+
+int ctdb_ctrl_transaction_start(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                               struct ctdb_client_context *client,
+                               int destnode, struct timeval timeout,
+                               uint32_t tid);
+
+int ctdb_ctrl_transaction_commit(TALLOC_CTX *mem_ctx,
+                                struct tevent_context *ev,
+                                struct ctdb_client_context *client,
+                                int destnode, struct timeval timeout,
+                                uint32_t tid);
+
+int ctdb_ctrl_wipe_database(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           uint32_t db_id, uint32_t tid);
+
+int ctdb_ctrl_uptime(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                    struct ctdb_client_context *client,
+                    int destnode, struct timeval timeout,
+                    struct ctdb_uptime **uptime);
+
+int ctdb_ctrl_start_recovery(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                            struct ctdb_client_context *client,
+                            int destnode, struct timeval timeout);
+
+int ctdb_ctrl_end_recovery(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                          struct ctdb_client_context *client,
+                          int destnode, struct timeval timeout);
+
+int ctdb_ctrl_reload_nodes_file(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                               struct ctdb_client_context *client,
+                               int destnode, struct timeval timeout);
+
+int ctdb_ctrl_enable_monitor(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                            struct ctdb_client_context *client,
+                            int destnode, struct timeval timeout);
+
+int ctdb_ctrl_disable_monitor(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                             struct ctdb_client_context *client,
+                             int destnode, struct timeval timeout);
+
+int ctdb_ctrl_add_public_ip(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           struct ctdb_addr_info *addr_info);
+
+int ctdb_ctrl_del_public_ip(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           struct ctdb_addr_info *addr_info);
+
+int ctdb_ctrl_run_eventscripts(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                              struct ctdb_client_context *client,
+                              int destnode, struct timeval timeout,
+                              const char *event);
+
+int ctdb_ctrl_get_capabilities(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                              struct ctdb_client_context *client,
+                              int destnode, struct timeval timeout,
+                              uint32_t *caps);
+
+int ctdb_ctrl_release_ip(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                        struct ctdb_client_context *client,
+                        int destnode, struct timeval timeout,
+                        struct ctdb_public_ip *pubip);
+
+int ctdb_ctrl_takeover_ip(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                         struct ctdb_client_context *client,
+                         int destnode, struct timeval timeout,
+                         struct ctdb_public_ip *pubip);
+
+int ctdb_ctrl_get_public_ips(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                            struct ctdb_client_context *client,
+                            int destnode, struct timeval timeout,
+                            struct ctdb_public_ip_list **pubip_list);
+
+int ctdb_ctrl_get_nodemap(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                         struct ctdb_client_context *client,
+                         int destnode, struct timeval timeout,
+                         struct ctdb_node_map **nodemap);
+
+int ctdb_ctrl_get_event_script_status(TALLOC_CTX *mem_ctx,
+                                     struct tevent_context *ev,
+                                     struct ctdb_client_context *client,
+                                     int destnode, struct timeval timeout,
+                                     enum ctdb_event event,
+                                     struct ctdb_script_list **slist);
+
+int ctdb_ctrl_traverse_kill(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           struct ctdb_traverse_start *traverse);
+
+int ctdb_ctrl_get_reclock_file(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                              struct ctdb_client_context *client,
+                              int destnode, struct timeval timeout,
+                              const char **reclock_file);
+
+int ctdb_ctrl_set_reclock_file(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                              struct ctdb_client_context *client,
+                              int destnode, struct timeval timeout,
+                              const char *reclock_file);
+
+int ctdb_ctrl_stop_node(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                       struct ctdb_client_context *client,
+                       int destnode, struct timeval timeout);
+
+int ctdb_ctrl_continue_node(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout);
+
+int ctdb_ctrl_set_natgwstate(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                            struct ctdb_client_context *client,
+                            int destnode, struct timeval timeout,
+                            uint32_t natgw_role);
+
+int ctdb_ctrl_set_lmasterrole(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                             struct ctdb_client_context *client,
+                             int destnode, struct timeval timeout,
+                             uint32_t lmaster_role);
+
+int ctdb_ctrl_set_recmasterrole(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                               struct ctdb_client_context *client,
+                               int destnode, struct timeval timeout,
+                               uint32_t recmaster_role);
+
+int ctdb_ctrl_enable_script(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           const char *script);
+
+int ctdb_ctrl_disable_script(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                            struct ctdb_client_context *client,
+                            int destnode, struct timeval timeout,
+                            const char *script);
+
+int ctdb_ctrl_set_ban_state(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           struct ctdb_ban_state *ban_state);
+
+int ctdb_ctrl_get_ban_state(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           struct ctdb_ban_state **ban_state);
+
+int ctdb_ctrl_set_db_priority(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                             struct ctdb_client_context *client,
+                             int destnode, struct timeval timeout,
+                             uint32_t db_id, int priority);
+
+int ctdb_ctrl_get_db_priority(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                             struct ctdb_client_context *client,
+                             int destnode, struct timeval timeout,
+                             uint32_t db_id, uint32_t *priority);
+
+int ctdb_ctrl_transaction_cancel(TALLOC_CTX *mem_ctx,
+                                struct tevent_context *ev,
+                                struct ctdb_client_context *client,
+                                int destnode, struct timeval timeout,
+                                uint32_t tid);
+
+int ctdb_ctrl_register_notify(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                             struct ctdb_client_context *client,
+                             int destnode, struct timeval timeout,
+                             struct ctdb_notify_data *notify);
+
+int ctdb_ctrl_deregister_notify(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                               struct ctdb_client_context *client,
+                               int destnode, struct timeval timeout,
+                               uint64_t srvid);
+
+int ctdb_ctrl_trans3_commit(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           struct ctdb_rec_buffer *recbuf);
+
+int ctdb_ctrl_get_db_seqnum(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           uint32_t db_id, uint64_t *seqnum);
+
+int ctdb_ctrl_db_set_healthy(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                            struct ctdb_client_context *client,
+                            int destnode, struct timeval timeout,
+                            uint32_t db_id);
+
+int ctdb_ctrl_db_get_health(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           uint32_t db_id, const char **reason);
+
+int ctdb_ctrl_get_public_ip_info(TALLOC_CTX *mem_ctx,
+                                struct tevent_context *ev,
+                                struct ctdb_client_context *client,
+                                int destnode, struct timeval timeout,
+                                ctdb_sock_addr *addr,
+                                struct ctdb_public_ip_info **ipinfo);
+
+int ctdb_ctrl_get_ifaces(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                        struct ctdb_client_context *client,
+                        int destnode, struct timeval timeout,
+                        struct ctdb_iface_list **iface_list);
+
+int ctdb_ctrl_set_iface_link_state(TALLOC_CTX *mem_ctx,
+                                  struct tevent_context *ev,
+                                  struct ctdb_client_context *client,
+                                  int destnode, struct timeval timeout,
+                                  struct ctdb_iface *iface);
+
+int ctdb_ctrl_tcp_add_delayed_update(TALLOC_CTX *mem_ctx,
+                                    struct tevent_context *ev,
+                                    struct ctdb_client_context *client,
+                                    int destnode, struct timeval timeout,
+                                    struct ctdb_connection *conn);
+
+int ctdb_ctrl_get_stat_history(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                              struct ctdb_client_context *client,
+                              int destnode, struct timeval timeout,
+                              struct ctdb_statistics_list **stats_list);
+
+int ctdb_ctrl_schedule_for_deletion(TALLOC_CTX *mem_ctx,
+                                   struct tevent_context *ev,
+                                   struct ctdb_client_context *client,
+                                   int destnode, struct timeval timeout,
+                                   struct ctdb_key_data *key);
+
+int ctdb_ctrl_set_db_readonly(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                             struct ctdb_client_context *client,
+                             int destnode, struct timeval timeout,
+                             uint32_t db_id);
+
+int ctdb_ctrl_check_srvids(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                          struct ctdb_client_context *client,
+                          int destnode, struct timeval timeout,
+                          uint64_t *srvid, int count, uint8_t **result);
+
+int ctdb_ctrl_traverse_start_ext(TALLOC_CTX *mem_ctx,
+                                struct tevent_context *ev,
+                                struct ctdb_client_context *client,
+                                int destnode, struct timeval timeout,
+                                struct ctdb_traverse_start_ext *traverse);
+
+int ctdb_ctrl_get_db_statistics(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                               struct ctdb_client_context *client,
+                               int destnode, struct timeval timeout,
+                               uint32_t db_id,
+                               struct ctdb_db_statistics **dbstats);
+
+int ctdb_ctrl_set_db_sticky(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           uint32_t db_id);
+
+int ctdb_ctrl_reload_public_ips(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                               struct ctdb_client_context *client,
+                               int destnode, struct timeval timeout);
+
+int ctdb_ctrl_ipreallocated(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout);
+
+int ctdb_ctrl_get_runstate(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                          struct ctdb_client_context *client,
+                          int destnode, struct timeval timeout,
+                          enum ctdb_runstate *runstate);
+
+int ctdb_ctrl_db_detach(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                       struct ctdb_client_context *client,
+                       int destnode, struct timeval timeout,
+                       uint32_t db_id);
+
+int ctdb_ctrl_get_nodes_file(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                            struct ctdb_client_context *client,
+                            int destnode, struct timeval timeout,
+                            struct ctdb_node_map **nodemap);
+
+/* from client/client_db.c */
+
+struct tevent_req *ctdb_attach_send(TALLOC_CTX *mem_ctx,
+                                   struct tevent_context *ev,
+                                   struct ctdb_client_context *client,
+                                   struct timeval timeout,
+                                   const char *db_name, uint8_t db_flags);
+
+bool ctdb_attach_recv(struct tevent_req *req, int *perr,
+                     struct ctdb_db_context **out);
+
+int ctdb_attach(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+               struct ctdb_client_context *client,
+               struct timeval timeout,
+               const char *db_name, uint8_t db_flags,
+               struct ctdb_db_context **out);
+
+int ctdb_detach(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+               struct ctdb_client_context *client,
+               struct timeval timeout, uint32_t db_id);
+
+uint32_t ctdb_db_id(struct ctdb_db_context *db);
+
+int ctdb_db_traverse(struct ctdb_db_context *db, bool readonly,
+                    bool extract_header,
+                    ctdb_rec_parser_func_t parser, void *private_data);
+
+struct tevent_req *ctdb_fetch_lock_send(TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct ctdb_client_context *client,
+                                       struct ctdb_db_context *db,
+                                       TDB_DATA key, bool readonly);
+
+struct ctdb_record_handle *ctdb_fetch_lock_recv(struct tevent_req *req,
+                                               struct ctdb_ltdb_header *header,
+                                               TALLOC_CTX *mem_ctx,
+                                               TDB_DATA *data, int *perr);
+
+int ctdb_fetch_lock(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                   struct ctdb_client_context *client,
+                   struct ctdb_db_context *db, TDB_DATA key, bool readonly,
+                   struct ctdb_record_handle **out,
+                   struct ctdb_ltdb_header *header, TDB_DATA *data);
+
+int ctdb_store_record(struct ctdb_record_handle *h, TDB_DATA data);
+
+int ctdb_delete_record(struct ctdb_record_handle *h);
+
+struct tevent_req *ctdb_g_lock_lock_send(TALLOC_CTX *mem_ctx,
+                                        struct tevent_context *ev,
+                                        struct ctdb_client_context *client,
+                                        struct ctdb_db_context *db,
+                                        const char *keyname,
+                                        struct ctdb_server_id *sid,
+                                        bool readonly);
+
+bool ctdb_g_lock_lock_recv(struct tevent_req *req, int *perr);
+
+struct tevent_req *ctdb_g_lock_unlock_send(TALLOC_CTX *mem_ctx,
+                                          struct tevent_context *ev,
+                                          struct ctdb_client_context *client,
+                                          struct ctdb_db_context *db,
+                                          const char *keyname,
+                                          struct ctdb_server_id sid);
+
+bool ctdb_g_lock_unlock_recv(struct tevent_req *req, int *perr);
+
+struct tevent_req *ctdb_transaction_start_send(TALLOC_CTX *mem_ctx,
+                                              struct tevent_context *ev,
+                                              struct ctdb_client_context *client,
+                                              struct timeval timeout,
+                                              struct ctdb_db_context *db,
+                                              bool readonly);
+
+struct ctdb_transaction_handle *ctdb_transaction_start_recv(
+                                       struct tevent_req *req,
+                                       int *perr);
+
+int ctdb_transaction_start(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                          struct ctdb_client_context *client,
+                          struct timeval timeout,
+                          struct ctdb_db_context *db, bool readonly,
+                          struct ctdb_transaction_handle **out);
+
+int ctdb_transaction_fetch_record(struct ctdb_transaction_handle *h,
+                                 TDB_DATA key,
+                                 TALLOC_CTX *mem_ctx, TDB_DATA *data);
+
+int ctdb_transaction_store_record(struct ctdb_transaction_handle *h,
+                                 TDB_DATA key, TDB_DATA data);
+
+int ctdb_transaction_delete_record(struct ctdb_transaction_handle *h,
+                                  TDB_DATA key);
+
+struct tevent_req *ctdb_transaction_commit_send(
+                                       TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct ctdb_transaction_handle *h);
+
+bool ctdb_transaction_commit_recv(struct tevent_req *req, int *perr);
+
+int ctdb_transaction_commit(struct ctdb_transaction_handle *h);
+
+int ctdb_transaction_cancel(struct ctdb_transaction_handle *h);
+
+/* from client/client_util.c */
+
+int list_of_nodes(struct ctdb_node_map *nodemap,
+                 uint32_t flags_mask, uint32_t exclude_pnn,
+                 TALLOC_CTX *mem_ctx, uint32_t **pnn_list);
+
+int list_of_active_nodes(struct ctdb_node_map *nodemap, uint32_t exclude_pnn,
+                        TALLOC_CTX *mem_ctx, uint32_t **pnn_list);
+
+int list_of_connected_nodes(struct ctdb_node_map *nodemap,
+                           uint32_t exclude_pnn,
+                           TALLOC_CTX *mem_ctx, uint32_t **pnn_list);
+
+int ctdb_ctrl_modflags(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                      struct ctdb_client_context *client,
+                      uint32_t destnode, struct timeval timeout,
+                      uint32_t set, uint32_t clear);
+
+bool ctdb_server_id_equal(struct ctdb_server_id *sid1,
+                         struct ctdb_server_id *sid2);
+
+int ctdb_server_id_exists(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                         struct ctdb_client_context *client,
+                         struct ctdb_server_id *sid, bool *exists);
+
+#endif /* __CTDB_CLIENT_H__ */
diff --git a/ctdb/client/client_call.c b/ctdb/client/client_call.c
new file mode 100644 (file)
index 0000000..aa8a05b
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+   CTDB client code
+
+   Copyright (C) Amitay Isaacs  2015
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "system/network.h"
+#include "system/filesys.h"
+
+#include <talloc.h>
+#include <tevent.h>
+#include <tdb.h>
+
+#include "lib/util/tevent_unix.h"
+
+#include "common/reqid.h"
+#include "common/srvid.h"
+#include "common/comm.h"
+
+#include "protocol/protocol.h"
+#include "protocol/protocol_api.h"
+
+#include "client/client_private.h"
+#include "client/client.h"
+
+
+/*
+ * Handle REQ_CALL and REPLY_CALL
+ */
+
+struct ctdb_client_call_state {
+       struct ctdb_client_context *client;
+       uint32_t reqid;
+       struct ctdb_reply_call *reply;
+       struct tevent_req *req;
+};
+
+static int ctdb_client_call_state_destructor(
+       struct ctdb_client_call_state *state);
+static void ctdb_client_call_done(struct tevent_req *subreq);
+
+struct tevent_req *ctdb_client_call_send(TALLOC_CTX *mem_ctx,
+                                        struct tevent_context *ev,
+                                        struct ctdb_client_context *client,
+                                        struct ctdb_req_call *request)
+{
+       struct ctdb_req_header h;
+       struct tevent_req *req, *subreq;
+       struct ctdb_client_call_state *state;
+       uint32_t reqid;
+       uint8_t *buf;
+       size_t buflen;
+       int ret;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct ctdb_client_call_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       reqid = reqid_new(client->idr, state);
+       if (reqid == REQID_INVALID) {
+               talloc_free(req);
+               return NULL;
+       }
+
+       state->client = client;
+       state->reqid = reqid;
+       state->req = req;
+       state->reply = talloc_zero(state, struct ctdb_reply_call);
+       if (tevent_req_nomem(state->reply, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       talloc_set_destructor(state, ctdb_client_call_state_destructor);
+
+       ctdb_req_header_fill(&h, 0, CTDB_REQ_CALL, CTDB_CURRENT_NODE,
+                            client->pnn, reqid);
+
+       ret = ctdb_req_call_push(&h, request, state, &buf, &buflen);
+       if (ret != 0) {
+               tevent_req_error(req, ret);
+               return tevent_req_post(req, ev);
+       }
+
+       subreq = comm_write_send(state, ev, client->comm, buf, buflen);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, ctdb_client_call_done, req);
+
+       return req;
+}
+
+static int ctdb_client_call_state_destructor(
+       struct ctdb_client_call_state *state)
+{
+       reqid_remove(state->client->idr, state->reqid);
+       return 0;
+}
+
+static void ctdb_client_call_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       bool status;
+       int ret;
+
+       status = comm_write_recv(subreq, &ret);
+       TALLOC_FREE(subreq);
+       if (! status) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       /* wait for the reply */
+}
+
+void ctdb_client_reply_call(struct ctdb_client_context *client,
+                           uint8_t *buf, size_t buflen, uint32_t reqid)
+{
+       struct ctdb_req_header h;
+       struct ctdb_client_call_state *state;
+       int ret;
+
+       state = reqid_find(client->idr, reqid, struct ctdb_client_call_state);
+       if (state == NULL) {
+               return;
+       }
+
+       if (reqid != state->reqid) {
+               return;
+       }
+
+       ret = ctdb_reply_call_pull(buf, buflen, &h, state, state->reply);
+       if (ret != 0) {
+               tevent_req_error(state->req, ret);
+               return;
+       }
+
+       tevent_req_done(state->req);
+}
+
+bool ctdb_client_call_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+                          struct ctdb_reply_call **reply, int *perr)
+{
+       struct ctdb_client_call_state *state = tevent_req_data(
+               req, struct ctdb_client_call_state);
+       int err;
+
+       if (tevent_req_is_unix_error(req, &err)) {
+               if (perr != NULL) {
+                       *perr = err;
+               }
+               return false;
+       }
+
+       if (reply != NULL) {
+               *reply = talloc_steal(mem_ctx, state->reply);
+       }
+
+       return true;
+}
diff --git a/ctdb/client/client_connect.c b/ctdb/client/client_connect.c
new file mode 100644 (file)
index 0000000..5f2b62e
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+   CTDB client code
+
+   Copyright (C) Amitay Isaacs  2015
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "system/network.h"
+#include "system/filesys.h"
+
+#include <talloc.h>
+#include <tevent.h>
+#include <tdb.h>
+
+#include "common/reqid.h"
+#include "common/srvid.h"
+#include "common/comm.h"
+
+#include "lib/util/tevent_unix.h"
+#include "lib/util/debug.h"
+#include "ctdb_logging.h"
+
+#include "protocol/protocol.h"
+#include "protocol/protocol_api.h"
+
+#include "client/client_private.h"
+#include "client/client.h"
+
+static int ctdb_client_connect(struct ctdb_client_context *client,
+                              struct tevent_context *ev,
+                              const char *sockpath);
+
+int ctdb_client_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                    const char *sockpath, struct ctdb_client_context **out)
+{
+       struct ctdb_client_context *client;
+       int ret;
+
+       client = talloc_zero(mem_ctx, struct ctdb_client_context);
+       if (client == NULL) {
+               DEBUG(DEBUG_ERR, (__location__ " memory allocation error\n"));
+               return ENOMEM;
+       }
+
+       ret = reqid_init(client, INT_MAX-200, &client->idr);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR, ("reqid_init() failed, ret=%d\n", ret));
+               talloc_free(client);
+               return ret;
+       }
+
+       ret = srvid_init(client, &client->srv);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR, ("srvid_init() failed, ret=%d\n", ret));
+               talloc_free(client);
+               return ret;
+       }
+
+       client->fd = -1;
+       client->pnn = CTDB_UNKNOWN_PNN;
+
+       ret = ctdb_client_connect(client, ev, sockpath);
+       if (ret != 0) {
+               talloc_free(client);
+               return ret;
+       }
+
+       *out = client;
+       return 0;
+}
+
+static void client_read_handler(uint8_t *buf, size_t buflen,
+                               void *private_data);
+static void client_dead_handler(void *private_data);
+
+static int ctdb_client_connect(struct ctdb_client_context *client,
+                              struct tevent_context *ev, const char *sockpath)
+{
+       struct sockaddr_un addr;
+       size_t len;
+       int fd, ret;
+
+       if (sockpath == NULL) {
+               DEBUG(DEBUG_ERR, ("socket path cannot be NULL\n"));
+               return EINVAL;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       len = strlcpy(addr.sun_path, sockpath, sizeof(addr.sun_path));
+       if (len != strlen(sockpath)) {
+               DEBUG(DEBUG_ERR, ("socket path too long, len=%zu\n",
+                                 strlen(sockpath)));
+               return ENAMETOOLONG;
+       }
+
+       fd = socket(AF_UNIX, SOCK_STREAM, 0);
+       if (fd == -1) {
+               ret = errno;
+               DEBUG(DEBUG_ERR, ("socket() failed, errno=%d\n", ret));
+               return ret;
+       }
+
+       ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
+       if (ret == -1) {
+               ret = errno;
+               DEBUG(DEBUG_ERR, ("connect() failed, errno=%d\n", ret));
+               close(fd);
+               return ret;
+       }
+       client->fd = fd;
+
+       ret = comm_setup(client, ev, fd, client_read_handler, client,
+                        client_dead_handler, client, &client->comm);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR, ("comm_setup() failed, ret=%d\n", ret));
+               close(fd);
+               client->fd = -1;
+               return ret;
+       }
+
+       ret = ctdb_ctrl_get_pnn(client, ev, client, CTDB_CURRENT_NODE,
+                               tevent_timeval_zero(), &client->pnn);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR, ("failed to get current node pnn\n"));
+               close(fd);
+               client->fd = -1;
+               TALLOC_FREE(client->comm);
+               return ret;
+       }
+
+       return 0;
+}
+
+static void client_read_handler(uint8_t *buf, size_t buflen,
+                               void *private_data)
+{
+       struct ctdb_client_context *client = talloc_get_type_abort(
+               private_data, struct ctdb_client_context);
+       struct ctdb_req_header hdr;
+       int ret;
+
+       ret = ctdb_req_header_pull(discard_const(buf), buflen, &hdr);
+       if (ret != 0) {
+               DEBUG(DEBUG_WARNING, ("invalid header, ret=%d\n", ret));
+               return;
+       }
+
+       if (buflen != hdr.length) {
+               DEBUG(DEBUG_WARNING, ("packet size mismatch %zu != %d\n",
+                                     buflen, hdr.length));
+               return;
+       }
+
+       ret = ctdb_req_header_verify(&hdr, 0);
+       if (ret != 0) {
+               DEBUG(DEBUG_WARNING, ("invalid header, ret=%d\n", ret));
+               return;
+       }
+
+       switch (hdr.operation) {
+       case CTDB_REPLY_CALL:
+               ctdb_client_reply_call(client, buf, buflen, hdr.reqid);
+               break;
+
+       case CTDB_REQ_MESSAGE:
+               ctdb_client_req_message(client, buf, buflen, hdr.reqid);
+               break;
+
+       case CTDB_REPLY_CONTROL:
+               ctdb_client_reply_control(client, buf, buflen, hdr.reqid);
+               break;
+
+       default:
+               break;
+       }
+}
+
+static void client_dead_handler(void *private_data)
+{
+       struct ctdb_client_context *client = talloc_get_type_abort(
+               private_data, struct ctdb_client_context);
+
+       DEBUG(DEBUG_NOTICE, ("connection to daemon closed, exiting\n"));
+       talloc_free(client);
+       exit(1);
+}
+
+uint32_t ctdb_client_pnn(struct ctdb_client_context *client)
+{
+       return client->pnn;
+}
+
+void ctdb_client_wait(struct tevent_context *ev, bool *done)
+{
+       while (! (*done)) {
+               tevent_loop_once(ev);
+       }
+}
+
+struct ctdb_recovery_wait_state {
+       struct tevent_context *ev;
+       struct ctdb_client_context *client;
+};
+
+static void ctdb_recovery_wait_retry(struct tevent_req *subreq);
+
+struct tevent_req *ctdb_recovery_wait_send(TALLOC_CTX *mem_ctx,
+                                          struct tevent_context *ev,
+                                          struct ctdb_client_context *client)
+{
+       struct tevent_req *req, *subreq;
+       struct ctdb_recovery_wait_state *state;
+       int recmode;
+       int ret;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct ctdb_recovery_wait_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       state->ev = ev;
+       state->client = client;
+
+       ret = ctdb_ctrl_get_recmode(client, ev, client, client->pnn,
+                                   tevent_timeval_zero(), &recmode);
+       if (ret != 0) {
+               tevent_req_error(req, ret);
+               return tevent_req_post(req, ev);
+       }
+
+       if (recmode == CTDB_RECOVERY_NORMAL) {
+               tevent_req_done(req);
+               return tevent_req_post(req, ev);
+       }
+
+       subreq = tevent_wakeup_send(state, ev,
+                                   tevent_timeval_current_ofs(1, 0));
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, ctdb_recovery_wait_retry, req);
+
+       return req;
+}
+
+static void ctdb_recovery_wait_retry(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct ctdb_recovery_wait_state *state = tevent_req_data(
+               req, struct ctdb_recovery_wait_state);
+       int ret, recmode;
+       bool status;
+
+       status = tevent_wakeup_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (! status) {
+               tevent_req_error(req, ENOMEM);
+               return;
+       }
+
+       ret = ctdb_ctrl_get_recmode(state, state->ev, state->client,
+                                   ctdb_client_pnn(state->client),
+                                   tevent_timeval_zero(), &recmode);
+       if (ret != 0) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       if (recmode == CTDB_RECOVERY_NORMAL) {
+               tevent_req_done(req);
+               return;
+       }
+
+       subreq = tevent_wakeup_send(state, state->ev,
+                                   tevent_timeval_current_ofs(1, 0));
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, ctdb_recovery_wait_retry, req);
+}
+
+bool ctdb_recovery_wait_recv(struct tevent_req *req, int *perr)
+{
+       int err;
+
+       if (tevent_req_is_unix_error(req, &err)) {
+               if (perr != NULL) {
+                       *perr = err;
+               }
+               return false;
+       }
+
+       return true;
+}
diff --git a/ctdb/client/client_control.c b/ctdb/client/client_control.c
new file mode 100644 (file)
index 0000000..4249bfb
--- /dev/null
@@ -0,0 +1,430 @@
+/*
+   CTDB client code
+
+   Copyright (C) Amitay Isaacs  2015
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "system/network.h"
+#include "system/filesys.h"
+
+#include <talloc.h>
+#include <tevent.h>
+#include <tdb.h>
+
+#include "lib/util/tevent_unix.h"
+
+#include "common/reqid.h"
+#include "common/srvid.h"
+#include "common/comm.h"
+
+#include "protocol/protocol.h"
+#include "protocol/protocol_api.h"
+
+#include "client/client_private.h"
+#include "client/client.h"
+
+
+/*
+ * Handle REQ_CONTROL and REPLY_CONTROL
+ */
+
+struct ctdb_client_control_state {
+       struct ctdb_client_context *client;
+       uint32_t opcode;
+       uint32_t flags;
+       uint32_t reqid;
+       struct ctdb_reply_control *reply;
+       struct tevent_req *req;
+};
+
+static int ctdb_client_control_state_destructor(
+       struct ctdb_client_control_state *state);
+static void ctdb_client_control_done(struct tevent_req *subreq);
+
+struct tevent_req *ctdb_client_control_send(TALLOC_CTX *mem_ctx,
+                                           struct tevent_context *ev,
+                                           struct ctdb_client_context *client,
+                                           uint32_t destnode,
+                                           struct timeval timeout,
+                                           struct ctdb_req_control *request)
+{
+       struct ctdb_req_header h;
+       struct tevent_req *req, *subreq;
+       struct ctdb_client_control_state *state;
+       uint32_t reqid;
+       uint8_t *buf;
+       size_t buflen;
+       int ret;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct ctdb_client_control_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       reqid = reqid_new(client->idr, state);
+       if (reqid == REQID_INVALID) {
+               talloc_free(req);
+               return NULL;
+       }
+
+       state->client = client;
+       state->flags = request->flags;
+       state->opcode = request->opcode;
+       state->reqid = reqid;
+       state->req = req;
+       state->reply = talloc_zero(state, struct ctdb_reply_control);
+       if (tevent_req_nomem(state->reply, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       talloc_set_destructor(state, ctdb_client_control_state_destructor);
+
+       ctdb_req_header_fill(&h, 0, CTDB_REQ_CONTROL, destnode,
+                            client->pnn, reqid);
+
+       ret = ctdb_req_control_push(&h, request, state, &buf, &buflen);
+       if (ret != 0) {
+               tevent_req_error(req, ret);
+               return tevent_req_post(req, ev);
+       }
+
+       if (!tevent_timeval_is_zero(&timeout)) {
+               tevent_req_set_endtime(req, ev, timeout);
+       }
+
+       subreq = comm_write_send(state, ev, client->comm, buf, buflen);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, ctdb_client_control_done, req);
+
+       return req;
+}
+
+static int ctdb_client_control_state_destructor(
+       struct ctdb_client_control_state *state)
+{
+       reqid_remove(state->client->idr, state->reqid);
+       return 0;
+}
+
+static void ctdb_client_control_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct ctdb_client_control_state *state = tevent_req_data(
+               req, struct ctdb_client_control_state);
+       bool status;
+       int ret;
+
+       status = comm_write_recv(subreq, &ret);
+       TALLOC_FREE(subreq);
+       if (! status) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       /* Daemon will not reply, so we set status to 0 */
+       if (state->flags & CTDB_CTRL_FLAG_NOREPLY) {
+               reqid_remove(state->client->idr, state->reqid);
+               state->reply->status = 0;
+               tevent_req_done(req);
+       }
+
+       /* wait for the reply or timeout */
+}
+
+void ctdb_client_reply_control(struct ctdb_client_context *client,
+                              uint8_t *buf, size_t buflen, uint32_t reqid)
+{
+       struct ctdb_req_header h;
+       struct ctdb_client_control_state *state;
+       int ret;
+
+       state = reqid_find(client->idr, reqid,
+                          struct ctdb_client_control_state);
+       if (state == NULL) {
+               return;
+       }
+
+       if (reqid != state->reqid) {
+               return;
+       }
+
+       ret = ctdb_reply_control_pull(buf, buflen, state->opcode, &h,
+                                     state->reply, state->reply);
+       if (ret != 0) {
+               tevent_req_error(state->req, ret);
+               return;
+       }
+
+       tevent_req_done(state->req);
+}
+
+bool ctdb_client_control_recv(struct tevent_req *req, int *perr,
+                             TALLOC_CTX *mem_ctx,
+                             struct ctdb_reply_control **reply)
+{
+       struct ctdb_client_control_state *state = tevent_req_data(
+               req, struct ctdb_client_control_state);
+       int err;
+
+       if (tevent_req_is_unix_error(req, &err)) {
+               if (perr != NULL) {
+                       *perr = err;
+               }
+               return false;
+       }
+
+       if (reply != NULL) {
+               *reply = talloc_steal(mem_ctx, state->reply);
+       }
+
+       return true;
+}
+
+/*
+ * Handle multiple nodes - there cannot be any return data
+ */
+
+struct ctdb_client_control_multi_state {
+       uint32_t *pnn_list;
+       int count;
+       int done;
+       int err;
+       int *err_list;
+       struct ctdb_reply_control **reply;
+};
+
+struct control_index_state {
+       struct tevent_req *req;
+       int index;
+};
+
+static void ctdb_client_control_multi_done(struct tevent_req *subreq);
+
+struct tevent_req *ctdb_client_control_multi_send(
+                               TALLOC_CTX *mem_ctx,
+                               struct tevent_context *ev,
+                               struct ctdb_client_context *client,
+                               uint32_t *pnn_list, int count,
+                               struct timeval timeout,
+                               struct ctdb_req_control *request)
+{
+       struct tevent_req *req, *subreq;
+       struct ctdb_client_control_multi_state *state;
+       int i;
+
+       if (pnn_list == NULL || count == 0) {
+               return NULL;
+       }
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct ctdb_client_control_multi_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       state->pnn_list = pnn_list;
+       state->count = count;
+       state->done = 0;
+       state->err = 0;
+       state->err_list = talloc_zero_array(state, int, count);
+       if (tevent_req_nomem(state->err_list, req)) {
+               return tevent_req_post(req, ev);
+       }
+       state->reply = talloc_zero_array(state, struct ctdb_reply_control *,
+                                        count);
+       if (tevent_req_nomem(state->reply, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       for (i=0; i<count; i++) {
+               struct control_index_state *substate;
+
+               subreq = ctdb_client_control_send(state, ev, client,
+                                                 pnn_list[i], timeout,
+                                                 request);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+
+               substate = talloc(subreq, struct control_index_state);
+               if (tevent_req_nomem(substate, req)) {
+                       return tevent_req_post(req, ev);
+               }
+
+               substate->req = req;
+               substate->index = i;
+
+               tevent_req_set_callback(subreq, ctdb_client_control_multi_done,
+                                       substate);
+       }
+
+       return req;
+}
+
+static void ctdb_client_control_multi_done(struct tevent_req *subreq)
+{
+       struct control_index_state *substate = tevent_req_callback_data(
+               subreq, struct control_index_state);
+       struct tevent_req *req = substate->req;
+       int idx = substate->index;
+       struct ctdb_client_control_multi_state *state = tevent_req_data(
+               req, struct ctdb_client_control_multi_state);
+       bool status;
+       int ret;
+
+       status = ctdb_client_control_recv(subreq, &ret, state->reply,
+                                         &state->reply[idx]);
+       TALLOC_FREE(subreq);
+       if (! status) {
+               if (state->err == 0) {
+                       state->err = ret;
+                       state->err_list[idx] = state->err;
+               }
+       } else {
+               if (state->reply[idx]->status != 0) {
+                       if (state->err == 0) {
+                               state->err = state->reply[idx]->status;
+                               state->err_list[idx] = state->err;
+                       }
+               }
+       }
+
+       state->done += 1;
+
+       if (state->done == state->count) {
+               tevent_req_done(req);
+       }
+}
+
+bool ctdb_client_control_multi_recv(struct tevent_req *req, int *perr,
+                                   TALLOC_CTX *mem_ctx, int **perr_list,
+                                   struct ctdb_reply_control ***preply)
+{
+       struct ctdb_client_control_multi_state *state = tevent_req_data(
+               req, struct ctdb_client_control_multi_state);
+       int err;
+
+       if (tevent_req_is_unix_error(req, &err)) {
+               if (perr != NULL) {
+                       *perr = err;
+               }
+               if (perr_list != NULL) {
+                       *perr_list = talloc_steal(mem_ctx, state->err_list);
+               }
+               return false;
+       }
+
+       if (perr != NULL) {
+               *perr = state->err;
+       }
+
+       if (perr_list != NULL) {
+               *perr_list = talloc_steal(mem_ctx, state->err_list);
+       }
+
+       if (preply != NULL) {
+               *preply = talloc_steal(mem_ctx, state->reply);
+       }
+
+       if (state->err != 0) {
+               return false;
+       }
+
+       return true;
+}
+
+int ctdb_client_control_multi_error(uint32_t *pnn_list, int count,
+                                   int *err_list, uint32_t *pnn)
+{
+       int ret = 0, i;
+
+       for (i=0; i<count; i++) {
+               if (err_list[i] != 0) {
+                       ret = err_list[i];
+                       *pnn = pnn_list[i];
+               }
+       }
+
+       return ret;
+}
+
+/*
+ * Sync version of control send/recv
+ */
+
+int ctdb_client_control(TALLOC_CTX *mem_ctx,
+                       struct tevent_context *ev,
+                       struct ctdb_client_context *client,
+                       uint32_t destnode,
+                       struct timeval timeout,
+                       struct ctdb_req_control *request,
+                       struct ctdb_reply_control **reply)
+{
+       struct tevent_req *req;
+       int ret;
+       bool status;
+
+       req = ctdb_client_control_send(mem_ctx, ev, client, destnode, timeout,
+                                      request);
+       if (req == NULL) {
+               return ENOMEM;
+       }
+
+       tevent_req_poll(req, ev);
+
+       status = ctdb_client_control_recv(req, &ret, mem_ctx, reply);
+       if (! status) {
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_client_control_multi(TALLOC_CTX *mem_ctx,
+                             struct tevent_context *ev,
+                             struct ctdb_client_context *client,
+                             uint32_t *pnn_list, int count,
+                             struct timeval timeout,
+                             struct ctdb_req_control *request,
+                             int **perr_list,
+                             struct ctdb_reply_control ***preply)
+{
+       struct tevent_req *req;
+       bool status;
+       int ret;
+
+       req = ctdb_client_control_multi_send(mem_ctx, ev, client,
+                                            pnn_list, count,
+                                            timeout, request);
+       if (req == NULL) {
+               return ENOMEM;
+       }
+
+       tevent_req_poll(req, ev);
+
+       status = ctdb_client_control_multi_recv(req, &ret, mem_ctx, perr_list,
+                                               preply);
+       if (! status) {
+               return ret;
+       }
+
+       return 0;
+}
diff --git a/ctdb/client/client_control_sync.c b/ctdb/client/client_control_sync.c
new file mode 100644 (file)
index 0000000..0050b74
--- /dev/null
@@ -0,0 +1,2972 @@
+/*
+   CTDB client code
+
+   Copyright (C) Amitay Isaacs  2015
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "system/network.h"
+#include "system/filesys.h"
+
+#include <talloc.h>
+#include <tevent.h>
+#include <tdb.h>
+
+#include "lib/util/debug.h"
+#include "ctdb_logging.h"
+
+#include "protocol/protocol.h"
+#include "protocol/protocol_api.h"
+#include "client/client_private.h"
+#include "client/client.h"
+
+int ctdb_ctrl_process_exists(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                            struct ctdb_client_context *client,
+                            int destnode, struct timeval timeout,
+                            pid_t pid, int *status)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_process_exists(&request, pid);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control PROCESS_EXISTS failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_process_exists(reply, status);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control PROCESS_EXISTS failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_statistics(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                        struct ctdb_client_context *client,
+                        int destnode, struct timeval timeout,
+                        struct ctdb_statistics **stats)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_statistics(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control STATISTICS failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_statistics(reply, mem_ctx, stats);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control STATISTICS failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_ping(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                  struct ctdb_client_context *client,
+                  int destnode, struct timeval timeout,
+                  int *num_clients)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_ping(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control PING failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_ping(reply, num_clients);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control PING failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_getdbpath(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                       struct ctdb_client_context *client,
+                       int destnode, struct timeval timeout,
+                       uint32_t db_id,
+                       const char **db_path)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_getdbpath(&request, db_id);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GETDBPATH failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_getdbpath(reply, mem_ctx, db_path);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GETDBPATH failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_getvnnmap(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                       struct ctdb_client_context *client,
+                       int destnode, struct timeval timeout,
+                       struct ctdb_vnn_map **vnnmap)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_getvnnmap(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GETVNNMAP failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_getvnnmap(reply, mem_ctx, vnnmap);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GETVNNMAP failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_getdebug(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                      struct ctdb_client_context *client,
+                      int destnode, struct timeval timeout,
+                      uint32_t *loglevel)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_get_debug(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_DEBUG failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_get_debug(reply, loglevel);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_DEBUG failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_setdebug(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                      struct ctdb_client_context *client,
+                      int destnode, struct timeval timeout,
+                      uint32_t loglevel)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_set_debug(&request, loglevel);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SET_DEBUG failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_set_debug(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SET_DEBUG failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_get_dbmap(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                       struct ctdb_client_context *client,
+                       int destnode, struct timeval timeout,
+                       struct ctdb_dbid_map **dbmap)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_get_dbmap(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_DBMAP failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_get_dbmap(reply, mem_ctx, dbmap);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_DBMAP failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_pull_db(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                     struct ctdb_client_context *client, int destnode,
+                     struct timeval timeout, struct ctdb_pulldb *pulldb,
+                     struct ctdb_rec_buffer **recbuf)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_pull_db(&request, pulldb);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control PULL_DB failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_pull_db(reply, mem_ctx, recbuf);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control PULL_DB failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_push_db(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                     struct ctdb_client_context *client, int destnode,
+                     struct timeval timeout, struct ctdb_rec_buffer *recbuf)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_push_db(&request, recbuf);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control PUSH_DB failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_push_db(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control PUSH_DB failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+
+int ctdb_ctrl_get_recmode(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                         struct ctdb_client_context *client,
+                         int destnode, struct timeval timeout,
+                         int *recmode)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_get_recmode(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_RECMODE failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_get_recmode(reply, recmode);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_RECMODE failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_set_recmode(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                         struct ctdb_client_context *client,
+                         int destnode, struct timeval timeout,
+                         int recmode)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_set_recmode(&request, recmode);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SET_RECMODE failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_set_recmode(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SET_RECMODE failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_statistics_reset(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                              struct ctdb_client_context *client,
+                              int destnode, struct timeval timeout)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_statistics_reset(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control STATISTICS_RESET failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_statistics_reset(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control STATISTICS_RESET failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_db_attach(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                       struct ctdb_client_context *client,
+                       int destnode, struct timeval timeout,
+                       const char *db_name, uint32_t tdb_flags,
+                       uint32_t *db_id)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_db_attach(&request, db_name, tdb_flags);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control DB_ATTACH failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_db_attach(reply, db_id);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control DB_ATTACH failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_traverse_start(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                            struct ctdb_client_context *client,
+                            int destnode, struct timeval timeout,
+                            struct ctdb_traverse_start *traverse)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_traverse_start(&request, traverse);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control TRAVERSE_START failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_traverse_start(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control TRAVERSE_START failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_register_srvid(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                            struct ctdb_client_context *client,
+                            int destnode, struct timeval timeout,
+                            uint64_t srvid)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_register_srvid(&request, srvid);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control REGISTER_SRVID failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_register_srvid(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control REGISTER_SRVID failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_deregister_srvid(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                              struct ctdb_client_context *client,
+                              int destnode, struct timeval timeout,
+                              uint64_t srvid)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_deregister_srvid(&request, srvid);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control DEREGISTER_SRVID failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_deregister_srvid(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control DEREGISTER_SRVID failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_get_dbname(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                        struct ctdb_client_context *client,
+                        int destnode, struct timeval timeout,
+                        uint32_t db_id, const char **db_name)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_get_dbname(&request, db_id);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_DBNAME failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_get_dbname(reply, mem_ctx, db_name);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_DBNAME failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_enable_seqnum(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           uint32_t db_id)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_enable_seqnum(&request, db_id);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control ENABLE_SEQNUM failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_enable_seqnum(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control ENABLE_SEQNUM failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_update_seqnum(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           uint32_t db_id)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_update_seqnum(&request, db_id);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control UPDATE_SEQNUM failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_update_seqnum(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control UPDATE_SEQNUM failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_dump_memory(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                         struct ctdb_client_context *client,
+                         int destnode, struct timeval timeout,
+                         const char **mem_str)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_dump_memory(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control DUMP_MEMORY failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_dump_memory(reply, mem_ctx, mem_str);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control DUMP_MEMORY failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_get_pid(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                     struct ctdb_client_context *client,
+                     int destnode, struct timeval timeout,
+                     pid_t *pid)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_get_pid(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_PID failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_get_pid(reply, pid);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_PID failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_get_recmaster(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           uint32_t *recmaster)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_get_recmaster(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_RECMASTER failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_get_recmaster(reply, recmaster);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_RECMASTER failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_set_recmaster(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           uint32_t recmaster)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_set_recmaster(&request, recmaster);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SET_RECMASTER failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_set_recmaster(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SET_RECMASTER failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_freeze(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                    struct ctdb_client_context *client,
+                    int destnode, struct timeval timeout,
+                    int priority)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_freeze(&request, priority);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control FREEZE failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_freeze(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control FREEZE failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_thaw(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                  struct ctdb_client_context *client,
+                  int destnode, struct timeval timeout,
+                  int priority)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_thaw(&request, priority);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control THAW failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_thaw(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control THAW failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_get_pnn(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                     struct ctdb_client_context *client,
+                     int destnode, struct timeval timeout,
+                     uint32_t *pnn)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_get_pnn(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_PNN failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_get_pnn(reply, pnn);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_PNN failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_shutdown(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                      struct ctdb_client_context *client,
+                      int destnode, struct timeval timeout)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_shutdown(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SHUTDOWN failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_shutdown(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SHUTDOWN failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_get_monmode(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                         struct ctdb_client_context *client,
+                         int destnode, struct timeval timeout,
+                         int *mon_mode)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_get_monmode(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_MONMODE failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_get_monmode(reply, mon_mode);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_MONMODE failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_tcp_add(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                     struct ctdb_client_context *client,
+                     int destnode, struct timeval timeout,
+                     struct ctdb_connection *conn)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_tcp_add(&request, conn);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control TCP_ADD failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_tcp_add(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control TCP_ADD failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_tcp_remove(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                        struct ctdb_client_context *client,
+                        int destnode, struct timeval timeout,
+                        struct ctdb_connection *conn)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_tcp_remove(&request, conn);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control TCP_REMOVE failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_tcp_remove(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control TCP_REMOVE failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_set_tunable(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                         struct ctdb_client_context *client,
+                         int destnode, struct timeval timeout,
+                         struct ctdb_tunable *tunable)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_set_tunable(&request, tunable);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SET_TUNABLE failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_set_tunable(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SET_TUNABLE failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_get_tunable(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                         struct ctdb_client_context *client,
+                         int destnode, struct timeval timeout,
+                         const char *var, uint32_t *value)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_get_tunable(&request, var);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_TUNABLE failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_get_tunable(reply, value);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_TUNABLE failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_list_tunables(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           struct ctdb_var_list **var_list)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_list_tunables(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control LIST_TUNABLES failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_list_tunables(reply, mem_ctx, var_list);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control LIST_TUNABLES failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_modify_flags(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                          struct ctdb_client_context *client,
+                          int destnode, struct timeval timeout,
+                          uint32_t pnn, uint32_t old_flags,
+                          uint32_t new_flags)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       struct ctdb_node_flag_change flag_change;
+       int ret;
+
+       flag_change.pnn = pnn;
+       flag_change.old_flags = old_flags;
+       flag_change.new_flags = new_flags;
+
+       ctdb_req_control_modify_flags(&request, &flag_change);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control MODIFY_FLAGS failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_modify_flags(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control MODIFY_FLAGS failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_get_all_tunables(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                              struct ctdb_client_context *client,
+                              int destnode, struct timeval timeout,
+                              struct ctdb_tunable_list **tun_list)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_get_all_tunables(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_ALL_TUNABLES failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_get_all_tunables(reply, mem_ctx, tun_list);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_ALL_TUNABLES failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_kill_tcp(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                      struct ctdb_client_context *client,
+                      int destnode, struct timeval timeout,
+                      struct ctdb_connection *conn)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_kill_tcp(&request, conn);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control KILL_TCP failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_kill_tcp(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control KILL_TCP failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_get_tcp_tickle_list(TALLOC_CTX *mem_ctx,
+                                 struct tevent_context *ev,
+                                 struct ctdb_client_context *client,
+                                 int destnode, struct timeval timeout,
+                                 ctdb_sock_addr *addr,
+                                 struct ctdb_tickle_list **tickles)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_get_tcp_tickle_list(&request, addr);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_TCP_TICKLE_LIST failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_get_tcp_tickle_list(reply, mem_ctx, tickles);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_TCP_TICKLE_LIST failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_set_tcp_tickle_list(TALLOC_CTX *mem_ctx,
+                                 struct tevent_context *ev,
+                                 struct ctdb_client_context *client,
+                                 int destnode, struct timeval timeout,
+                                 struct ctdb_tickle_list *tickles)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_set_tcp_tickle_list(&request, tickles);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SET_TCP_TICKLE_LIST failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_set_tcp_tickle_list(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SET_TCP_TICKLE_LIST failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_register_server_id(TALLOC_CTX *mem_ctx,
+                                struct tevent_context *ev,
+                                struct ctdb_client_context *client,
+                                int destnode, struct timeval timeout,
+                                struct ctdb_client_id *cid)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_register_server_id(&request, cid);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control REGISTER_SERVER_ID failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_register_server_id(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control REGISTER_SERVER_ID failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_unregister_server_id(TALLOC_CTX *mem_ctx,
+                                  struct tevent_context *ev,
+                                  struct ctdb_client_context *client,
+                                  int destnode, struct timeval timeout,
+                                  struct ctdb_client_id *cid)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_unregister_server_id(&request, cid);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control UNREGISTER_SERVER_ID failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_unregister_server_id(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control UNREGISTER_SERVER_ID failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_check_server_id(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                             struct ctdb_client_context *client,
+                             int destnode, struct timeval timeout,
+                             struct ctdb_client_id *cid)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_check_server_id(&request, cid);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control CHECK_SERVER_ID failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_check_server_id(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control CHECK_SERVER_ID failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_get_server_id_list(TALLOC_CTX *mem_ctx,
+                                struct tevent_context *ev,
+                                struct ctdb_client_context *client,
+                                int destnode, struct timeval timeout,
+                                struct ctdb_client_id_map **cid_map)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_get_server_id_list(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_SERVER_ID_LIST failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_get_server_id_list(reply, mem_ctx, cid_map);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_SERVER_ID_LIST failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_db_attach_persistent(TALLOC_CTX *mem_ctx,
+                                  struct tevent_context *ev,
+                                  struct ctdb_client_context *client,
+                                  int destnode, struct timeval timeout,
+                                  const char *db_name, int tdb_flags,
+                                  uint32_t *db_id)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_db_attach_persistent(&request, db_name, tdb_flags);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control DB_ATTACH_PERSISTENT failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_db_attach_persistent(reply, db_id);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control DB_ATTACH_PERSISTENT failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_send_gratuitous_arp(TALLOC_CTX *mem_ctx,
+                                 struct tevent_context *ev,
+                                 struct ctdb_client_context *client,
+                                 int destnode, struct timeval timeout,
+                                 struct ctdb_addr_info *addr_info)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_send_gratuitous_arp(&request, addr_info);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SEND_GRATUITOUS_ARP failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_send_gratuitous_arp(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SEND_GRATUITOUS_ARP failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_transaction_start(TALLOC_CTX *mem_ctx,
+                               struct tevent_context *ev,
+                               struct ctdb_client_context *client,
+                               int destnode, struct timeval timeout,
+                               uint32_t tid)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_transaction_start(&request, tid);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control TRANSACTION_START failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_transaction_start(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control TRANSACTION_START failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_transaction_commit(TALLOC_CTX *mem_ctx,
+                                struct tevent_context *ev,
+                                struct ctdb_client_context *client,
+                                int destnode, struct timeval timeout,
+                                uint32_t tid)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_transaction_commit(&request, tid);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control TRANSACTION_COMMIT failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_transaction_commit(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control TRANSACTION_COMMIT failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_wipe_database(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           uint32_t db_id, uint32_t tid)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       struct ctdb_transdb transdb;
+       int ret;
+
+       transdb.db_id = db_id;
+       transdb.tid = tid;
+
+       ctdb_req_control_wipe_database(&request, &transdb);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control WIPE_DATABASE failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_wipe_database(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control WIPE_DATABASE failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_uptime(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                    struct ctdb_client_context *client,
+                    int destnode, struct timeval timeout,
+                    struct ctdb_uptime **uptime)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_uptime(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control UPTIME failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_uptime(reply, mem_ctx, uptime);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control UPTIME failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_start_recovery(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                            struct ctdb_client_context *client,
+                            int destnode, struct timeval timeout)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_start_recovery(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control START_RECOVERY failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_start_recovery(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control START_RECOVERY failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_end_recovery(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                          struct ctdb_client_context *client,
+                          int destnode, struct timeval timeout)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_end_recovery(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control END_RECOVERY failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_end_recovery(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control END_RECOVERY failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_reload_nodes_file(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                               struct ctdb_client_context *client,
+                               int destnode, struct timeval timeout)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_reload_nodes_file(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control RELOAD_NODES_FILE failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_reload_nodes_file(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control RELOAD_NODES_FILE failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_enable_monitor(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                            struct ctdb_client_context *client,
+                            int destnode, struct timeval timeout)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_enable_monitor(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control ENABLE_MONITOR failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_enable_monitor(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control ENABLE_MONITOR failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_disable_monitor(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                             struct ctdb_client_context *client,
+                             int destnode, struct timeval timeout)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_disable_monitor(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control DISABLE_MONITOR failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_disable_monitor(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control DISABLE_MONITOR failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_add_public_ip(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           struct ctdb_addr_info *addr_info)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_add_public_ip(&request, addr_info);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control ADD_PUBLIC_IP failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_add_public_ip(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control ADD_PUBLIC_IP failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_del_public_ip(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           struct ctdb_addr_info *addr_info)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_del_public_ip(&request, addr_info);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control DEL_PUBLIC_IP failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_del_public_ip(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control DEL_PUBLIC_IP failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_run_eventscripts(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                              struct ctdb_client_context *client,
+                              int destnode, struct timeval timeout,
+                              const char *event)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_run_eventscripts(&request, event);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control RUN_EVENTSCRIPTS failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_run_eventscripts(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control RUN_EVENTSCRIPTS failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_get_capabilities(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                              struct ctdb_client_context *client,
+                              int destnode, struct timeval timeout,
+                              uint32_t *caps)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_get_capabilities(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_CAPABILITIES failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_get_capabilities(reply, caps);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_CAPABILITIES failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_release_ip(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                        struct ctdb_client_context *client,
+                        int destnode, struct timeval timeout,
+                        struct ctdb_public_ip *pubip)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_release_ip(&request, pubip);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control RELEASE_IP failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_release_ip(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control RELEASE_IP failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_takeover_ip(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                         struct ctdb_client_context *client,
+                         int destnode, struct timeval timeout,
+                         struct ctdb_public_ip *pubip)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_takeover_ip(&request, pubip);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control TAKEOVER_IP failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_takeover_ip(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control TAKEOVER_IP failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_get_public_ips(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                            struct ctdb_client_context *client,
+                            int destnode, struct timeval timeout,
+                            struct ctdb_public_ip_list **pubip_list)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_get_public_ips(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_PUBLIC_IPS failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_get_public_ips(reply, mem_ctx, pubip_list);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_PUBLIC_IPS failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_get_nodemap(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                         struct ctdb_client_context *client,
+                         int destnode, struct timeval timeout,
+                         struct ctdb_node_map **nodemap)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_get_nodemap(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_NODEMAP failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_get_nodemap(reply, mem_ctx, nodemap);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_NODEMAP failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_get_event_script_status(TALLOC_CTX *mem_ctx,
+                                     struct tevent_context *ev,
+                                     struct ctdb_client_context *client,
+                                     int destnode, struct timeval timeout,
+                                     enum ctdb_event event,
+                                     struct ctdb_script_list **slist)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_get_event_script_status(&request, event);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_EVENT_SCRIPT_STATUS failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_get_event_script_status(reply, mem_ctx, slist);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_EVENT_SCRIPT_STATUS failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_traverse_kill(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           struct ctdb_traverse_start *traverse)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_traverse_kill(&request, traverse);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control TRAVERSE_KILL failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_traverse_kill(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control TRAVERSE_KILL failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_get_reclock_file(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                              struct ctdb_client_context *client,
+                              int destnode, struct timeval timeout,
+                              const char **reclock_file)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_get_reclock_file(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_RECLOCK_FILE failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_get_reclock_file(reply, mem_ctx, reclock_file);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_RECLOCK_FILE failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_set_reclock_file(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                              struct ctdb_client_context *client,
+                              int destnode, struct timeval timeout,
+                              const char *reclock_file)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_set_reclock_file(&request, reclock_file);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SET_RECLOCK_FILE failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_set_reclock_file(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SET_RECLOCK_FILE failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_stop_node(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                       struct ctdb_client_context *client,
+                       int destnode, struct timeval timeout)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_stop_node(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control STOP_NODE failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_stop_node(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control STOP_NODE failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_continue_node(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_continue_node(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control CONTINUE_NODE failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_continue_node(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control CONTINUE_NODE failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_set_natgwstate(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                            struct ctdb_client_context *client,
+                            int destnode, struct timeval timeout,
+                            uint32_t natgw_role)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_set_natgwstate(&request, natgw_role);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SET_NATGWSTATE failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_set_natgwstate(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SET_NATGWSTATE failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_set_lmasterrole(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                             struct ctdb_client_context *client,
+                             int destnode, struct timeval timeout,
+                             uint32_t lmaster_role)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_set_lmasterrole(&request, lmaster_role);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SET_LMASTERROLE failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_set_lmasterrole(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SET_LMASTERROLE failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_set_recmasterrole(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                               struct ctdb_client_context *client,
+                               int destnode, struct timeval timeout,
+                               uint32_t recmaster_role)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_set_recmasterrole(&request, recmaster_role);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SET_RECMASTERROLE failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_set_recmasterrole(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SET_RECMASTERROLE failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_enable_script(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           const char *script)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_enable_script(&request, script);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control ENABLE_SCRIPT failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_enable_script(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control ENABLE_SCRIPT failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_disable_script(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                            struct ctdb_client_context *client,
+                            int destnode, struct timeval timeout,
+                            const char *script)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_disable_script(&request, script);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control DISABLE_SCRIPT failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_disable_script(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control DISABLE_SCRIPT failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_set_ban_state(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           struct ctdb_ban_state *ban_state)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_set_ban_state(&request, ban_state);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SET_BAN_STATE failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_set_ban_state(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SET_BAN_STATE failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_get_ban_state(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           struct ctdb_ban_state **ban_state)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_get_ban_state(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_BAN_STATE failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_get_ban_state(reply, mem_ctx, ban_state);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_BAN_STATE failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_set_db_priority(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                             struct ctdb_client_context *client,
+                             int destnode, struct timeval timeout,
+                             uint32_t db_id, int priority)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       struct ctdb_db_priority db_prio;
+       int ret;
+
+       db_prio.db_id = db_id;
+       db_prio.priority = priority;
+
+       ctdb_req_control_set_db_priority(&request, &db_prio);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SET_DB_PRIORITY failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_set_db_priority(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SET_DB_PRIORITY failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_get_db_priority(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                             struct ctdb_client_context *client,
+                             int destnode, struct timeval timeout,
+                             uint32_t db_id, uint32_t *priority)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_get_db_priority(&request, db_id);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_DB_PRIORITY failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_get_db_priority(reply, priority);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_DB_PRIORITY failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_transaction_cancel(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                                struct ctdb_client_context *client,
+                                int destnode, struct timeval timeout,
+                                uint32_t tid)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_transaction_cancel(&request, tid);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control TRANSACTION_CANCEL failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_transaction_cancel(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control TRANSACTION_CANCEL failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_register_notify(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                             struct ctdb_client_context *client,
+                             int destnode, struct timeval timeout,
+                             struct ctdb_notify_data *notify)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_register_notify(&request, notify);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control REGISTER_NOTIFY failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_register_notify(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control REGISTER_NOTIFY failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_deregister_notify(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                               struct ctdb_client_context *client,
+                               int destnode, struct timeval timeout,
+                               uint64_t srvid)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_deregister_notify(&request, srvid);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control DEREGISTER_NOTIFY failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_deregister_notify(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control DEREGISTER_NOTIFY failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_trans3_commit(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           struct ctdb_rec_buffer *recbuf)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_trans3_commit(&request, recbuf);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control TRANS3_COMMIT failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_trans3_commit(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control TRANS3_COMMIT failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_get_db_seqnum(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           uint32_t db_id, uint64_t *seqnum)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_get_db_seqnum(&request, db_id);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_DB_SEQNUM failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_get_db_seqnum(reply, seqnum);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_DB_SEQNUM failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_db_set_healthy(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                            struct ctdb_client_context *client,
+                            int destnode, struct timeval timeout,
+                            uint32_t db_id)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_db_set_healthy(&request, db_id);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control DB_SET_HEALTHY failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_db_set_healthy(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control DB_SET_HEALTHY failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_db_get_health(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           uint32_t db_id, const char **reason)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_db_get_health(&request, db_id);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control DB_GET_HEALTH failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_db_get_health(reply, mem_ctx, reason);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control DB_GET_HEALTH failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_get_public_ip_info(TALLOC_CTX *mem_ctx,
+                                struct tevent_context *ev,
+                                struct ctdb_client_context *client,
+                                int destnode, struct timeval timeout,
+                                ctdb_sock_addr *addr,
+                                struct ctdb_public_ip_info **ipinfo)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_get_public_ip_info(&request, addr);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_PUBLIC_IP_INFO failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_get_public_ip_info(reply, mem_ctx, ipinfo);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_PUBLIC_IP_INFO failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_get_ifaces(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                        struct ctdb_client_context *client,
+                        int destnode, struct timeval timeout,
+                        struct ctdb_iface_list **iface_list)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_get_ifaces(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_IFACES failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_get_ifaces(reply, mem_ctx, iface_list);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_IFACES failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_set_iface_link_state(TALLOC_CTX *mem_ctx,
+                                  struct tevent_context *ev,
+                                  struct ctdb_client_context *client,
+                                  int destnode, struct timeval timeout,
+                                  struct ctdb_iface *iface)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_set_iface_link_state(&request, iface);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SET_IFACE_LINK_STATE failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_set_iface_link_state(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SET_IFACE_LINK_STATE failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_tcp_add_delayed_update(TALLOC_CTX *mem_ctx,
+                                    struct tevent_context *ev,
+                                    struct ctdb_client_context *client,
+                                    int destnode, struct timeval timeout,
+                                    struct ctdb_connection *conn)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_tcp_add_delayed_update(&request, conn);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control TCP_ADD_DELAYED_UPDATE failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_tcp_add_delayed_update(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control TCP_ADD_DELAYED_UDATE failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_get_stat_history(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                              struct ctdb_client_context *client,
+                              int destnode, struct timeval timeout,
+                              struct ctdb_statistics_list **stats_list)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_get_stat_history(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_STAT_HISTORY failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_get_stat_history(reply, mem_ctx, stats_list);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_STAT_HISTORY failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_schedule_for_deletion(TALLOC_CTX *mem_ctx,
+                                   struct tevent_context *ev,
+                                   struct ctdb_client_context *client,
+                                   int destnode, struct timeval timeout,
+                                   struct ctdb_key_data *key)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_schedule_for_deletion(&request, key);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SCHEDULE_FOR_DELETION failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_schedule_for_deletion(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SCHEDULE_FOR_DELETION failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_set_db_readonly(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                             struct ctdb_client_context *client,
+                             int destnode, struct timeval timeout,
+                             uint32_t db_id)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_set_db_readonly(&request, db_id);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SET_DB_READONY failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_set_db_readonly(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SET_DB_READONY failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_check_srvids(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                          struct ctdb_client_context *client,
+                          int destnode, struct timeval timeout,
+                          uint64_t *srvid, int count, uint8_t **result)
+{
+       struct ctdb_uint64_array srvid_list;
+       struct ctdb_uint8_array *u8_array;
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       srvid_list.num = count;
+       srvid_list.val = srvid;
+
+       ctdb_req_control_check_srvids(&request, &srvid_list);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control CHECK_SRVIDS failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_check_srvids(reply, &request, &u8_array);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control CHECK_SRVIDS failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       if (u8_array->num != count) {
+               DEBUG(DEBUG_ERR,
+                     ("Control CHECK_SRVIDS returned invalid data %d != %d\n",
+                      u8_array->num, count));
+               return ret;
+       }
+
+       *result = talloc_steal(mem_ctx, u8_array->val);
+       return 0;
+}
+
+int ctdb_ctrl_traverse_start_ext(TALLOC_CTX *mem_ctx,
+                                struct tevent_context *ev,
+                                struct ctdb_client_context *client,
+                                int destnode, struct timeval timeout,
+                                struct ctdb_traverse_start_ext *traverse)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_traverse_start_ext(&request, traverse);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control TRAVERSE_START_EXT failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_traverse_start_ext(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control TRAVERSE_START_EXT failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_get_db_statistics(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                               struct ctdb_client_context *client,
+                               int destnode, struct timeval timeout,
+                               uint32_t db_id,
+                               struct ctdb_db_statistics **dbstats)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_get_db_statistics(&request, db_id);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_DB_STATISTICS failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_get_db_statistics(reply, mem_ctx, dbstats);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_DB_STATISTICS failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_set_db_sticky(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout,
+                           uint32_t db_id)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_set_db_sticky(&request, db_id);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SET_DB_STICKY failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_set_db_sticky(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control SET_DB_STICKY failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_reload_public_ips(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                               struct ctdb_client_context *client,
+                               int destnode, struct timeval timeout)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_reload_public_ips(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control RELOAD_PUBLIC_IPS failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_reload_public_ips(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control RELOAD_PUBLIC_IPS failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_ipreallocated(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                           struct ctdb_client_context *client,
+                           int destnode, struct timeval timeout)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_ipreallocated(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control IPREALLOCATED failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_ipreallocated(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control IPREALLOCATED failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_get_runstate(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                          struct ctdb_client_context *client,
+                          int destnode, struct timeval timeout,
+                          enum ctdb_runstate *runstate)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_get_runstate(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_RUNSTATE failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_get_runstate(reply, runstate);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_RUNSTATE failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_db_detach(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                       struct ctdb_client_context *client,
+                       int destnode, struct timeval timeout,
+                       uint32_t db_id)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_db_detach(&request, db_id);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control DB_DETACH failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_db_detach(reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control DB_DETACH failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_get_nodes_file(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                            struct ctdb_client_context *client,
+                            int destnode, struct timeval timeout,
+                            struct ctdb_node_map **nodemap)
+{
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       int ret;
+
+       ctdb_req_control_get_nodes_file(&request);
+       ret = ctdb_client_control(mem_ctx, ev, client, destnode, timeout,
+                                 &request, &reply);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_NODES_FILE failed to node %u, ret=%d\n",
+                      destnode, ret));
+               return ret;
+       }
+
+       ret = ctdb_reply_control_get_nodes_file(reply, mem_ctx, nodemap);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Control GET_NODES_FILE failed, ret=%d\n", ret));
+               return ret;
+       }
+
+       return 0;
+}
diff --git a/ctdb/client/client_db.c b/ctdb/client/client_db.c
new file mode 100644 (file)
index 0000000..97ec1f4
--- /dev/null
@@ -0,0 +1,2137 @@
+/*
+   CTDB client code
+
+   Copyright (C) Amitay Isaacs  2015
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "system/network.h"
+#include "system/filesys.h"
+
+#include <talloc.h>
+#include <tevent.h>
+#include <tdb.h>
+
+#include "lib/tdb_wrap/tdb_wrap.h"
+#include "lib/util/tevent_unix.h"
+#include "lib/util/dlinklist.h"
+#include "lib/util/debug.h"
+#include "ctdb_logging.h"
+
+#include "protocol/protocol.h"
+#include "protocol/protocol_api.h"
+#include "client/client_private.h"
+#include "client/client.h"
+
+static struct ctdb_db_context *client_db_handle(
+                                       struct ctdb_client_context *client,
+                                       const char *db_name)
+{
+       struct ctdb_db_context *db;
+
+       for (db = client->db; db != NULL; db = db->next) {
+               if (strcmp(db_name, db->db_name) == 0) {
+                       return db;
+               }
+       }
+
+       return NULL;
+}
+
+struct ctdb_set_db_flags_state {
+       struct tevent_context *ev;
+       struct ctdb_client_context *client;
+       struct timeval timeout;
+       uint32_t db_id;
+       uint8_t db_flags;
+       bool readonly_done, sticky_done;
+       uint32_t *pnn_list;
+       int count;
+};
+
+static void ctdb_set_db_flags_nodemap_done(struct tevent_req *subreq);
+static void ctdb_set_db_flags_readonly_done(struct tevent_req *subreq);
+static void ctdb_set_db_flags_sticky_done(struct tevent_req *subreq);
+
+static struct tevent_req *ctdb_set_db_flags_send(
+                               TALLOC_CTX *mem_ctx,
+                               struct tevent_context *ev,
+                               struct ctdb_client_context *client,
+                               uint32_t destnode, struct timeval timeout,
+                               uint32_t db_id, uint8_t db_flags)
+{
+       struct tevent_req *req, *subreq;
+       struct ctdb_set_db_flags_state *state;
+       struct ctdb_req_control request;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct ctdb_set_db_flags_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       if (! (db_flags & (CTDB_DB_FLAGS_READONLY | CTDB_DB_FLAGS_STICKY))) {
+               tevent_req_done(req);
+               return tevent_req_post(req, ev);
+       }
+
+       state->ev = ev;
+       state->client = client;
+       state->timeout = timeout;
+       state->db_id = db_id;
+       state->db_flags = db_flags;
+
+       ctdb_req_control_get_nodemap(&request);
+       subreq = ctdb_client_control_send(state, ev, client, destnode, timeout,
+                                         &request);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, ctdb_set_db_flags_nodemap_done, req);
+
+       return req;
+}
+
+static void ctdb_set_db_flags_nodemap_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct ctdb_set_db_flags_state *state = tevent_req_data(
+               req, struct ctdb_set_db_flags_state);
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       struct ctdb_node_map *nodemap;
+       int ret;
+       bool status;
+
+       status = ctdb_client_control_recv(subreq, &ret, state, &reply);
+       TALLOC_FREE(subreq);
+       if (! status) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       ret = ctdb_reply_control_get_nodemap(reply, state, &nodemap);
+       talloc_free(reply);
+       if (ret != 0) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       state->count = list_of_connected_nodes(nodemap, CTDB_UNKNOWN_PNN,
+                                              state, &state->pnn_list);
+       talloc_free(nodemap);
+       if (state->count <= 0) {
+               tevent_req_error(req, ENOMEM);
+               return;
+       }
+
+       if (state->db_flags & CTDB_DB_FLAGS_READONLY) {
+               ctdb_req_control_set_db_readonly(&request, state->db_id);
+               subreq = ctdb_client_control_multi_send(
+                                       state, state->ev, state->client,
+                                       state->pnn_list, state->count,
+                                       state->timeout, &request);
+               if (tevent_req_nomem(subreq, req)) {
+                       return;
+               }
+               tevent_req_set_callback(subreq,
+                                       ctdb_set_db_flags_readonly_done, req);
+       } else {
+               state->readonly_done = true;
+       }
+
+       if (state->db_flags & CTDB_DB_FLAGS_STICKY) {
+               ctdb_req_control_set_db_sticky(&request, state->db_id);
+               subreq = ctdb_client_control_multi_send(
+                                       state, state->ev, state->client,
+                                       state->pnn_list, state->count,
+                                       state->timeout, &request);
+               if (tevent_req_nomem(subreq, req)) {
+                       return;
+               }
+               tevent_req_set_callback(subreq, ctdb_set_db_flags_sticky_done,
+                                       req);
+       } else {
+               state->sticky_done = true;
+       }
+}
+
+static void ctdb_set_db_flags_readonly_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct ctdb_set_db_flags_state *state = tevent_req_data(
+               req, struct ctdb_set_db_flags_state);
+       int ret;
+       bool status;
+
+       status = ctdb_client_control_multi_recv(subreq, &ret, NULL, NULL,
+                                               NULL);
+       TALLOC_FREE(subreq);
+       if (! status) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       state->readonly_done = true;
+
+       if (state->readonly_done && state->sticky_done) {
+               tevent_req_done(req);
+       }
+}
+
+static void ctdb_set_db_flags_sticky_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct ctdb_set_db_flags_state *state = tevent_req_data(
+               req, struct ctdb_set_db_flags_state);
+       int ret;
+       bool status;
+
+       status = ctdb_client_control_multi_recv(subreq, &ret, NULL, NULL,
+                                               NULL);
+       TALLOC_FREE(subreq);
+       if (! status) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       state->sticky_done = true;
+
+       if (state->readonly_done && state->sticky_done) {
+               tevent_req_done(req);
+       }
+}
+
+static bool ctdb_set_db_flags_recv(struct tevent_req *req, int *perr)
+{
+       int err;
+
+       if (tevent_req_is_unix_error(req, &err)) {
+               if (perr != NULL) {
+                       *perr = err;
+               }
+               return false;
+       }
+       return true;
+}
+
+struct ctdb_attach_state {
+       struct tevent_context *ev;
+       struct ctdb_client_context *client;
+       struct timeval timeout;
+       uint32_t destnode;
+       uint8_t db_flags;
+       uint32_t tdb_flags;
+       struct ctdb_db_context *db;
+};
+
+static void ctdb_attach_mutex_done(struct tevent_req *subreq);
+static void ctdb_attach_dbid_done(struct tevent_req *subreq);
+static void ctdb_attach_dbpath_done(struct tevent_req *subreq);
+static void ctdb_attach_health_done(struct tevent_req *subreq);
+static void ctdb_attach_flags_done(struct tevent_req *subreq);
+
+struct tevent_req *ctdb_attach_send(TALLOC_CTX *mem_ctx,
+                                   struct tevent_context *ev,
+                                   struct ctdb_client_context *client,
+                                   struct timeval timeout,
+                                   const char *db_name, uint8_t db_flags)
+{
+       struct tevent_req *req, *subreq;
+       struct ctdb_attach_state *state;
+       struct ctdb_req_control request;
+
+       req = tevent_req_create(mem_ctx, &state, struct ctdb_attach_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       state->db = client_db_handle(client, db_name);
+       if (state->db != NULL) {
+               tevent_req_done(req);
+               return tevent_req_post(req, ev);
+       }
+
+       state->ev = ev;
+       state->client = client;
+       state->timeout = timeout;
+       state->destnode = ctdb_client_pnn(client);
+       state->db_flags = db_flags;
+
+       state->db = talloc_zero(client, struct ctdb_db_context);
+       if (tevent_req_nomem(state->db, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       state->db->db_name = talloc_strdup(state->db, db_name);
+       if (tevent_req_nomem(state->db, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       if (db_flags & CTDB_DB_FLAGS_PERSISTENT) {
+               state->db->persistent = true;
+       }
+
+       ctdb_req_control_get_tunable(&request, "TDBMutexEnabled");
+       subreq = ctdb_client_control_send(state, ev, client,
+                                         ctdb_client_pnn(client), timeout,
+                                         &request);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, ctdb_attach_mutex_done, req);
+
+       return req;
+}
+
+static void ctdb_attach_mutex_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct ctdb_attach_state *state = tevent_req_data(
+               req, struct ctdb_attach_state);
+       struct ctdb_reply_control *reply;
+       struct ctdb_req_control request;
+       uint32_t mutex_enabled;
+       int ret;
+       bool status;
+
+       status = ctdb_client_control_recv(subreq, &ret, state, &reply);
+       TALLOC_FREE(subreq);
+       if (! status) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       ret = ctdb_reply_control_get_tunable(reply, &mutex_enabled);
+       if (ret != 0) {
+               /* Treat error as mutex support not available */
+               mutex_enabled = 0;
+       }
+
+       state->tdb_flags = TDB_DEFAULT;
+       if (! state->db->persistent) {
+               state->tdb_flags |= (TDB_INCOMPATIBLE_HASH |
+                                    TDB_CLEAR_IF_FIRST);
+       }
+       if (mutex_enabled == 1) {
+               state->tdb_flags |= TDB_MUTEX_LOCKING;
+       }
+
+       if (state->db->persistent) {
+               ctdb_req_control_db_attach_persistent(&request,
+                                                     state->db->db_name,
+                                                     state->tdb_flags);
+       } else {
+               ctdb_req_control_db_attach(&request, state->db->db_name,
+                                          state->tdb_flags);
+       }
+
+       subreq = ctdb_client_control_send(state, state->ev, state->client,
+                                         state->destnode, state->timeout,
+                                         &request);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, ctdb_attach_dbid_done, req);
+}
+
+static void ctdb_attach_dbid_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct ctdb_attach_state *state = tevent_req_data(
+               req, struct ctdb_attach_state);
+       struct ctdb_req_control request;
+       struct ctdb_reply_control *reply;
+       bool status;
+       int ret;
+
+       status = ctdb_client_control_recv(subreq, &ret, state, &reply);
+       TALLOC_FREE(subreq);
+       if (! status) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       if (state->db->persistent) {
+               ret = ctdb_reply_control_db_attach_persistent(
+                               reply, &state->db->db_id);
+       } else {
+               ret = ctdb_reply_control_db_attach(reply, &state->db->db_id);
+       }
+       talloc_free(reply);
+       if (ret != 0) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       ctdb_req_control_getdbpath(&request, state->db->db_id);
+       subreq = ctdb_client_control_send(state, state->ev, state->client,
+                                         state->destnode, state->timeout,
+                                         &request);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, ctdb_attach_dbpath_done, req);
+}
+
+static void ctdb_attach_dbpath_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct ctdb_attach_state *state = tevent_req_data(
+               req, struct ctdb_attach_state);
+       struct ctdb_reply_control *reply;
+       struct ctdb_req_control request;
+       bool status;
+       int ret;
+
+       status = ctdb_client_control_recv(subreq, &ret, state, &reply);
+       TALLOC_FREE(subreq);
+       if (! status) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       ret = ctdb_reply_control_getdbpath(reply, state->db,
+                                          &state->db->db_path);
+       talloc_free(reply);
+       if (ret != 0) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       ctdb_req_control_db_get_health(&request, state->db->db_id);
+       subreq = ctdb_client_control_send(state, state->ev, state->client,
+                                         state->destnode, state->timeout,
+                                         &request);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, ctdb_attach_health_done, req);
+}
+
+static void ctdb_attach_health_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct ctdb_attach_state *state = tevent_req_data(
+               req, struct ctdb_attach_state);
+       struct ctdb_reply_control *reply;
+       const char *reason;
+       bool status;
+       int ret;
+
+       status = ctdb_client_control_recv(subreq, &ret, state, &reply);
+       TALLOC_FREE(subreq);
+       if (! status) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       ret = ctdb_reply_control_db_get_health(reply, state, &reason);
+       if (ret != 0) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       if (reason != NULL) {
+               /* Database unhealthy, avoid attach */
+               /* FIXME: Log here */
+               tevent_req_error(req, EIO);
+               return;
+       }
+
+       subreq = ctdb_set_db_flags_send(state, state->ev, state->client,
+                                       state->destnode, state->timeout,
+                                       state->db->db_id, state->db_flags);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, ctdb_attach_flags_done, req);
+}
+
+static void ctdb_attach_flags_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct ctdb_attach_state *state = tevent_req_data(
+               req, struct ctdb_attach_state);
+       bool status;
+       int ret;
+
+       status = ctdb_set_db_flags_recv(subreq, &ret);
+       TALLOC_FREE(subreq);
+       if (! status) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       state->db->ltdb = tdb_wrap_open(state->db, state->db->db_path, 0,
+                                       state->tdb_flags, O_RDWR, 0);
+       if (tevent_req_nomem(state->db->ltdb, req)) {
+               return;
+       }
+       DLIST_ADD(state->client->db, state->db);
+
+       tevent_req_done(req);
+}
+
+bool ctdb_attach_recv(struct tevent_req *req, int *perr,
+                     struct ctdb_db_context **out)
+{
+       struct ctdb_attach_state *state = tevent_req_data(
+               req, struct ctdb_attach_state);
+       int err;
+
+       if (tevent_req_is_unix_error(req, &err)) {
+               if (perr != NULL) {
+                       *perr = err;
+               }
+               return false;
+       }
+
+       if (out != NULL) {
+               *out = state->db;
+       }
+       return true;
+}
+
+int ctdb_attach(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+               struct ctdb_client_context *client,
+               struct timeval timeout,
+               const char *db_name, uint8_t db_flags,
+               struct ctdb_db_context **out)
+{
+       struct tevent_req *req;
+       bool status;
+       int ret;
+
+       req = ctdb_attach_send(mem_ctx, ev, client, timeout,
+                              db_name, db_flags);
+       if (req == NULL) {
+               return ENOMEM;
+       }
+
+       tevent_req_poll(req, ev);
+
+       status = ctdb_attach_recv(req, &ret, out);
+       if (! status) {
+               return ret;
+       }
+
+       /*
+       ctdb_set_call(db, CTDB_NULL_FUNC, ctdb_null_func);
+       ctdb_set_call(db, CTDB_FETCH_FUNC, ctdb_fetch_func);
+       ctdb_set_call(db, CTDB_FETCH_WITH_HEADER_FUNC, ctdb_fetch_with_header_func);
+       */
+
+       return 0;
+}
+
+int ctdb_detach(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+               struct ctdb_client_context *client,
+               struct timeval timeout, uint32_t db_id)
+{
+       struct ctdb_db_context *db;
+       int ret;
+
+       ret = ctdb_ctrl_db_detach(mem_ctx, ev, client, client->pnn, timeout,
+                                 db_id);
+       if (ret != 0) {
+               return ret;
+       }
+
+       for (db = client->db; db != NULL; db = db->next) {
+               if (db->db_id == db_id) {
+                       DLIST_REMOVE(client->db, db);
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+uint32_t ctdb_db_id(struct ctdb_db_context *db)
+{
+       return db->db_id;
+}
+
+struct ctdb_db_traverse_state {
+       ctdb_rec_parser_func_t parser;
+       void *private_data;
+       bool extract_header;
+       int error;
+};
+
+static int ctdb_db_traverse_handler(struct tdb_context *tdb, TDB_DATA key,
+                                   TDB_DATA data, void *private_data)
+{
+       struct ctdb_db_traverse_state *state =
+               (struct ctdb_db_traverse_state *)private_data;
+       int ret;
+
+       if (state->extract_header) {
+               struct ctdb_ltdb_header header;
+               size_t len;
+
+               ret = ctdb_ltdb_header_pull(data.dptr, data.dsize, &header);
+               if (ret != 0) {
+                       state->error = ret;
+                       return 1;
+               }
+
+               len = ctdb_ltdb_header_len(&header);
+
+               data.dptr += len;
+               data.dsize -= len;
+
+               ret = state->parser(0, &header, key, data, state->private_data);
+       } else {
+               ret = state->parser(0, NULL, key, data, state->private_data);
+       }
+
+       if (ret != 0) {
+               state->error = ret;
+               return 1;
+       }
+
+       return 0;
+}
+
+int ctdb_db_traverse(struct ctdb_db_context *db, bool readonly,
+                    bool extract_header,
+                    ctdb_rec_parser_func_t parser, void *private_data)
+{
+       struct ctdb_db_traverse_state state;
+       int ret;
+
+       state.parser = parser;
+       state.private_data = private_data;
+       state.extract_header = extract_header;
+       state.error = 0;
+
+       if (readonly) {
+               ret = tdb_traverse_read(db->ltdb->tdb,
+                                       ctdb_db_traverse_handler, &state);
+       } else {
+               ret = tdb_traverse(db->ltdb->tdb,
+                                  ctdb_db_traverse_handler, &state);
+       }
+
+       if (ret == -1) {
+               return EIO;
+       }
+
+       return state.error;
+}
+
+static int ctdb_ltdb_fetch(struct ctdb_db_context *db, TDB_DATA key,
+                          struct ctdb_ltdb_header *header,
+                          TALLOC_CTX *mem_ctx, TDB_DATA *data)
+{
+       TDB_DATA rec;
+       int ret;
+
+       rec = tdb_fetch(db->ltdb->tdb, key);
+       if (rec.dsize < sizeof(struct ctdb_ltdb_header)) {
+               /* No record present */
+               if (rec.dptr != NULL) {
+                       free(rec.dptr);
+               }
+
+               if (tdb_error(db->ltdb->tdb) != TDB_ERR_NOEXIST) {
+                       return EIO;
+               }
+
+               header->rsn = 0;
+               header->dmaster = CTDB_UNKNOWN_PNN;
+               header->flags = 0;
+
+               if (data != NULL) {
+                       *data = tdb_null;
+               }
+               return 0;
+       }
+
+       ret = ctdb_ltdb_header_pull(rec.dptr, rec.dsize, header);
+       if (ret != 0) {
+               return ret;
+       }
+
+       ret = 0;
+       if (data != NULL) {
+               size_t offset = ctdb_ltdb_header_len(header);
+
+               data->dsize = rec.dsize - offset;
+               data->dptr = talloc_memdup(mem_ctx, rec.dptr + offset,
+                                          data->dsize);
+               if (data->dptr == NULL) {
+                       ret = ENOMEM;
+               }
+       }
+
+       free(rec.dptr);
+       return ret;
+}
+
+/*
+ * Fetch a record from volatile database
+ *
+ * Steps:
+ *  1. Get a lock on the hash chain
+ *  2. If the record does not exist, migrate the record
+ *  3. If readonly=true and delegations do not exist, migrate the record.
+ *  4. If readonly=false and delegations exist, migrate the record.
+ *  5. If the local node is not dmaster, migrate the record.
+ *  6. Return record
+ */
+
+struct ctdb_fetch_lock_state {
+       struct tevent_context *ev;
+       struct ctdb_client_context *client;
+       struct ctdb_record_handle *h;
+       bool readonly;
+       uint32_t pnn;
+};
+
+static int ctdb_fetch_lock_check(struct tevent_req *req);
+static void ctdb_fetch_lock_migrate(struct tevent_req *req);
+static void ctdb_fetch_lock_migrate_done(struct tevent_req *subreq);
+
+struct tevent_req *ctdb_fetch_lock_send(TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct ctdb_client_context *client,
+                                       struct ctdb_db_context *db,
+                                       TDB_DATA key, bool readonly)
+{
+       struct ctdb_fetch_lock_state *state;
+       struct tevent_req *req;
+       int ret;
+
+       req = tevent_req_create(mem_ctx, &state, struct ctdb_fetch_lock_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       state->ev = ev;
+       state->client = client;
+
+       state->h = talloc_zero(db, struct ctdb_record_handle);
+       if (tevent_req_nomem(state->h, req)) {
+               return tevent_req_post(req, ev);
+       }
+       state->h->client = client;
+       state->h->db = db;
+       state->h->key.dptr = talloc_memdup(state->h, key.dptr, key.dsize);
+       if (tevent_req_nomem(state->h->key.dptr, req)) {
+               return tevent_req_post(req, ev);
+       }
+       state->h->key.dsize = key.dsize;
+       state->h->readonly = false;
+
+       state->readonly = readonly;
+       state->pnn = ctdb_client_pnn(client);
+
+       /* Check that database is not persistent */
+       if (db->persistent) {
+               tevent_req_error(req, EINVAL);
+               return tevent_req_post(req, ev);
+       }
+
+       ret = ctdb_fetch_lock_check(req);
+       if (ret == 0) {
+               tevent_req_done(req);
+               return tevent_req_post(req, ev);
+       }
+       if (ret != EAGAIN) {
+               tevent_req_error(req, ret);
+               return tevent_req_post(req, ev);
+       }
+       return req;
+}
+
+static int ctdb_fetch_lock_check(struct tevent_req *req)
+{
+       struct ctdb_fetch_lock_state *state = tevent_req_data(
+               req, struct ctdb_fetch_lock_state);
+       struct ctdb_record_handle *h = state->h;
+       struct ctdb_ltdb_header header;
+       TDB_DATA data = tdb_null;
+       int ret, err = 0;
+       bool do_migrate = false;
+
+       ret = tdb_chainlock(state->h->db->ltdb->tdb, state->h->key);
+       if (ret != 0) {
+               err = EIO;
+               goto failed;
+       }
+
+       data = tdb_fetch(h->db->ltdb->tdb, h->key);
+       if (data.dptr == NULL) {
+               if (tdb_error(h->db->ltdb->tdb) == TDB_ERR_NOEXIST) {
+                       goto migrate;
+               } else {
+                       err = EIO;
+                       goto failed;
+               }
+       }
+
+       /* Got the record */
+       ret = ctdb_ltdb_header_pull(data.dptr, data.dsize, &header);
+       if (ret != 0) {
+               err = ret;
+               goto failed;
+       }
+
+       if (! state->readonly) {
+               /* Read/write access */
+               if (header.dmaster == state->pnn &&
+                   header.flags & CTDB_REC_RO_HAVE_DELEGATIONS) {
+                       goto migrate;
+               }
+
+               if (header.dmaster != state->pnn) {
+                       goto migrate;
+               }
+       } else {
+               /* Readonly access */
+               if (header.dmaster != state->pnn &&
+                   ! (header.flags & (CTDB_REC_RO_HAVE_READONLY |
+                                      CTDB_REC_RO_HAVE_DELEGATIONS))) {
+                       goto migrate;
+               }
+       }
+
+       /* We are the dmaster or readonly delegation */
+       h->header = header;
+       h->data = data;
+       if (header.flags & (CTDB_REC_RO_HAVE_READONLY |
+                           CTDB_REC_RO_HAVE_DELEGATIONS)) {
+               h->readonly = true;
+       }
+       return 0;
+
+migrate:
+       do_migrate = true;
+       err = EAGAIN;
+
+failed:
+       if (data.dptr != NULL) {
+               free(data.dptr);
+       }
+       ret = tdb_chainunlock(h->db->ltdb->tdb, h->key);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR, ("tdb_chainunlock failed on %s\n",
+                                 h->db->db_name));
+               return EIO;
+       }
+
+       if (do_migrate) {
+               ctdb_fetch_lock_migrate(req);
+       }
+       return err;
+}
+
+static void ctdb_fetch_lock_migrate(struct tevent_req *req)
+{
+       struct ctdb_fetch_lock_state *state = tevent_req_data(
+               req, struct ctdb_fetch_lock_state);
+       struct ctdb_req_call request;
+       struct tevent_req *subreq;
+
+       ZERO_STRUCT(request);
+       request.flags = CTDB_IMMEDIATE_MIGRATION;
+       if (state->readonly) {
+               request.flags |= CTDB_WANT_READONLY;
+       }
+       request.db_id = state->h->db->db_id;
+       request.callid = CTDB_NULL_FUNC;
+       request.key = state->h->key;
+
+       subreq = ctdb_client_call_send(state, state->ev, state->client,
+                                      &request);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+
+       tevent_req_set_callback(subreq, ctdb_fetch_lock_migrate_done, req);
+}
+
+static void ctdb_fetch_lock_migrate_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct ctdb_fetch_lock_state *state = tevent_req_data(
+               req, struct ctdb_fetch_lock_state);
+       struct ctdb_reply_call *reply;
+       int ret;
+       bool status;
+
+       status = ctdb_client_call_recv(subreq, state, &reply, &ret);
+       TALLOC_FREE(subreq);
+       if (! status) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       if (reply->status != 0) {
+               tevent_req_error(req, EIO);
+               return;
+       }
+       talloc_free(reply);
+
+       ret = ctdb_fetch_lock_check(req);
+       if (ret != 0) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       tevent_req_done(req);
+}
+
+static int ctdb_record_handle_destructor(struct ctdb_record_handle *h)
+{
+       tdb_chainunlock(h->db->ltdb->tdb, h->key);
+       free(h->data.dptr);
+       return 0;
+}
+
+struct ctdb_record_handle *ctdb_fetch_lock_recv(struct tevent_req *req,
+                                               struct ctdb_ltdb_header *header,
+                                               TALLOC_CTX *mem_ctx,
+                                               TDB_DATA *data, int *perr)
+{
+       struct ctdb_fetch_lock_state *state = tevent_req_data(
+               req, struct ctdb_fetch_lock_state);
+       struct ctdb_record_handle *h = state->h;
+       int err;
+
+       if (tevent_req_is_unix_error(req, &err)) {
+               if (perr != NULL) {
+                       *perr = err;
+               }
+               return NULL;
+       }
+
+       if (header != NULL) {
+               *header = h->header;
+       }
+       if (data != NULL) {
+               size_t offset;
+
+               offset = ctdb_ltdb_header_len(&h->header);
+
+               data->dsize = h->data.dsize - offset;
+               data->dptr = talloc_memdup(mem_ctx, h->data.dptr + offset,
+                                          data->dsize);
+               if (data->dptr == NULL) {
+                       TALLOC_FREE(state->h);
+                       if (perr != NULL) {
+                               *perr = ENOMEM;
+                       }
+                       return NULL;
+               }
+       }
+
+       talloc_set_destructor(h, ctdb_record_handle_destructor);
+       return h;
+}
+
+int ctdb_fetch_lock(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                   struct ctdb_client_context *client,
+                   struct ctdb_db_context *db, TDB_DATA key, bool readonly,
+                   struct ctdb_record_handle **out,
+                   struct ctdb_ltdb_header *header, TDB_DATA *data)
+{
+       struct tevent_req *req;
+       struct ctdb_record_handle *h;
+       int ret;
+
+       req = ctdb_fetch_lock_send(mem_ctx, ev, client, db, key, readonly);
+       if (req == NULL) {
+               return ENOMEM;
+       }
+
+       tevent_req_poll(req, ev);
+
+       h = ctdb_fetch_lock_recv(req, header, mem_ctx, data, &ret);
+       if (h == NULL) {
+               return ret;
+       }
+
+       *out = h;
+       return 0;
+}
+
+int ctdb_store_record(struct ctdb_record_handle *h, TDB_DATA data)
+{
+       TDB_DATA rec;
+       size_t offset;
+       int ret;
+
+       /* Cannot modify the record if it was obtained as a readonly copy */
+       if (h->readonly) {
+               return EINVAL;
+       }
+
+       /* Check if the new data is same */
+       if (h->data.dsize == data.dsize &&
+           memcmp(h->data.dptr, data.dptr, data.dsize) == 0) {
+               /* No need to do anything */
+               return 0;
+       }
+
+       offset = ctdb_ltdb_header_len(&h->header);
+       rec.dsize = offset + data.dsize;
+       rec.dptr = talloc_size(h, rec.dsize);
+       if (rec.dptr == NULL) {
+               return ENOMEM;
+       }
+
+       ctdb_ltdb_header_push(&h->header, rec.dptr);
+       memcpy(rec.dptr + offset, data.dptr, data.dsize);
+
+       ret = tdb_store(h->db->ltdb->tdb, h->key, rec, TDB_REPLACE);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR, ("Failed to store record in DB %s\n",
+                                 h->db->db_name));
+               return EIO;
+       }
+
+       talloc_free(rec.dptr);
+       return 0;
+}
+
+int ctdb_delete_record(struct ctdb_record_handle *h)
+{
+       TDB_DATA rec;
+       struct ctdb_key_data key;
+       int ret;
+
+       /* Cannot delete the record if it was obtained as a readonly copy */
+       if (h->readonly) {
+               return EINVAL;
+       }
+
+       rec.dsize = ctdb_ltdb_header_len(&h->header);
+       rec.dptr = talloc_size(h, rec.dsize);
+       if (rec.dptr == NULL) {
+               return ENOMEM;
+       }
+
+       ctdb_ltdb_header_push(&h->header, rec.dptr);
+
+       ret = tdb_store(h->db->ltdb->tdb, h->key, rec, TDB_REPLACE);
+       talloc_free(rec.dptr);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR, ("Failed to delete record in DB %s\n",
+                                 h->db->db_name));
+               return EIO;
+       }
+
+       key.db_id = h->db->db_id;
+       key.header = h->header;
+       key.key = h->key;
+
+       ret = ctdb_ctrl_schedule_for_deletion(h, h->ev, h->client,
+                                             h->client->pnn,
+                                             tevent_timeval_zero(), &key);
+       if (ret != 0) {
+               DEBUG(DEBUG_WARNING,
+                     ("Failed to mark record to be deleted in DB %s\n",
+                      h->db->db_name));
+               return ret;
+       }
+
+       return 0;
+}
+
+/*
+ * Global lock functions
+ */
+
+struct ctdb_g_lock_lock_state {
+       struct tevent_context *ev;
+       struct ctdb_client_context *client;
+       struct ctdb_db_context *db;
+       TDB_DATA key;
+       struct ctdb_server_id my_sid;
+       enum ctdb_g_lock_type lock_type;
+       struct ctdb_record_handle *h;
+       /* state for verification of active locks */
+       struct ctdb_g_lock_list *lock_list;
+       unsigned int current;
+};
+
+static void ctdb_g_lock_lock_fetched(struct tevent_req *subreq);
+static void ctdb_g_lock_lock_process_locks(struct tevent_req *req);
+static void ctdb_g_lock_lock_checked(struct tevent_req *subreq);
+static int ctdb_g_lock_lock_update(struct tevent_req *req);
+static void ctdb_g_lock_lock_retry(struct tevent_req *subreq);
+
+static bool ctdb_g_lock_conflicts(enum ctdb_g_lock_type l1,
+                                 enum ctdb_g_lock_type l2)
+{
+       if ((l1 == G_LOCK_READ) && (l2 == G_LOCK_READ)) {
+               return false;
+       }
+       return true;
+}
+
+struct tevent_req *ctdb_g_lock_lock_send(TALLOC_CTX *mem_ctx,
+                                        struct tevent_context *ev,
+                                        struct ctdb_client_context *client,
+                                        struct ctdb_db_context *db,
+                                        const char *keyname,
+                                        struct ctdb_server_id *sid,
+                                        bool readonly)
+{
+       struct tevent_req *req, *subreq;
+       struct ctdb_g_lock_lock_state *state;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct ctdb_g_lock_lock_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       state->ev = ev;
+       state->client = client;
+       state->db = db;
+       state->key.dptr = discard_const(keyname);
+       state->key.dsize = strlen(keyname) + 1;
+       state->my_sid = *sid;
+       state->lock_type = (readonly ? G_LOCK_READ : G_LOCK_WRITE);
+
+       subreq = ctdb_fetch_lock_send(state, ev, client, db, state->key,
+                                     false);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, ctdb_g_lock_lock_fetched, req);
+
+       return req;
+}
+
+static void ctdb_g_lock_lock_fetched(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct ctdb_g_lock_lock_state *state = tevent_req_data(
+               req, struct ctdb_g_lock_lock_state);
+       TDB_DATA data;
+       int ret = 0;
+
+       state->h = ctdb_fetch_lock_recv(subreq, NULL, state, &data, &ret);
+       TALLOC_FREE(subreq);
+       if (state->h == NULL) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       if (state->lock_list != NULL) {
+               TALLOC_FREE(state->lock_list);
+               state->current = 0;
+       }
+
+       ret = ctdb_g_lock_list_pull(data.dptr, data.dsize, state,
+                                   &state->lock_list);
+       talloc_free(data.dptr);
+       if (ret != 0) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       ctdb_g_lock_lock_process_locks(req);
+}
+
+static void ctdb_g_lock_lock_process_locks(struct tevent_req *req)
+{
+       struct ctdb_g_lock_lock_state *state = tevent_req_data(
+               req, struct ctdb_g_lock_lock_state);
+       struct tevent_req *subreq;
+       struct ctdb_g_lock *lock;
+       bool check_server = false;
+       int ret;
+
+       while (state->current < state->lock_list->num) {
+               lock = &state->lock_list->lock[state->current];
+
+               /* We should not ask for the same lock more than once */
+               if (ctdb_server_id_equal(&lock->sid, &state->my_sid)) {
+                       tevent_req_error(req, EDEADLK);
+                       return;
+               }
+
+               if (ctdb_g_lock_conflicts(lock->type, state->lock_type)) {
+                       check_server = true;
+                       break;
+               }
+
+               state->current += 1;
+       }
+
+       if (check_server) {
+               struct ctdb_req_control request;
+               struct ctdb_uint64_array u64_array;
+
+               u64_array.num = 1;
+               u64_array.val = &lock->sid.unique_id;
+
+               ctdb_req_control_check_srvids(&request, &u64_array);
+               subreq = ctdb_client_control_send(state, state->ev,
+                                                 state->client,
+                                                 state->client->pnn,
+                                                 tevent_timeval_zero(),
+                                                 &request);
+               if (tevent_req_nomem(subreq, req)) {
+                       return;
+               }
+               tevent_req_set_callback(subreq, ctdb_g_lock_lock_checked, req);
+               return;
+       }
+
+       /* There is no conflict, add ourself to the lock_list */
+       state->lock_list->lock = talloc_realloc(state->lock_list,
+                                               state->lock_list->lock,
+                                               struct ctdb_g_lock,
+                                               state->lock_list->num + 1);
+       if (state->lock_list->lock == NULL) {
+               tevent_req_error(req, ENOMEM);
+               return;
+       }
+
+       lock = &state->lock_list->lock[state->lock_list->num];
+       lock->type = state->lock_type;
+       lock->sid = state->my_sid;
+       state->lock_list->num += 1;
+
+       ret = ctdb_g_lock_lock_update(req);
+       if (ret != 0) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       tevent_req_done(req);
+}
+
+static void ctdb_g_lock_lock_checked(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct ctdb_g_lock_lock_state *state = tevent_req_data(
+               req, struct ctdb_g_lock_lock_state);
+       struct ctdb_reply_control *reply;
+       struct ctdb_uint8_array *u8_array;
+       int ret;
+       bool status;
+       int8_t val;
+
+       status = ctdb_client_control_recv(subreq, &ret, state, &reply);
+       TALLOC_FREE(subreq);
+       if (! status) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       ret = ctdb_reply_control_check_srvids(reply, state, &u8_array);
+       if (ret != 0) {
+               tevent_req_error(req, ENOMEM);
+               return;
+       }
+
+       if (u8_array->num != 1) {
+               talloc_free(u8_array);
+               tevent_req_error(req, EIO);
+               return;
+       }
+
+       val = u8_array->val[0];
+       talloc_free(u8_array);
+
+       if (val == 1) {
+               /* server process exists, need to retry */
+               subreq = tevent_wakeup_send(state, state->ev,
+                                           tevent_timeval_current_ofs(1,0));
+               if (tevent_req_nomem(subreq, req)) {
+                       return;
+               }
+               tevent_req_set_callback(subreq, ctdb_g_lock_lock_retry, req);
+               return;
+       }
+
+       /* server process does not exist, remove conflicting entry */
+       state->lock_list->lock[state->current] =
+               state->lock_list->lock[state->lock_list->num-1];
+       state->lock_list->num -= 1;
+
+       ret = ctdb_g_lock_lock_update(req);
+       if (ret != 0) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       ctdb_g_lock_lock_process_locks(req);
+}
+
+static int ctdb_g_lock_lock_update(struct tevent_req *req)
+{
+       struct ctdb_g_lock_lock_state *state = tevent_req_data(
+               req, struct ctdb_g_lock_lock_state);
+       TDB_DATA data;
+       int ret;
+
+       data.dsize = ctdb_g_lock_list_len(state->lock_list);
+       data.dptr = talloc_size(state, data.dsize);
+       if (data.dptr == NULL) {
+               return ENOMEM;
+       }
+
+       ctdb_g_lock_list_push(state->lock_list, data.dptr);
+       ret = ctdb_store_record(state->h, data);
+       talloc_free(data.dptr);
+       return ret;
+}
+
+#if 0
+static int ctdb_g_lock_lock_update(struct ctdb_g_lock_lock_state *state,
+                                  struct ctdb_g_lock_list *lock_list,
+                                  struct ctdb_record_handle *h)
+{
+       struct ctdb_g_lock *lock;
+       bool conflict = false;
+       bool modified = false;
+       int ret, i;
+
+       for (i=0; i<lock_list->num; i++) {
+               lock = &lock_list->lock[i];
+
+               /* We should not ask for lock more than once */
+               if (ctdb_server_id_equal(&lock->sid, &state->my_sid)) {
+                       return EDEADLK;
+               }
+
+               if (ctdb_g_lock_conflicts(lock->type, state->lock_type)) {
+                       bool exists;
+
+                       conflict = true;
+                       ret = ctdb_server_id_exists(state->client, &lock->sid,
+                                                   &exists);
+                       if (ret != 0) {
+                               return ret;
+                       }
+
+                       if (exists) {
+                               break;
+                       }
+
+                       /* Server does not exist, delete conflicting entry */
+                       lock_list->lock[i] = lock_list->lock[lock_list->num-1];
+                       lock_list->num -= 1;
+                       modified = true;
+               }
+       }
+
+       if (! conflict) {
+               lock = talloc_realloc(lock_list, lock_list->lock,
+                                     struct ctdb_g_lock, lock_list->num+1);
+               if (lock == NULL) {
+                       return ENOMEM;
+               }
+
+               lock[lock_list->num].type = state->lock_type;
+               lock[lock_list->num].sid = state->my_sid;
+               lock_list->lock = lock;
+               lock_list->num += 1;
+               modified = true;
+       }
+
+       if (modified) {
+               TDB_DATA data;
+
+               data.dsize = ctdb_g_lock_list_len(lock_list);
+               data.dptr = talloc_size(state, data.dsize);
+               if (data.dptr == NULL) {
+                       return ENOMEM;
+               }
+
+               ctdb_g_lock_list_push(lock_list, data.dptr);
+               ret = ctdb_store_record(h, data);
+               talloc_free(data.dptr);
+               if (ret != 0) {
+                       return ret;
+               }
+       }
+
+       if (conflict) {
+               return EAGAIN;
+       }
+       return 0;
+}
+#endif
+
+static void ctdb_g_lock_lock_retry(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct ctdb_g_lock_lock_state *state = tevent_req_data(
+               req, struct ctdb_g_lock_lock_state);
+       bool success;
+
+       success = tevent_wakeup_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (! success) {
+               tevent_req_error(req, ENOMEM);
+               return;
+       }
+
+       subreq = ctdb_fetch_lock_send(state, state->ev, state->client,
+                                     state->db, state->key, false);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, ctdb_g_lock_lock_fetched, req);
+}
+
+bool ctdb_g_lock_lock_recv(struct tevent_req *req, int *perr)
+{
+       struct ctdb_g_lock_lock_state *state = tevent_req_data(
+               req, struct ctdb_g_lock_lock_state);
+       int err;
+
+       TALLOC_FREE(state->h);
+
+       if (tevent_req_is_unix_error(req, &err)) {
+               if (perr != NULL) {
+                       *perr = err;
+               }
+               return false;
+       }
+
+       return true;
+}
+
+struct ctdb_g_lock_unlock_state {
+       struct tevent_context *ev;
+       struct ctdb_client_context *client;
+       struct ctdb_db_context *db;
+       TDB_DATA key;
+       struct ctdb_server_id my_sid;
+       struct ctdb_record_handle *h;
+       struct ctdb_g_lock_list *lock_list;
+};
+
+static void ctdb_g_lock_unlock_fetched(struct tevent_req *subreq);
+static int ctdb_g_lock_unlock_update(struct tevent_req *req);
+
+struct tevent_req *ctdb_g_lock_unlock_send(TALLOC_CTX *mem_ctx,
+                                          struct tevent_context *ev,
+                                          struct ctdb_client_context *client,
+                                          struct ctdb_db_context *db,
+                                          const char *keyname,
+                                          struct ctdb_server_id sid)
+{
+       struct tevent_req *req, *subreq;
+       struct ctdb_g_lock_unlock_state *state;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct ctdb_g_lock_unlock_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       state->ev = ev;
+       state->client = client;
+       state->db = db;
+       state->key.dptr = discard_const(keyname);
+       state->key.dsize = strlen(keyname) + 1;
+       state->my_sid = sid;
+
+       subreq = ctdb_fetch_lock_send(state, ev, client, db, state->key,
+                                     false);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, ctdb_g_lock_unlock_fetched, req);
+
+       return req;
+}
+
+static void ctdb_g_lock_unlock_fetched(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct ctdb_g_lock_unlock_state *state = tevent_req_data(
+               req, struct ctdb_g_lock_unlock_state);
+       TDB_DATA data;
+       int ret = 0;
+
+       state->h = ctdb_fetch_lock_recv(subreq, NULL, state, &data, &ret);
+       TALLOC_FREE(subreq);
+       if (state->h == NULL) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       ret = ctdb_g_lock_list_pull(data.dptr, data.dsize, state,
+                                   &state->lock_list);
+       if (ret != 0) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       ret = ctdb_g_lock_unlock_update(req);
+       if (ret != 0) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       tevent_req_done(req);
+}
+
+static int ctdb_g_lock_unlock_update(struct tevent_req *req)
+{
+       struct ctdb_g_lock_unlock_state *state = tevent_req_data(
+               req, struct ctdb_g_lock_unlock_state);
+       struct ctdb_g_lock *lock;
+       int ret, i;
+
+       for (i=0; i<state->lock_list->num; i++) {
+               lock = &state->lock_list->lock[i];
+
+               if (ctdb_server_id_equal(&lock->sid, &state->my_sid)) {
+                       break;
+               }
+       }
+
+       if (i < state->lock_list->num) {
+               state->lock_list->lock[i] =
+                       state->lock_list->lock[state->lock_list->num-1];
+               state->lock_list->num -= 1;
+       }
+
+       if (state->lock_list->num == 0) {
+               ctdb_delete_record(state->h);
+       } else {
+               TDB_DATA data;
+
+               data.dsize = ctdb_g_lock_list_len(state->lock_list);
+               data.dptr = talloc_size(state, data.dsize);
+               if (data.dptr == NULL) {
+                       return ENOMEM;
+               }
+
+               ctdb_g_lock_list_push(state->lock_list, data.dptr);
+               ret = ctdb_store_record(state->h, data);
+               talloc_free(data.dptr);
+               if (ret != 0) {
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+bool ctdb_g_lock_unlock_recv(struct tevent_req *req, int *perr)
+{
+       struct ctdb_g_lock_unlock_state *state = tevent_req_data(
+               req, struct ctdb_g_lock_unlock_state);
+       int err;
+
+       TALLOC_FREE(state->h);
+
+       if (tevent_req_is_unix_error(req, &err)) {
+               if (perr != NULL) {
+                       *perr = err;
+               }
+               return false;
+       }
+
+       return true;
+}
+
+/*
+ * Persistent database functions
+ */
+struct ctdb_transaction_start_state {
+       struct tevent_context *ev;
+       struct ctdb_client_context *client;
+       struct timeval timeout;
+       struct ctdb_transaction_handle *h;
+       uint32_t destnode;
+};
+
+static void ctdb_transaction_g_lock_attached(struct tevent_req *subreq);
+static void ctdb_transaction_register_done(struct tevent_req *subreq);
+static void ctdb_transaction_g_lock_done(struct tevent_req *subreq);
+static int ctdb_transaction_handle_destructor(struct ctdb_transaction_handle *h);
+
+struct tevent_req *ctdb_transaction_start_send(TALLOC_CTX *mem_ctx,
+                                              struct tevent_context *ev,
+                                              struct ctdb_client_context *client,
+                                              struct timeval timeout,
+                                              struct ctdb_db_context *db,
+                                              bool readonly)
+{
+       struct ctdb_transaction_start_state *state;
+       struct tevent_req *req, *subreq;
+       struct ctdb_transaction_handle *h;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct ctdb_transaction_start_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       if (! db->persistent) {
+               tevent_req_error(req, EINVAL);
+               return tevent_req_post(req, ev);
+       }
+
+       state->ev = ev;
+       state->client = client;
+       state->destnode = ctdb_client_pnn(client);
+
+       h = talloc_zero(db, struct ctdb_transaction_handle);
+       if (tevent_req_nomem(h, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       h->client = client;
+       h->db = db;
+       h->readonly = readonly;
+       h->updated = false;
+
+       /* SRVID is unique for databases, so client can have transactions active
+        * for multiple databases */
+       h->sid.pid = getpid();
+       h->sid.task_id = db->db_id;
+       h->sid.vnn = state->destnode;
+       h->sid.unique_id = h->sid.task_id;
+       h->sid.unique_id = (h->sid.unique_id << 32) | h->sid.pid;
+
+       h->recbuf = talloc_zero(h, struct ctdb_rec_buffer);
+       if (tevent_req_nomem(h->recbuf, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       h->lock_name = talloc_asprintf(h, "transaction_db_0x%08x", db->db_id);
+       if (tevent_req_nomem(h->lock_name, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       state->h = h;
+
+       subreq = ctdb_attach_send(state, ev, client, timeout, "g_lock.tdb", 0);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, ctdb_transaction_g_lock_attached, req);
+
+       return req;
+}
+
+static void ctdb_transaction_g_lock_attached(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct ctdb_transaction_start_state *state = tevent_req_data(
+               req, struct ctdb_transaction_start_state);
+       struct ctdb_req_control request;
+       bool status;
+       int ret;
+
+       status = ctdb_attach_recv(subreq, &ret, &state->h->db_g_lock);
+       TALLOC_FREE(subreq);
+       if (! status) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       ctdb_req_control_register_srvid(&request, state->h->sid.unique_id);
+       subreq = ctdb_client_control_send(state, state->ev, state->client,
+                                         state->destnode, state->timeout,
+                                         &request);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, ctdb_transaction_register_done, req);
+}
+
+static void ctdb_transaction_register_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct ctdb_transaction_start_state *state = tevent_req_data(
+               req, struct ctdb_transaction_start_state);
+       struct ctdb_reply_control *reply;
+       bool status;
+       int ret;
+
+       status = ctdb_client_control_recv(subreq, &ret, state, &reply);
+       TALLOC_FREE(subreq);
+       if (! status) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       ret = ctdb_reply_control_register_srvid(reply);
+       talloc_free(reply);
+       if (ret != 0) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       subreq = ctdb_g_lock_lock_send(state, state->ev, state->client,
+                                      state->h->db_g_lock, state->h->lock_name,
+                                      &state->h->sid, state->h->readonly);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, ctdb_transaction_g_lock_done, req);
+}
+
+static void ctdb_transaction_g_lock_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       int ret;
+       bool status;
+
+       status = ctdb_g_lock_lock_recv(subreq, &ret);
+       TALLOC_FREE(subreq);
+       if (! status) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       tevent_req_done(req);
+}
+
+struct ctdb_transaction_handle *ctdb_transaction_start_recv(
+                                       struct tevent_req *req,
+                                       int *perr)
+{
+       struct ctdb_transaction_start_state *state = tevent_req_data(
+               req, struct ctdb_transaction_start_state);
+       struct ctdb_transaction_handle *h = state->h;
+       int err;
+
+       if (tevent_req_is_unix_error(req, &err)) {
+               if (perr != NULL) {
+                       *perr = err;
+               }
+               return NULL;
+       }
+
+       talloc_set_destructor(h, ctdb_transaction_handle_destructor);
+       return h;
+}
+
+static int ctdb_transaction_handle_destructor(struct ctdb_transaction_handle *h)
+{
+       int ret;
+
+       ret = ctdb_ctrl_deregister_srvid(h, h->ev, h->client, h->client->pnn,
+                                        tevent_timeval_zero(),
+                                        h->sid.unique_id);
+       if (ret != 0) {
+               DEBUG(DEBUG_WARNING, ("Failed to deregister SRVID\n"));
+       }
+
+       return 0;
+}
+
+int ctdb_transaction_start(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                          struct ctdb_client_context *client,
+                          struct timeval timeout,
+                          struct ctdb_db_context *db, bool readonly,
+                          struct ctdb_transaction_handle **out)
+{
+       struct tevent_req *req;
+       struct ctdb_transaction_handle *h;
+       int ret;
+
+       req = ctdb_transaction_start_send(mem_ctx, ev, client, timeout, db,
+                                         readonly);
+       if (req == NULL) {
+               return ENOMEM;
+       }
+
+       tevent_req_poll(req, ev);
+
+       h = ctdb_transaction_start_recv(req, &ret);
+       if (h == NULL) {
+               return ret;
+       }
+
+       *out = h;
+       return 0;
+}
+
+struct ctdb_transaction_record_fetch_state {
+       TDB_DATA key, data;
+       struct ctdb_ltdb_header header;
+       bool found;
+};
+
+static int ctdb_transaction_record_fetch_traverse(uint32_t reqid,
+                                                 struct ctdb_ltdb_header *header,
+                                                 TDB_DATA key,
+                                                 TDB_DATA data,
+                                                 void *private_data)
+{
+       struct ctdb_transaction_record_fetch_state *state =
+               (struct ctdb_transaction_record_fetch_state *)private_data;
+
+       if (state->key.dsize == key.dsize &&
+           memcmp(state->key.dptr, key.dptr, key.dsize) == 0) {
+               state->data = data;
+               state->header = *header;
+               state->found = true;
+       }
+
+       return 0;
+}
+
+static int ctdb_transaction_record_fetch(struct ctdb_transaction_handle *h,
+                                        TDB_DATA key,
+                                        struct ctdb_ltdb_header *header,
+                                        TDB_DATA *data)
+{
+       struct ctdb_transaction_record_fetch_state state;
+       int ret;
+
+       state.key = key;
+       state.found = false;
+
+       ret = ctdb_rec_buffer_traverse(h->recbuf,
+                                      ctdb_transaction_record_fetch_traverse,
+                                      &state);
+       if (ret != 0) {
+               return ret;
+       }
+
+       if (state.found) {
+               if (header != NULL) {
+                       *header = state.header;
+               }
+               if (data != NULL) {
+                       *data = state.data;
+               }
+               return 0;
+       }
+
+       return ENOENT;
+}
+
+int ctdb_transaction_fetch_record(struct ctdb_transaction_handle *h,
+                                 TDB_DATA key,
+                                 TALLOC_CTX *mem_ctx, TDB_DATA *data)
+{
+       TDB_DATA tmp_data;
+       struct ctdb_ltdb_header header;
+       int ret;
+
+       ret = ctdb_transaction_record_fetch(h, key, NULL, &tmp_data);
+       if (ret == 0) {
+               data->dptr = talloc_memdup(mem_ctx, tmp_data.dptr,
+                                          tmp_data.dsize);
+               if (data->dptr == NULL) {
+                       return ENOMEM;
+               }
+               data->dsize = tmp_data.dsize;
+               return 0;
+       }
+
+       ret = ctdb_ltdb_fetch(h->db, key, &header, mem_ctx, data);
+       if (ret != 0) {
+               return ret;
+       }
+
+       ret = ctdb_rec_buffer_add(h, h->recbuf, 0, &header, key, *data);
+       if (ret != 0) {
+               return ret;
+       }
+
+       return 0;
+}
+
+int ctdb_transaction_store_record(struct ctdb_transaction_handle *h,
+                                 TDB_DATA key, TDB_DATA data)
+{
+       TALLOC_CTX *tmp_ctx;
+       struct ctdb_ltdb_header header;
+       TDB_DATA old_data;
+       int ret;
+
+       if (h->readonly) {
+               return EINVAL;
+       }
+
+       tmp_ctx = talloc_new(h);
+       if (tmp_ctx == NULL) {
+               return ENOMEM;
+       }
+
+       ret = ctdb_transaction_record_fetch(h, key, &header, &old_data);
+       if (ret != 0) {
+               ret = ctdb_ltdb_fetch(h->db, key, &header, tmp_ctx, &old_data);
+               if (ret != 0) {
+                       return ret;
+               }
+       }
+
+       if (old_data.dsize == data.dsize &&
+           memcmp(old_data.dptr, data.dptr, data.dsize) == 0) {
+               talloc_free(tmp_ctx);
+               return 0;
+       }
+
+       header.dmaster = ctdb_client_pnn(h->client);
+       header.rsn += 1;
+
+       ret = ctdb_rec_buffer_add(h, h->recbuf, 0, &header, key, data);
+       talloc_free(tmp_ctx);
+       if (ret != 0) {
+               return ret;
+       }
+       h->updated = true;
+
+       return 0;
+}
+
+int ctdb_transaction_delete_record(struct ctdb_transaction_handle *h,
+                                  TDB_DATA key)
+{
+       return ctdb_transaction_store_record(h, key, tdb_null);
+}
+
+static int ctdb_transaction_store_db_seqnum(struct ctdb_transaction_handle *h,
+                                           uint64_t seqnum)
+{
+       const char *keyname = CTDB_DB_SEQNUM_KEY;
+       TDB_DATA key, data;
+
+       key.dptr = discard_const(keyname);
+       key.dsize = strlen(keyname) + 1;
+
+       data.dptr = (uint8_t *)&seqnum;
+       data.dsize = sizeof(seqnum);
+
+       return ctdb_transaction_store_record(h, key, data);
+}
+
+struct ctdb_transaction_commit_state {
+       struct tevent_context *ev;
+       struct ctdb_transaction_handle *h;
+       uint64_t seqnum;
+};
+
+static void ctdb_transaction_commit_done(struct tevent_req *subreq);
+static void ctdb_transaction_commit_try(struct tevent_req *subreq);
+
+struct tevent_req *ctdb_transaction_commit_send(
+                                       TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct ctdb_transaction_handle *h)
+{
+       struct tevent_req *req, *subreq;
+       struct ctdb_transaction_commit_state *state;
+       int ret;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct ctdb_transaction_commit_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       state->ev = ev;
+       state->h = h;
+
+       ret = ctdb_ctrl_get_db_seqnum(state, ev, h->client,
+                                     h->client->pnn, tevent_timeval_zero(),
+                                     h->db->db_id, &state->seqnum);
+       if (ret != 0) {
+               tevent_req_error(req, ret);
+               return tevent_req_post(req, ev);
+       }
+
+       ret = ctdb_transaction_store_db_seqnum(h, state->seqnum+1);
+       if (ret != 0) {
+               tevent_req_error(req, ret);
+               return tevent_req_post(req, ev);
+       }
+
+       subreq = ctdb_recovery_wait_send(state, ev, h->client);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, ctdb_transaction_commit_try, req);
+
+       return req;
+}
+
+static void ctdb_transaction_commit_try(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct ctdb_transaction_commit_state *state = tevent_req_data(
+               req, struct ctdb_transaction_commit_state);
+       struct ctdb_req_control request;
+       int ret;
+       bool status;
+
+       status = ctdb_recovery_wait_recv(subreq, &ret);
+       TALLOC_FREE(subreq);
+       if (! status) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       ctdb_req_control_trans3_commit(&request, state->h->recbuf);
+       subreq = ctdb_client_control_send(state, state->ev, state->h->client,
+                                         state->h->client->pnn,
+                                         tevent_timeval_zero(), &request);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, ctdb_transaction_commit_done, req);
+}
+
+static void ctdb_transaction_commit_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct ctdb_transaction_commit_state *state = tevent_req_data(
+               req, struct ctdb_transaction_commit_state);
+       struct ctdb_reply_control *reply;
+       uint64_t seqnum;
+       int ret;
+       bool status;
+
+       status = ctdb_client_control_recv(subreq, &ret, state, &reply);
+       TALLOC_FREE(subreq);
+       if (! status) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       ret = ctdb_reply_control_trans3_commit(reply);
+       if (ret < 0) {
+               /* Control failed due to recovery */
+               subreq = ctdb_recovery_wait_send(state, state->ev,
+                                                state->h->client);
+               if (tevent_req_nomem(subreq, req)) {
+                       return;
+               }
+               tevent_req_set_callback(subreq, ctdb_transaction_commit_try,
+                                       req);
+               return;
+       }
+
+       ret = ctdb_ctrl_get_db_seqnum(state, state->ev, state->h->client,
+                                     state->h->client->pnn,
+                                     tevent_timeval_zero(),
+                                     state->h->db->db_id, &seqnum);
+       if (ret != 0) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       if (seqnum == state->seqnum) {
+               subreq = ctdb_recovery_wait_send(state, state->ev,
+                                                state->h->client);
+               if (tevent_req_nomem(subreq, req)) {
+                       return;
+               }
+               tevent_req_set_callback(subreq, ctdb_transaction_commit_try,
+                                       req);
+               return;
+       }
+
+       if (seqnum != state->seqnum + 1) {
+               tevent_req_error(req, EIO);
+               return;
+       }
+
+       tevent_req_done(req);
+}
+
+bool ctdb_transaction_commit_recv(struct tevent_req *req, int *perr)
+{
+       struct ctdb_transaction_commit_state *state = tevent_req_data(
+               req, struct ctdb_transaction_commit_state);
+       int err;
+
+       if (tevent_req_is_unix_error(req, &err)) {
+               if (perr != NULL) {
+                       *perr = err;
+               }
+               TALLOC_FREE(state->h);
+               return false;
+       }
+
+       TALLOC_FREE(state->h);
+       return true;
+}
+
+int ctdb_transaction_commit(struct ctdb_transaction_handle *h)
+{
+       struct tevent_req *req;
+       int ret;
+       bool status;
+
+       if (h->readonly || ! h->updated) {
+               talloc_free(h);
+               return 0;
+       }
+
+       req = ctdb_transaction_commit_send(h, h->ev, h);
+       if (req == NULL) {
+               talloc_free(h);
+               return ENOMEM;
+       }
+
+       tevent_req_poll(req, h->ev);
+
+       status = ctdb_transaction_commit_recv(req, &ret);
+       if (! status) {
+               talloc_free(h);
+               return ret;
+       }
+
+       talloc_free(h);
+       return 0;
+}
+
+int ctdb_transaction_cancel(struct ctdb_transaction_handle *h)
+{
+       talloc_free(h);
+       return 0;
+}
+
+/*
+ * TODO:
+ *
+ * In future Samba should register SERVER_ID.
+ * Make that structure same as struct srvid {}.
+ */
diff --git a/ctdb/client/client_message.c b/ctdb/client/client_message.c
new file mode 100644 (file)
index 0000000..bfa9ba2
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+   CTDB client code
+
+   Copyright (C) Amitay Isaacs  2015
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "system/network.h"
+#include "system/filesys.h"
+
+#include <talloc.h>
+#include <tevent.h>
+#include <tdb.h>
+
+#include "lib/util/tevent_unix.h"
+
+#include "common/reqid.h"
+#include "common/srvid.h"
+#include "common/comm.h"
+
+#include "protocol/protocol.h"
+#include "protocol/protocol_api.h"
+
+#include "client/client_private.h"
+#include "client/client.h"
+
+
+/*
+ * Handle REQ_MESSAGE
+ */
+
+struct ctdb_client_message_state {
+       struct ctdb_client_context *client;
+       uint32_t reqid;
+};
+
+static int ctdb_client_message_state_destructor(
+       struct ctdb_client_message_state *state);
+static void ctdb_client_message_done(struct tevent_req *subreq);
+
+struct tevent_req *ctdb_client_message_send(TALLOC_CTX *mem_ctx,
+                                           struct tevent_context *ev,
+                                           struct ctdb_client_context *client,
+                                           uint32_t destnode,
+                                           struct ctdb_req_message *message)
+{
+       struct tevent_req *req, *subreq;
+       struct ctdb_client_message_state *state;
+       struct ctdb_req_header h;
+       uint32_t reqid;
+       uint8_t *buf;
+       size_t buflen;
+       int ret;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct ctdb_client_message_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       reqid = reqid_new(client->idr, state);
+       if (reqid == REQID_INVALID) {
+               talloc_free(req);
+               return NULL;
+       }
+
+       state->client = client;
+       state->reqid = reqid;
+
+       talloc_set_destructor(state, ctdb_client_message_state_destructor);
+
+       ctdb_req_header_fill(&h, 0, CTDB_REQ_MESSAGE, destnode,
+                            client->pnn, reqid);
+
+       ret = ctdb_req_message_push(&h, message, state, &buf, &buflen);
+       if (ret != 0) {
+               tevent_req_error(req, ret);
+               return tevent_req_post(req, ev);
+       }
+
+       subreq = comm_write_send(state, ev, client->comm, buf, buflen);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, ctdb_client_message_done, req);
+
+       return req;
+}
+
+static int ctdb_client_message_state_destructor(
+       struct ctdb_client_message_state *state)
+{
+       reqid_remove(state->client->idr, state->reqid);
+       return 0;
+}
+
+static void ctdb_client_message_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       int ret;
+       bool status;
+
+       status = comm_write_recv(subreq, &ret);
+       TALLOC_FREE(subreq);
+       if (! status) {
+               tevent_req_error(req, ret);
+               return;
+       }
+
+       tevent_req_done(req);
+}
+
+bool ctdb_client_message_recv(struct tevent_req *req, int *perr)
+{
+       int err;
+
+       if (tevent_req_is_unix_error(req, &err)) {
+               if (perr != NULL) {
+                       *perr = err;
+               }
+               return false;
+       }
+
+       return true;
+}
+
+void ctdb_client_req_message(struct ctdb_client_context *client,
+                            uint8_t *buf, size_t buflen, uint32_t reqid)
+{
+       struct ctdb_req_header h;
+       struct ctdb_req_message_data message;
+       TALLOC_CTX *tmp_ctx = talloc_new(client);
+       int ret;
+
+       ret = ctdb_req_message_data_pull(buf, buflen, &h, tmp_ctx, &message);
+       if (ret != 0) {
+               return;
+       }
+
+       srvid_dispatch(client->srv, message.srvid, CTDB_SRVID_ALL,
+                      message.data);
+       talloc_free(tmp_ctx);
+}
+
+/*
+ * sync version of message send
+ */
+
+int ctdb_client_message(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                       struct ctdb_client_context *client,
+                       uint32_t destnode, struct ctdb_req_message *message)
+{
+       TALLOC_CTX *tmp_ctx;
+       struct tevent_req *req;
+       int ret;
+       bool status;
+
+       tmp_ctx = talloc_new(client);
+       if (tmp_ctx == NULL) {
+               return ENOMEM;
+       }
+
+       req = ctdb_client_message_send(tmp_ctx, ev, client, destnode, message);
+       if (req == NULL) {
+               talloc_free(tmp_ctx);
+               return ENOMEM;
+       }
+
+       tevent_req_poll(req, ev);
+
+       status = ctdb_client_message_recv(req, &ret);
+       if (! status) {
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       talloc_free(tmp_ctx);
+       return 0;
+}
+
+int ctdb_client_set_message_handler(TALLOC_CTX *mem_ctx,
+                                   struct tevent_context *ev,
+                                   struct ctdb_client_context *client,
+                                   uint64_t srvid, srvid_handler_fn handler,
+                                   void *private_data)
+{
+       int ret;
+
+       ret = ctdb_ctrl_register_srvid(mem_ctx, ev, client, client->pnn,
+                                      tevent_timeval_zero(), srvid);
+       if (ret != 0) {
+               return ret;
+       }
+
+       return srvid_register(client->srv, client, srvid,
+                             handler, private_data);
+}
+
+int ctdb_client_remove_message_handler(TALLOC_CTX *mem_ctx,
+                                      struct tevent_context *ev,
+                                      struct ctdb_client_context *client,
+                                      uint64_t srvid, void *private_data)
+{
+       int ret;
+
+       ret = ctdb_ctrl_deregister_srvid(mem_ctx, ev, client, client->pnn,
+                                        tevent_timeval_zero(), srvid);
+       if (ret != 0) {
+               return ret;
+       }
+
+       return srvid_deregister(client->srv, srvid, private_data);
+}
diff --git a/ctdb/client/client_message_sync.c b/ctdb/client/client_message_sync.c
new file mode 100644 (file)
index 0000000..b6e53f3
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+   CTDB client code
+
+   Copyright (C) Amitay Isaacs  2015
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "system/network.h"
+#include "system/filesys.h"
+
+#include <talloc.h>
+#include <tevent.h>
+#include <tdb.h>
+
+#include "lib/util/debug.h"
+#include "ctdb_logging.h"
+
+#include "protocol/protocol.h"
+#include "protocol/protocol_api.h"
+#include "client/client_private.h"
+#include "client/client.h"
+
+int ctdb_message_recd_update_ip(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                               struct ctdb_client_context *client,
+                               int destnode, struct ctdb_public_ip *pubip)
+{
+       struct ctdb_req_message message;
+       int ret;
+
+       message.srvid = CTDB_SRVID_RECD_UPDATE_IP;
+       message.data.pubip = pubip;
+
+       ret = ctdb_client_message(mem_ctx, ev, client, destnode, &message);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Message RECD_UPDATE_IP failed to node %u\n",
+                      destnode));
+       }
+
+       return ret;
+}
+
+int ctdb_message_mem_dump(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                         struct ctdb_client_context *client,
+                         int destnode, struct ctdb_srvid_message *msg)
+{
+       struct ctdb_req_message message;
+       int ret;
+
+       message.srvid = CTDB_SRVID_MEM_DUMP;
+       message.data.msg = msg;
+
+       ret = ctdb_client_message(mem_ctx, ev, client, destnode, &message);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Message MEM_DUMP failed to node %u\n", destnode));
+       }
+
+       return ret;
+}
+
+int ctdb_message_reload_nodes(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                             struct ctdb_client_context *client,
+                             int destnode)
+{
+       struct ctdb_req_message message;
+       int ret;
+
+       message.srvid = CTDB_SRVID_RELOAD_NODES;
+
+       ret = ctdb_client_message(mem_ctx, ev, client, destnode, &message);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Message RELOAD_NODES failed to node %u\n", destnode));
+       }
+
+       return ret;
+}
+
+int ctdb_message_takeover_run(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                             struct ctdb_client_context *client,
+                             int destnode, struct ctdb_srvid_message *msg)
+{
+       struct ctdb_req_message message;
+       int ret;
+
+       message.srvid = CTDB_SRVID_TAKEOVER_RUN;
+       message.data.msg = msg;
+
+       ret = ctdb_client_message(mem_ctx, ev, client, destnode, &message);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Message TAKEOVER_RUN failed to node %u\n", destnode));
+       }
+
+       return ret;
+}
+
+int ctdb_message_rebalance_node(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                               struct ctdb_client_context *client,
+                               int destnode, uint32_t pnn)
+{
+       struct ctdb_req_message message;
+       int ret;
+
+       message.srvid = CTDB_SRVID_REBALANCE_NODE;
+       message.data.pnn = pnn;
+
+       ret = ctdb_client_message(mem_ctx, ev, client, destnode, &message);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Message REBALANCE_NODE failed to node %u\n",
+                      destnode));
+       }
+
+       return ret;
+}
+
+int ctdb_message_disable_takeover_runs(TALLOC_CTX *mem_ctx,
+                                      struct tevent_context *ev,
+                                      struct ctdb_client_context *client,
+                                      int destnode,
+                                      struct ctdb_disable_message *disable)
+{
+       struct ctdb_req_message message;
+       int ret;
+
+       message.srvid = CTDB_SRVID_DISABLE_TAKEOVER_RUNS;
+       message.data.disable = disable;
+
+       ret = ctdb_client_message(mem_ctx, ev, client, destnode, &message);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Message DISABLE_TAKEOVER_RUNS failed to node %u\n",
+                      destnode));
+       }
+
+       return ret;
+}
+
+int ctdb_message_disable_recoveries(TALLOC_CTX *mem_ctx,
+                                   struct tevent_context *ev,
+                                   struct ctdb_client_context *client,
+                                   int destnode,
+                                   struct ctdb_disable_message *disable)
+{
+       struct ctdb_req_message message;
+       int ret;
+
+       message.srvid = CTDB_SRVID_DISABLE_RECOVERIES;
+       message.data.disable = disable;
+
+       ret = ctdb_client_message(mem_ctx, ev, client, destnode, &message);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Message DISABLE_RECOVERIES failed to node %u\n",
+                      destnode));
+       }
+
+       return ret;
+}
+
+int ctdb_message_disable_ip_check(TALLOC_CTX *mem_ctx,
+                                 struct tevent_context *ev,
+                                 struct ctdb_client_context *client,
+                                 int destnode, uint32_t timeout)
+{
+       struct ctdb_req_message message;
+       int ret;
+
+       message.srvid = CTDB_SRVID_DISABLE_IP_CHECK;
+       message.data.timeout = timeout;
+
+       ret = ctdb_client_message(mem_ctx, ev, client, destnode, &message);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,
+                     ("Message DISABLE_IP_CHECK failed to node %u\n",
+                      destnode));
+       }
+
+       return ret;
+}
diff --git a/ctdb/client/client_private.h b/ctdb/client/client_private.h
new file mode 100644 (file)
index 0000000..b1d8d4b
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+   CTDB client code
+
+   Copyright (C) Amitay Isaacs  2015
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __CTDB_CLIENT_PRIVATE_H__
+#define __CTDB_CLIENT_PRIVATE_H__
+
+#include "protocol/protocol.h"
+
+struct ctdb_db_context {
+       struct ctdb_db_context *prev, *next;
+       uint32_t db_id;
+       const char *db_name;
+       const char *db_path;
+       struct tdb_wrap *ltdb;
+       bool persistent;
+};
+
+struct ctdb_client_context {
+       struct reqid_context *idr;
+       struct srvid_context *srv;
+       struct comm_context *comm;
+       int fd;
+       uint32_t pnn;
+       struct ctdb_db_context *db;
+};
+
+struct ctdb_record_handle {
+       struct tevent_context *ev;
+       struct ctdb_client_context *client;
+       struct ctdb_db_context *db;
+       struct ctdb_ltdb_header header;
+       TDB_DATA key;
+       TDB_DATA data; /* This is returned from tdb_fetch() */
+       bool readonly;
+};
+
+struct ctdb_transaction_handle {
+       struct tevent_context *ev;
+       struct ctdb_client_context *client;
+       struct ctdb_db_context *db, *db_g_lock;
+       struct ctdb_rec_buffer *recbuf;
+       struct ctdb_server_id sid;
+       const char *lock_name;
+       bool readonly;
+       bool updated;
+};
+
+/* From client_call.c */
+
+void ctdb_client_reply_call(struct ctdb_client_context *client,
+                           uint8_t *buf, size_t buflen, uint32_t reqid);
+
+/* From client_message.c */
+
+void ctdb_client_req_message(struct ctdb_client_context *client,
+                            uint8_t *buf, size_t buflen, uint32_t reqid);
+
+/* From client_control.c */
+
+void ctdb_client_reply_control(struct ctdb_client_context *client,
+                              uint8_t *buf, size_t buflen, uint32_t reqid);
+
+#endif /* __CTDB_CLIENT_PRIVATE_H__ */
diff --git a/ctdb/client/client_util.c b/ctdb/client/client_util.c
new file mode 100644 (file)
index 0000000..bb2dd6e
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+   CTDB client code
+
+   Copyright (C) Amitay Isaacs  2015
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "system/network.h"
+#include "system/filesys.h"
+
+#include <talloc.h>
+#include <tevent.h>
+#include <tdb.h>
+
+#include "lib/util/debug.h"
+#include "ctdb_logging.h"
+
+#include "protocol/protocol.h"
+#include "protocol/protocol_api.h"
+#include "client/client_private.h"
+#include "client/client.h"
+
+int list_of_nodes(struct ctdb_node_map *nodemap,
+                 uint32_t flags_mask, uint32_t exclude_pnn,
+                 TALLOC_CTX *mem_ctx, uint32_t **pnn_list)
+{
+       int num_nodes = 0;
+       uint32_t *list;
+       int i;
+
+       /* Allocate the list of same number of nodes */
+       list = talloc_array(mem_ctx, uint32_t, nodemap->num);
+       if (list == NULL) {
+               return -1;
+       }
+
+       for (i=0; i<nodemap->num; i++) {
+               if (nodemap->node[i].flags & flags_mask) {
+                       continue;
+               }
+               if (nodemap->node[i].pnn == exclude_pnn) {
+                       continue;
+               }
+               list[num_nodes] = nodemap->node[i].pnn;
+               num_nodes++;
+       }
+
+       *pnn_list = list;
+       return num_nodes;
+}
+
+int list_of_active_nodes(struct ctdb_node_map *nodemap, uint32_t exclude_pnn,
+                        TALLOC_CTX *mem_ctx, uint32_t **pnn_list)
+{
+       return list_of_nodes(nodemap, NODE_FLAGS_INACTIVE, exclude_pnn,
+                            mem_ctx, pnn_list);
+}
+
+int list_of_connected_nodes(struct ctdb_node_map *nodemap,
+                           uint32_t exclude_pnn,
+                           TALLOC_CTX *mem_ctx, uint32_t **pnn_list)
+{
+       return list_of_nodes(nodemap, NODE_FLAGS_DISCONNECTED, exclude_pnn,
+                            mem_ctx, pnn_list);
+}
+
+int ctdb_ctrl_modflags(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                      struct ctdb_client_context *client,
+                      uint32_t destnode, struct timeval timeout,
+                      uint32_t set, uint32_t clear)
+{
+       struct ctdb_node_map *nodemap;
+       struct ctdb_node_flag_change flag_change;
+       struct ctdb_req_control request;
+       uint32_t *pnn_list;
+       int ret, count;
+
+       ret = ctdb_ctrl_get_nodemap(mem_ctx, ev, client, destnode,
+                                   tevent_timeval_zero(), &nodemap);
+       if (ret != 0) {
+               return ret;
+       }
+
+       flag_change.pnn = destnode;
+       flag_change.old_flags = nodemap->node[destnode].flags;
+       flag_change.new_flags = flag_change.old_flags | set;
+       flag_change.new_flags &= ~clear;
+
+       count = list_of_connected_nodes(nodemap, -1, mem_ctx, &pnn_list);
+       if (count == -1) {
+               return ENOMEM;
+       }
+
+       ctdb_req_control_modify_flags(&request, &flag_change);
+       ret = ctdb_client_control_multi(mem_ctx, ev, client, pnn_list, count,
+                                       tevent_timeval_zero(), &request,
+                                       NULL, NULL);
+       return ret;
+}
+
+bool ctdb_server_id_equal(struct ctdb_server_id *sid1,
+                         struct ctdb_server_id *sid2)
+{
+       if (sid1->pid != sid2->pid) {
+               return false;
+       }
+       if (sid1->task_id != sid2->task_id) {
+               return false;
+       }
+       if (sid1->vnn != sid2->vnn) {
+               return false;
+       }
+       if (sid1->unique_id != sid2->unique_id) {
+               return false;
+       }
+
+       return true;
+}
+
+int ctdb_server_id_exists(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                         struct ctdb_client_context *client,
+                         struct ctdb_server_id *sid, bool *exists)
+{
+       uint8_t *result;
+       int ret;
+
+       ret = ctdb_ctrl_check_srvids(mem_ctx, ev, client, sid->vnn,
+                                    tevent_timeval_zero(),
+                                    &sid->unique_id, 1, &result);
+       if (ret != 0) {
+               return ret;
+       }
+
+       if (result[0] == 1) {
+               *exists = true;
+       } else {
+               *exists = false;
+       }
+
+       return 0;
+}
index 3d8664279ada85ed49f2f5d773531089f5346fba..fcdb7c03e838060ebc3ee369a0f2fcd555e55bd4 100755 (executable)
@@ -357,6 +357,17 @@ def build(bld):
                         deps='''replace popt talloc tevent tdb
                                 samba-util tdb-wrap ctdb-util''')
 
+    bld.SAMBA_SUBSYSTEM('ctdb-client2',
+                        source=bld.SUBDIR('client',
+                                          '''client_connect.c client_call.c
+                                             client_message.c client_control.c
+                                             client_message_sync.c
+                                             client_control_sync.c
+                                             client_db.c client_util.c
+                                          '''),
+                        includes='include include/internal',
+                        deps='replace talloc tevent tdb tdb-wrap')
+
     bld.SAMBA_SUBSYSTEM('ctdb-server',
                         source='server/ctdbd.c ' +
                                bld.SUBDIR('server',