afd18b4f2fa5596a49cb8908c9e6c369f084a455
[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_context {
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)(struct messaging_context *msg, 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_context *msg;
53         struct socket_context *sock;
54         struct fd_event *fde;
55         const char *path;
56
57         struct {
58                 uint32_t version;
59                 uint32_t msg_type;
60                 servid_t from;
61                 servid_t to;
62                 uint32_t length;
63         } header;
64
65         DATA_BLOB data;
66
67         uint32_t ndone;
68 };
69
70 /*
71  A useful function for testing the message system.
72 */
73 static void ping_message(struct messaging_context *msg, void *private, 
74                          uint32_t msg_type, servid_t src, DATA_BLOB *data)
75 {
76         DEBUG(1,("INFO: Received PING message from server %u [%.*s]\n",
77                  (uint_t)src, data->length, data->data?(const char *)data->data:""));
78         messaging_send(msg, src, MSG_PONG, data);
79 }
80
81 /* 
82    return the path to a messaging socket
83 */
84 static char *messaging_path(TALLOC_CTX *mem_ctx, servid_t server_id)
85 {
86         char *name = talloc_asprintf(mem_ctx, "messaging/msg.%u", (unsigned)server_id);
87         char *ret;
88         ret = lock_path(mem_ctx, name);
89         talloc_free(name);
90         return ret;
91 }
92
93 /*
94   dispatch a fully received message
95 */
96 static void messaging_dispatch(struct messaging_context *msg, struct messaging_rec *rec)
97 {
98         struct dispatch_fn *d, *next;
99         for (d=msg->dispatch;d;d=next) {
100                 next = d->next;
101                 if (d->msg_type == rec->header.msg_type) {
102                         d->fn(msg, d->private, d->msg_type, rec->header.from, &rec->data);
103                 }
104         }
105
106         /* we don't free the record itself here as there may
107            be more messages from this client */
108         data_blob_free(&rec->data);
109         rec->header.length = 0;
110         rec->ndone = 0;
111 }
112
113
114 /*
115   handle IO for a single message
116 */
117 static void messaging_recv_handler(struct event_context *ev, struct fd_event *fde, 
118                                  time_t t, uint16_t flags)
119 {
120         struct messaging_rec *rec = fde->private;
121         struct messaging_context *msg = rec->msg;
122         NTSTATUS status;
123
124         if (rec->ndone < sizeof(rec->header)) {
125                 /* receive the header */
126                 DATA_BLOB blob;
127                 blob.length = 0;
128                 status = socket_recv(rec->sock, rec, 
129                                      &blob, sizeof(rec->header) - rec->ndone, 0);
130                 if (NT_STATUS_IS_ERR(status)) {
131                         talloc_free(rec);
132                         return;
133                 }
134
135                 if (blob.length == 0) {
136                         return;
137                 }
138
139                 memcpy(rec->ndone + (char *)&rec->header, blob.data, blob.length);
140                 rec->ndone += blob.length;
141                 data_blob_free(&blob);
142
143                 if (rec->ndone == sizeof(rec->header)) {
144                         if (rec->header.version != MESSAGING_VERSION) {
145                                 DEBUG(0,("meessage with wrong version %u\n",
146                                          rec->header.version));
147                                 talloc_free(rec);
148                         }
149                         rec->data = data_blob_talloc(rec, NULL, rec->header.length);
150                         if (rec->data.length != rec->header.length) {
151                                 DEBUG(0,("Unable to allocate message of size %u\n",
152                                          rec->header.length));
153                                 talloc_free(rec);
154                         }
155                 }
156         }
157
158         if (rec->ndone >= sizeof(rec->header) && 
159             rec->ndone < sizeof(rec->header) + rec->header.length) {
160                 /* receive the body, if any */
161                 DATA_BLOB blob;
162                 blob.length = 0;
163                 status = socket_recv(rec->sock, rec, 
164                                      &blob, sizeof(rec->header) + rec->header.length - rec->ndone, 0);
165                 if (NT_STATUS_IS_ERR(status)) {
166                         talloc_free(rec);
167                         return;
168                 }
169
170                 if (blob.length == 0) {
171                         return;
172                 }
173
174                 memcpy(rec->data.data + (rec->ndone - sizeof(rec->header)), 
175                        blob.data, blob.length);
176
177                 rec->ndone += blob.length;
178         }
179
180         if (rec->ndone == sizeof(rec->header) + rec->header.length) {
181                 /* we've got the whole message */
182                 messaging_dispatch(msg, rec);
183         }
184 }
185
186 /*
187   destroy a messaging record
188 */
189 static int rec_destructor(void *ptr)
190 {
191         struct messaging_rec *rec = ptr;
192         struct messaging_context *msg = rec->msg;
193         event_remove_fd(msg->event.ev, rec->fde);
194         return 0;
195 }
196
197 /*
198   handle a new incoming connection
199 */
200 static void messaging_listen_handler(struct event_context *ev, struct fd_event *fde, 
201                                      time_t t, uint16_t flags)
202 {
203         struct messaging_context *msg = fde->private;
204         struct messaging_rec *rec;
205         NTSTATUS status;
206         struct fd_event fde2;
207
208         rec = talloc_p(msg, struct messaging_rec);
209         if (rec == NULL) {
210                 smb_panic("Unable to allocate messaging_rec");
211         }
212
213         status = socket_accept(msg->sock, &rec->sock, 0);
214         if (!NT_STATUS_IS_OK(status)) {
215                 smb_panic("Unable to accept messaging_rec");
216         }
217         talloc_steal(rec, rec->sock);
218
219         rec->msg = msg;
220         rec->ndone = 0;
221         rec->header.length = 0;
222         rec->path = msg->path;
223
224         fde2.private    = rec;
225         fde2.fd         = socket_get_fd(rec->sock);
226         fde2.flags      = EVENT_FD_READ;
227         fde2.handler    = messaging_recv_handler;
228
229         rec->fde        = event_add_fd(msg->event.ev, &fde2);
230
231         talloc_set_destructor(rec, rec_destructor);
232 }
233
234 /*
235   Register a dispatch function for a particular message type.
236 */
237 void messaging_register(struct messaging_context *msg, void *private,
238                         uint32_t msg_type, 
239                         void (*fn)(struct messaging_context *, void *, uint32_t, servid_t, DATA_BLOB *))
240 {
241         struct dispatch_fn *d;
242
243         d = talloc_p(msg, struct dispatch_fn);
244         d->msg_type = msg_type;
245         d->private = private;
246         d->fn = fn;
247         DLIST_ADD(msg->dispatch, d);
248 }
249
250 /*
251   De-register the function for a particular message type.
252 */
253 void messaging_deregister(struct messaging_context *msg, uint32_t msg_type, void *private)
254 {
255         struct dispatch_fn *d, *next;
256
257         for (d = msg->dispatch; d; d = next) {
258                 next = d->next;
259                 if (d->msg_type == msg_type && 
260                     d->private == private) {
261                         DLIST_REMOVE(msg->dispatch, d);
262                         talloc_free(d);
263                 }
264         }       
265 }
266
267
268
269 /*
270   handle IO for sending a message
271 */
272 static void messaging_send_handler(struct event_context *ev, struct fd_event *fde, 
273                                    time_t t, uint16_t flags)
274 {
275         struct messaging_rec *rec = fde->private;
276         NTSTATUS status;
277
278         if (rec->ndone < sizeof(rec->header)) {
279                 /* send the header */
280                 size_t nsent;
281                 DATA_BLOB blob;
282
283                 blob.data = rec->ndone + (char *)&rec->header;
284                 blob.length = sizeof(rec->header) - rec->ndone;
285
286                 status = socket_send(rec->sock, rec, &blob, &nsent, 0);
287                 if (NT_STATUS_IS_ERR(status)) {
288                         talloc_free(rec);
289                         return;
290                 }
291
292                 if (nsent == 0) {
293                         return;
294                 }
295
296                 rec->ndone += nsent;
297         }
298
299         if (rec->ndone >= sizeof(rec->header) && 
300             rec->ndone < sizeof(rec->header) + rec->header.length) {
301                 /* send the body, if any */
302                 DATA_BLOB blob;
303                 size_t nsent;
304
305                 blob.data = rec->data.data + (rec->ndone - sizeof(rec->header));
306                 blob.length = rec->header.length - (rec->ndone - sizeof(rec->header));
307
308                 status = socket_send(rec->sock, rec, &blob, &nsent, 0);
309                 if (NT_STATUS_IS_ERR(status)) {
310                         talloc_free(rec);
311                         return;
312                 }
313
314                 rec->ndone += nsent;
315         }
316
317         if (rec->ndone == sizeof(rec->header) + rec->header.length) {
318                 /* we've done the whole message */
319                 talloc_free(rec);
320         }
321 }
322
323
324 /*
325   when the servers listen queue is full we use this to backoff the message
326 */
327 static void messaging_backoff_handler(struct event_context *ev, struct timed_event *te, time_t t)
328 {
329         struct messaging_rec *rec = te->private;
330         struct messaging_context *msg = rec->msg;
331         NTSTATUS status;
332         struct fd_event fde;
333
334         status = socket_connect(rec->sock, NULL, 0, rec->path, 0, 0);
335         if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
336                 /* backoff again */
337                 te->next_event = t+1;
338                 return;
339         }
340
341         if (!NT_STATUS_IS_OK(status)) {
342                 DEBUG(1,("messaging: Lost message from %u to %u of type %u after backoff - %s\n", 
343                          rec->header.from, rec->header.to, rec->header.msg_type, nt_errstr(status)));
344                 talloc_free(rec);
345                 return;
346         }
347
348         fde.private     = rec;
349         fde.fd          = socket_get_fd(rec->sock);
350         fde.flags       = EVENT_FD_WRITE;
351         fde.handler     = messaging_send_handler;
352
353         rec->fde        = event_add_fd(msg->event.ev, &fde);
354
355         talloc_set_destructor(rec, rec_destructor);
356
357         messaging_send_handler(msg->event.ev, rec->fde, 0, EVENT_FD_WRITE);
358 }
359
360
361 /*
362   Send a message to a particular server
363 */
364 NTSTATUS messaging_send(struct messaging_context *msg, servid_t server, uint32_t msg_type, DATA_BLOB *data)
365 {
366         struct messaging_rec *rec;
367         NTSTATUS status;
368         struct fd_event fde;
369
370         rec = talloc_p(msg, struct messaging_rec);
371         if (rec == NULL) {
372                 return NT_STATUS_NO_MEMORY;
373         }
374
375         rec->msg = msg;
376         rec->header.version = MESSAGING_VERSION;
377         rec->header.msg_type = msg_type;
378         rec->header.from = msg->server_id;
379         rec->header.to = server;
380         rec->header.length = data?data->length:0;
381         if (rec->header.length != 0) {
382                 rec->data = data_blob_talloc(rec, data->data, data->length);
383         } else {
384                 rec->data = data_blob(NULL, 0);
385         }
386         rec->ndone = 0;
387
388         status = socket_create("unix", SOCKET_TYPE_STREAM, &rec->sock, 0);
389         if (!NT_STATUS_IS_OK(status)) {
390                 talloc_free(rec);
391                 return status;
392         }
393         talloc_steal(rec, rec->sock);
394
395         rec->path = messaging_path(rec, server);
396
397         status = socket_connect(rec->sock, NULL, 0, rec->path, 0, 0);
398         if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
399                 /* backoff on this message - the servers listen queue is full */
400                 struct timed_event te;
401                 te.next_event = time(NULL)+1;
402                 te.handler = messaging_backoff_handler;
403                 te.private = rec;
404                 event_add_timed(msg->event.ev, &te);
405                 return NT_STATUS_OK;
406         }
407
408         if (!NT_STATUS_IS_OK(status)) {
409                 talloc_free(rec);
410                 return status;
411         }
412
413         fde.private     = rec;
414         fde.fd          = socket_get_fd(rec->sock);
415         fde.flags       = EVENT_FD_WRITE;
416         fde.handler     = messaging_send_handler;
417
418         rec->fde        = event_add_fd(msg->event.ev, &fde);
419
420         talloc_set_destructor(rec, rec_destructor);
421
422         messaging_send_handler(msg->event.ev, rec->fde, 0, EVENT_FD_WRITE);
423
424         return NT_STATUS_OK;
425 }
426
427
428 /*
429   destroy the messaging context
430 */
431 static int messaging_destructor(void *ptr)
432 {
433         struct messaging_context *msg = ptr;
434         event_remove_fd(msg->event.ev, msg->event.fde);
435         unlink(msg->path);
436         return 0;
437 }
438
439 /*
440   create the listening socket and setup the dispatcher
441 */
442 struct messaging_context *messaging_init(TALLOC_CTX *mem_ctx, servid_t server_id, struct event_context *ev)
443 {
444         struct messaging_context *msg;
445         NTSTATUS status;
446         struct fd_event fde;
447
448         msg = talloc_p(mem_ctx, struct messaging_context);
449         if (msg == NULL) {
450                 return NULL;
451         }
452
453         /* create the messaging directory if needed */
454         msg->path = lock_path(msg, "messaging");
455         mkdir(msg->path, 0700);
456         talloc_free(msg->path);
457
458         msg->server_id = server_id;
459         msg->dispatch = NULL;
460         msg->path = messaging_path(msg, server_id);
461
462         status = socket_create("unix", SOCKET_TYPE_STREAM, &msg->sock, 0);
463         if (!NT_STATUS_IS_OK(status)) {
464                 talloc_free(msg);
465                 return NULL;
466         }
467
468         /* by stealing here we ensure that the socket is cleaned up (and even 
469            deleted) on exit */
470         talloc_steal(msg, msg->sock);
471
472         status = socket_listen(msg->sock, msg->path, 0, 50, 0);
473         if (!NT_STATUS_IS_OK(status)) {
474                 DEBUG(0,("Unable to setup messaging listener for '%s'\n", msg->path));
475                 talloc_free(msg);
476                 return NULL;
477         }
478
479         fde.private     = msg;
480         fde.fd          = socket_get_fd(msg->sock);
481         fde.flags       = EVENT_FD_READ;
482         fde.handler     = messaging_listen_handler;
483
484         msg->event.ev   = ev;
485         msg->event.fde  = event_add_fd(ev, &fde);
486
487         talloc_set_destructor(msg, messaging_destructor);
488         
489         messaging_register(msg, NULL, MSG_PING, ping_message);
490
491         return msg;
492 }
493
494