TMP: add a ctdb snapshot of current ctdb master (git://git.samba.org/ctdb.git) to...
[obnox/samba/samba-obnox.git] / ctdb / libctdb / messages.c
1 /*
2    core of libctdb
3
4    Copyright (C) Rusty Russell 2010
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "libctdb_private.h"
21 #include "messages.h"
22 #include "io_elem.h"
23 #include <ctdb.h>
24 #include <tdb.h>
25 #include <ctdb_protocol.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <errno.h>
29
30 /* Remove type-safety macros. */
31 #undef ctdb_set_message_handler_send
32 #undef ctdb_set_message_handler_recv
33 #undef ctdb_remove_message_handler_send
34
35 struct message_handler_info {
36         struct message_handler_info *next, *prev;
37
38         uint64_t srvid;
39         ctdb_message_fn_t handler;
40         void *handler_data;
41 };
42
43 void deliver_message(struct ctdb_connection *ctdb, struct ctdb_req_header *hdr)
44 {
45         struct message_handler_info *i;
46         struct ctdb_req_message *msg = (struct ctdb_req_message *)hdr;
47         TDB_DATA data;
48         bool found;
49
50         data.dptr = msg->data;
51         data.dsize = msg->datalen;
52
53         /* Note: we want to call *every* handler: there may be more than one */
54         for (i = ctdb->message_handlers; i; i = i->next) {
55                 if (i->srvid == msg->srvid) {
56                         i->handler(ctdb, msg->srvid, data, i->handler_data);
57                         found = true;
58                 }
59         }
60         if (!found) {
61                 DEBUG(ctdb, LOG_WARNING,
62                       "ctdb_service: messsage for unregistered srvid %llu",
63                       (unsigned long long)msg->srvid);
64         }
65 }
66
67 void remove_message_handlers(struct ctdb_connection *ctdb)
68 {
69         struct message_handler_info *i;
70
71         /* ctdbd should unregister automatically when we close fd, so we don't
72            need to do that here. */
73         while ((i = ctdb->message_handlers) != NULL) {
74                 DLIST_REMOVE(ctdb->message_handlers, i);
75                 free(i);
76         }
77 }
78
79 static void free_info(struct ctdb_connection *ctdb, struct ctdb_request *req);
80
81 struct ctdb_request *
82 ctdb_set_message_handler_send(struct ctdb_connection *ctdb, uint64_t srvid,
83                               ctdb_message_fn_t handler, void *handler_data,
84                               ctdb_callback_t callback, void *private_data)
85 {
86         struct message_handler_info *info;
87         struct ctdb_request *req;
88
89         info = malloc(sizeof(*info));
90         if (!info) {
91                 DEBUG(ctdb, LOG_ERR,
92                       "ctdb_set_message_handler_send: allocating info");
93                 return NULL;
94         }
95
96         req = new_ctdb_control_request(ctdb, CTDB_CONTROL_REGISTER_SRVID,
97                                        CTDB_CURRENT_NODE, NULL, 0,
98                                        callback, private_data);
99         if (!req) {
100                 DEBUG(ctdb, LOG_ERR,
101                       "ctdb_set_message_handler_send: allocating request");
102                 free(info);
103                 return NULL;
104         }
105         req->extra = info;
106         req->extra_destructor = free_info;
107         req->hdr.control->srvid = srvid;
108
109         info->srvid = srvid;
110         info->handler = handler;
111         info->handler_data = handler_data;
112
113         DEBUG(ctdb, LOG_DEBUG,
114               "ctdb_set_message_handler_send: sending request %u for id %llx",
115               req->hdr.hdr->reqid, (unsigned long long)srvid);
116         return req;
117 }
118
119 static void free_info(struct ctdb_connection *ctdb, struct ctdb_request *req)
120 {
121         free(req->extra);
122 }
123
124 bool ctdb_set_message_handler_recv(struct ctdb_connection *ctdb,
125                                    struct ctdb_request *req)
126 {
127         struct message_handler_info *info = req->extra;
128         struct ctdb_reply_control *reply;
129
130         reply = unpack_reply_control(req, CTDB_CONTROL_REGISTER_SRVID);
131         if (!reply) {
132                 return false;
133         }
134         if (reply->status != 0) {
135                 DEBUG(ctdb, LOG_ERR,
136                       "ctdb_set_message_handler_recv: status %i",
137                       reply->status);
138                 return false;
139         }
140
141         /* Put ourselves in list of handlers. */
142         DLIST_ADD(ctdb->message_handlers, info);
143         /* Keep safe from destructor */
144         req->extra = NULL;
145         return true;
146 }
147
148 struct ctdb_request *
149 ctdb_remove_message_handler_send(struct ctdb_connection *ctdb, uint64_t srvid,
150                                  ctdb_message_fn_t handler, void *hdata,
151                                  ctdb_callback_t callback, void *cbdata)
152 {
153         struct message_handler_info *i;
154         struct ctdb_request *req;
155
156         for (i = ctdb->message_handlers; i; i = i->next) {
157                 if (i->srvid == srvid
158                     && i->handler == handler && i->handler_data == hdata) {
159                         break;
160                 }
161         }
162         if (!i) {
163                 DEBUG(ctdb, LOG_ALERT,
164                       "ctdb_remove_message_handler_send: no such handler");
165                 errno = ENOENT;
166                 return NULL;
167         }
168
169         req = new_ctdb_control_request(ctdb, CTDB_CONTROL_DEREGISTER_SRVID,
170                                        CTDB_CURRENT_NODE, NULL, 0,
171                                        callback, cbdata);
172         if (!req) {
173                 DEBUG(ctdb, LOG_ERR,
174                       "ctdb_remove_message_handler_send: allocating request");
175                 return NULL;
176         }
177         req->hdr.control->srvid = srvid;
178         req->extra = i;
179
180         DEBUG(ctdb, LOG_DEBUG,
181               "ctdb_set_remove_handler_send: sending request %u for id %llu",
182               req->hdr.hdr->reqid, (unsigned long long)srvid);
183         return req;
184 }
185
186 bool ctdb_remove_message_handler_recv(struct ctdb_connection *ctdb,
187                                       struct ctdb_request *req)
188 {
189         struct message_handler_info *handler = req->extra;
190         struct ctdb_reply_control *reply;
191
192         reply = unpack_reply_control(req, CTDB_CONTROL_DEREGISTER_SRVID);
193         if (!reply) {
194                 return false;
195         }
196         if (reply->status != 0) {
197                 DEBUG(ctdb, LOG_ERR,
198                       "ctdb_remove_message_handler_recv: status %i",
199                       reply->status);
200                 return false;
201         }
202
203         /* Remove ourselves from list of handlers. */
204         DLIST_REMOVE(ctdb->message_handlers, handler);
205         free(handler);
206         /* Crash if they call this again! */
207         req->extra = NULL;
208         return true;
209 }
210
211 bool ctdb_send_message(struct ctdb_connection *ctdb,
212                       uint32_t pnn, uint64_t srvid,
213                       TDB_DATA data)
214 {
215         struct ctdb_request *req;
216         struct ctdb_req_message *pkt;
217
218         /* We just discard it once it's finished: no reply. */
219         req = new_ctdb_request(
220                 ctdb, offsetof(struct ctdb_req_message, data) + data.dsize,
221                 ctdb_cancel_callback, NULL);
222         if (!req) {
223                 DEBUG(ctdb, LOG_ERR, "ctdb_set_message: allocating message");
224                 return false;
225         }
226
227         io_elem_init_req_header(req->io,
228                                 CTDB_REQ_MESSAGE, pnn, new_reqid(ctdb));
229
230         pkt = req->hdr.message;
231         pkt->srvid = srvid;
232         pkt->datalen = data.dsize;
233         memcpy(pkt->data, data.dptr, data.dsize);
234         DLIST_ADD_END(ctdb->outq, req, struct ctdb_request);
235         return true;
236 }