r3023: added immediate send of messages when they are first queued. This makes things...
[bbaumbach/samba-autobuild/.git] / source4 / lib / messaging / messaging.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Samba internal messaging functions
5
6    Copyright (C) Andrew Tridgell 2004
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24
25 /* change the message version with any incompatible changes in the protocol */
26 #define MESSAGING_VERSION 1
27
28 struct messaging_state {
29         servid_t server_id;
30         struct socket_context *sock;
31         char *path;
32         struct dispatch_fn *dispatch;
33
34         struct {
35                 struct event_context *ev;
36                 struct fd_event *fde;
37         } event;
38 };
39
40 /* we have a linked list of dispatch handlers that this messaging
41    server can deal with */
42 struct dispatch_fn {
43         struct dispatch_fn *next, *prev;
44         uint32_t msg_type;
45         void *private;
46         void (*fn)(void *msg_ctx, void *private, 
47                    uint32_t msg_type, servid_t server_id, DATA_BLOB *data);
48 };
49
50 /* an individual message */
51 struct messaging_rec {
52         struct messaging_rec *next, *prev;
53
54         struct messaging_state *msg;
55         struct socket_context *sock;
56         struct fd_event *fde;
57         const char *path;
58
59         struct {
60                 uint32_t version;
61                 uint32_t msg_type;
62                 servid_t from;
63                 servid_t to;
64                 uint32_t length;
65         } header;
66
67         DATA_BLOB data;
68
69         uint32_t ndone;
70 };
71
72 /*
73  A useful function for testing the message system.
74 */
75 static void ping_message(void *msg_ctx, void *private, 
76                          uint32_t msg_type, servid_t src, DATA_BLOB *data)
77 {
78         DEBUG(1,("INFO: Received PING message from server %u [%.*s]\n",
79                  (uint_t)src, data->length, data->data?(const char *)data->data:""));
80         messaging_send(msg_ctx, src, MSG_PONG, data);
81 }
82
83 /* 
84    return the path to a messaging socket
85 */
86 static char *messaging_path(TALLOC_CTX *mem_ctx, servid_t server_id)
87 {
88         char *name = talloc_asprintf(mem_ctx, "messaging/msg.%u", (unsigned)server_id);
89         char *ret;
90         ret = lock_path(mem_ctx, name);
91         talloc_free(name);
92         return ret;
93 }
94
95 /*
96   dispatch a fully received message
97 */
98 static void messaging_dispatch(struct messaging_state *msg, struct messaging_rec *rec)
99 {
100         struct dispatch_fn *d;
101         for (d=msg->dispatch;d;d=d->next) {
102                 if (d->msg_type == rec->header.msg_type) {
103                         d->fn(msg, d->private, d->msg_type, rec->header.from, &rec->data);
104                 }
105         }
106
107         /* we don't free the record itself here as there may
108            be more messages from this client */
109         data_blob_free(&rec->data);
110         rec->header.length = 0;
111         rec->ndone = 0;
112 }
113
114
115 /*
116   handle IO for a single message
117 */
118 static void messaging_recv_handler(struct event_context *ev, struct fd_event *fde, 
119                                  time_t t, uint16_t flags)
120 {
121         struct messaging_rec *rec = fde->private;
122         struct messaging_state *msg = rec->msg;
123         NTSTATUS status;
124
125         if (rec->ndone < sizeof(rec->header)) {
126                 /* receive the header */
127                 DATA_BLOB blob;
128                 blob.length = 0;
129                 status = socket_recv(rec->sock, rec, 
130                                      &blob, sizeof(rec->header) - rec->ndone, 0);
131                 if (NT_STATUS_IS_ERR(status)) {
132                         talloc_free(rec);
133                         return;
134                 }
135
136                 if (blob.length == 0) {
137                         return;
138                 }
139
140                 memcpy(rec->ndone + (char *)&rec->header, blob.data, blob.length);
141                 rec->ndone += blob.length;
142                 data_blob_free(&blob);
143
144                 if (rec->ndone == sizeof(rec->header)) {
145                         if (rec->header.version != MESSAGING_VERSION) {
146                                 DEBUG(0,("meessage with wrong version %u\n",
147                                          rec->header.version));
148                                 talloc_free(rec);
149                         }
150                         rec->data = data_blob_talloc(rec, NULL, rec->header.length);
151                         if (rec->data.length != rec->header.length) {
152                                 DEBUG(0,("Unable to allocate message of size %u\n",
153                                          rec->header.length));
154                                 talloc_free(rec);
155                         }
156                 }
157         }
158
159         if (rec->ndone >= sizeof(rec->header) && 
160             rec->ndone < sizeof(rec->header) + rec->header.length) {
161                 /* receive the body, if any */
162                 DATA_BLOB blob;
163                 blob.length = 0;
164                 status = socket_recv(rec->sock, rec, 
165                                      &blob, sizeof(rec->header) + rec->header.length - rec->ndone, 0);
166                 if (NT_STATUS_IS_ERR(status)) {
167                         talloc_free(rec);
168                         return;
169                 }
170
171                 if (blob.length == 0) {
172                         return;
173                 }
174
175                 memcpy(rec->data.data + (rec->ndone - sizeof(rec->header)), 
176                        blob.data, blob.length);
177
178                 rec->ndone += blob.length;
179         }
180
181         if (rec->ndone == sizeof(rec->header) + rec->header.length) {
182                 /* we've got the whole message */
183                 messaging_dispatch(msg, rec);
184         }
185 }
186
187 /*
188   destroy a messaging record
189 */
190 static int rec_destructor(void *ptr)
191 {
192         struct messaging_rec *rec = ptr;
193         struct messaging_state *msg = rec->msg;
194         event_remove_fd(msg->event.ev, rec->fde);
195         return 0;
196 }
197
198 /*
199   handle a new incoming connection
200 */
201 static void messaging_listen_handler(struct event_context *ev, struct fd_event *fde, 
202                                      time_t t, uint16_t flags)
203 {
204         struct messaging_state *msg = fde->private;
205         struct messaging_rec *rec;
206         NTSTATUS status;
207         struct fd_event fde2;
208
209         rec = talloc_p(msg, struct messaging_rec);
210         if (rec == NULL) {
211                 smb_panic("Unable to allocate messaging_rec");
212         }
213
214         status = socket_accept(msg->sock, &rec->sock, 0);
215         if (!NT_STATUS_IS_OK(status)) {
216                 smb_panic("Unable to accept messaging_rec");
217         }
218         talloc_steal(rec, rec->sock);
219
220         rec->msg = msg;
221         rec->ndone = 0;
222         rec->header.length = 0;
223         rec->path = msg->path;
224
225         fde2.private    = rec;
226         fde2.fd         = socket_get_fd(rec->sock);
227         fde2.flags      = EVENT_FD_READ;
228         fde2.handler    = messaging_recv_handler;
229
230         rec->fde        = event_add_fd(msg->event.ev, &fde2);
231
232         talloc_set_destructor(rec, rec_destructor);
233 }
234
235 /*
236   Register a dispatch function for a particular message type.
237 */
238 void messaging_register(void *ctx, void *private,
239                         uint32_t msg_type, 
240                         void (*fn)(void *, void *, uint32_t, servid_t, DATA_BLOB *))
241 {
242         struct messaging_state *msg = ctx;
243         struct dispatch_fn *d;
244
245         d = talloc_p(msg, struct dispatch_fn);
246         d->msg_type = msg_type;
247         d->private = private;
248         d->fn = fn;
249         DLIST_ADD(msg->dispatch, d);
250 }
251
252 /*
253   De-register the function for a particular message type.
254 */
255 void messaging_deregister(void *ctx, uint32_t msg_type)
256 {
257         struct messaging_state *msg = ctx;
258         struct dispatch_fn *d, *next;
259
260         for (d = msg->dispatch; d; d = next) {
261                 next = d->next;
262                 if (d->msg_type == msg_type) {
263                         DLIST_REMOVE(msg->dispatch, d);
264                         talloc_free(d);
265                 }
266         }       
267 }
268
269
270
271 /*
272   handle IO for sending a message
273 */
274 static void messaging_send_handler(struct event_context *ev, struct fd_event *fde, 
275                                    time_t t, uint16_t flags)
276 {
277         struct messaging_rec *rec = fde->private;
278         NTSTATUS status;
279
280         if (rec->ndone < sizeof(rec->header)) {
281                 /* send the header */
282                 size_t nsent;
283                 DATA_BLOB blob;
284
285                 blob.data = rec->ndone + (char *)&rec->header;
286                 blob.length = sizeof(rec->header) - rec->ndone;
287
288                 status = socket_send(rec->sock, rec, &blob, &nsent, 0);
289                 if (NT_STATUS_IS_ERR(status)) {
290                         talloc_free(rec);
291                         return;
292                 }
293
294                 if (nsent == 0) {
295                         return;
296                 }
297
298                 rec->ndone += nsent;
299         }
300
301         if (rec->ndone >= sizeof(rec->header) && 
302             rec->ndone < sizeof(rec->header) + rec->header.length) {
303                 /* send the body, if any */
304                 DATA_BLOB blob;
305                 size_t nsent;
306
307                 blob.data = rec->data.data + (rec->ndone - sizeof(rec->header));
308                 blob.length = rec->header.length - (rec->ndone - sizeof(rec->header));
309
310                 status = socket_send(rec->sock, rec, &blob, &nsent, 0);
311                 if (NT_STATUS_IS_ERR(status)) {
312                         talloc_free(rec);
313                         return;
314                 }
315
316                 rec->ndone += nsent;
317         }
318
319         if (rec->ndone == sizeof(rec->header) + rec->header.length) {
320                 /* we've done the whole message */
321                 talloc_free(rec);
322         }
323 }
324
325
326 /*
327   Send a message to a particular server
328 */
329 NTSTATUS messaging_send(void *msg_ctx, servid_t server, uint32_t msg_type, DATA_BLOB *data)
330 {
331         struct messaging_state *msg = msg_ctx;
332         struct messaging_rec *rec;
333         NTSTATUS status;
334         struct fd_event fde;
335
336         rec = talloc_p(msg, struct messaging_rec);
337         if (rec == NULL) {
338                 return NT_STATUS_NO_MEMORY;
339         }
340
341         rec->msg = msg;
342         rec->header.version = MESSAGING_VERSION;
343         rec->header.msg_type = msg_type;
344         rec->header.from = msg->server_id;
345         rec->header.to = server;
346         rec->header.length = data?data->length:0;
347         if (rec->header.length != 0) {
348                 rec->data = data_blob_talloc(rec, data->data, data->length);
349         } else {
350                 rec->data = data_blob(NULL, 0);
351         }
352         rec->ndone = 0;
353
354         status = socket_create("unix", SOCKET_TYPE_STREAM, &rec->sock, 0);
355         if (!NT_STATUS_IS_OK(status)) {
356                 talloc_free(rec);
357                 return status;
358         }
359         talloc_steal(rec, rec->sock);
360
361         rec->path = messaging_path(rec, server);
362
363         status = socket_connect(rec->sock, NULL, 0, rec->path, 0, 0);
364         if (!NT_STATUS_IS_OK(status)) {
365                 talloc_free(rec);
366                 return status;
367         }
368
369         fde.private     = rec;
370         fde.fd          = socket_get_fd(rec->sock);
371         fde.flags       = EVENT_FD_WRITE;
372         fde.handler     = messaging_send_handler;
373
374         rec->fde        = event_add_fd(msg->event.ev, &fde);
375
376         talloc_set_destructor(rec, rec_destructor);
377
378         messaging_send_handler(msg->event.ev, rec->fde, 0, EVENT_FD_WRITE);
379
380         return NT_STATUS_OK;
381 }
382
383
384 /*
385   destroy the messaging context
386 */
387 static int messaging_destructor(void *msg_ctx)
388 {
389         struct messaging_state *msg = msg_ctx;
390         event_remove_fd(msg->event.ev, msg->event.fde);
391         return 0;
392 }
393
394 /*
395   create the listening socket and setup the dispatcher
396 */
397 void *messaging_init(TALLOC_CTX *mem_ctx, servid_t server_id, struct event_context *ev)
398 {
399         struct messaging_state *msg;
400         NTSTATUS status;
401         struct fd_event fde;
402
403         msg = talloc_p(mem_ctx, struct messaging_state);
404         if (msg == NULL) {
405                 return NULL;
406         }
407
408         /* create the messaging directory if needed */
409         msg->path = lock_path(msg, "messaging");
410         mkdir(msg->path, 0700);
411         talloc_free(msg->path);
412
413         msg->server_id = server_id;
414         msg->dispatch = NULL;
415         msg->path = messaging_path(msg, server_id);
416
417         status = socket_create("unix", SOCKET_TYPE_STREAM, &msg->sock, 0);
418         if (!NT_STATUS_IS_OK(status)) {
419                 talloc_free(msg);
420                 return NULL;
421         }
422
423         /* by stealing here we ensure that the socket is cleaned up (and even 
424            deleted) on exit */
425         talloc_steal(msg, msg->sock);
426
427         status = socket_listen(msg->sock, msg->path, 0, 50, 0);
428         if (!NT_STATUS_IS_OK(status)) {
429                 DEBUG(0,("Unable to setup messaging listener for '%s'\n", msg->path));
430                 talloc_free(msg);
431                 return NULL;
432         }
433
434         fde.private     = msg;
435         fde.fd          = socket_get_fd(msg->sock);
436         fde.flags       = EVENT_FD_READ;
437         fde.handler     = messaging_listen_handler;
438
439         msg->event.ev   = ev;
440         msg->event.fde  = event_add_fd(ev, &fde);
441
442         talloc_set_destructor(msg, messaging_destructor);
443         
444         messaging_register(msg, NULL, MSG_PING, ping_message);
445
446         return msg;
447 }
448
449