r7228: use socket_pending() instead of the direct ioctl in the messaging code
[sfrench/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 #include "lib/events/events.h"
25 #include "system/filesys.h"
26 #include "system/time.h"
27 #include "messages.h"
28 #include "dlinklist.h"
29 #include "lib/socket/socket.h"
30
31 /* change the message version with any incompatible changes in the protocol */
32 #define MESSAGING_VERSION 1
33
34 struct messaging_context {
35         uint32_t server_id;
36         struct socket_context *sock;
37         const char *path;
38         struct dispatch_fn *dispatch;
39         struct messaging_rec *pending;
40
41         struct {
42                 struct event_context *ev;
43                 struct fd_event *fde;
44         } event;
45 };
46
47 /* we have a linked list of dispatch handlers that this messaging
48    server can deal with */
49 struct dispatch_fn {
50         struct dispatch_fn *next, *prev;
51         uint32_t msg_type;
52         void *private;
53         void (*fn)(struct messaging_context *msg, void *private, 
54                    uint32_t msg_type, uint32_t server_id, DATA_BLOB *data);
55 };
56
57 /* an individual message */
58 struct messaging_rec {
59         struct messaging_rec *next, *prev;
60         struct messaging_context *msg;
61         const char *path;
62
63         struct messaging_header {
64                 uint32_t version;
65                 uint32_t msg_type;
66                 uint32_t from;
67                 uint32_t to;
68                 uint32_t length;
69         } *header;
70
71         DATA_BLOB packet;
72 };
73
74
75 /*
76  A useful function for testing the message system.
77 */
78 static void ping_message(struct messaging_context *msg, void *private, 
79                          uint32_t msg_type, uint32_t src, DATA_BLOB *data)
80 {
81         DEBUG(1,("INFO: Received PING message from server %u [%.*s]\n",
82                  (uint_t)src, data->length, data->data?(const char *)data->data:""));
83         messaging_send(msg, src, MSG_PONG, data);
84 }
85
86 /* 
87    return the path to a messaging socket
88 */
89 static char *messaging_path(TALLOC_CTX *mem_ctx, uint32_t server_id)
90 {
91         char *name = talloc_asprintf(mem_ctx, "messaging/msg.%u", (unsigned)server_id);
92         char *ret;
93         ret = smbd_tmp_path(mem_ctx, name);
94         talloc_free(name);
95         return ret;
96 }
97
98 /*
99   dispatch a fully received message
100 */
101 static void messaging_dispatch(struct messaging_context *msg, struct messaging_rec *rec)
102 {
103         struct dispatch_fn *d, *next;
104         for (d=msg->dispatch;d;d=next) {
105                 next = d->next;
106                 if (d->msg_type == rec->header->msg_type) {
107                         DATA_BLOB data;
108                         data.data = rec->packet.data + sizeof(*rec->header);
109                         data.length = rec->header->length;
110                         d->fn(msg, d->private, d->msg_type, rec->header->from, &data);
111                 }
112         }
113         rec->header->length = 0;
114 }
115
116
117 /*
118   try to send the message
119 */
120 static NTSTATUS try_send(struct messaging_rec *rec)
121 {
122         struct messaging_context *msg = rec->msg;
123         size_t nsent;
124         void *priv;
125         NTSTATUS status;
126
127         /* we send with privileges so messages work from any context */
128         priv = root_privileges();
129         status = socket_sendto(msg->sock, &rec->packet, &nsent, 0, rec->path, 0);
130         talloc_free(priv);
131
132         return status;
133 }
134
135 /*
136   handle a socket write event
137 */
138 static void messaging_send_handler(struct messaging_context *msg)
139 {
140         while (msg->pending) {
141                 struct messaging_rec *rec = msg->pending;
142                 NTSTATUS status;
143                 status = try_send(rec);
144                 if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
145                         break;
146                 }
147                 if (!NT_STATUS_IS_OK(status)) {
148                         DEBUG(1,("messaging: Lost message from %u to %u of type %u - %s\n", 
149                                  rec->header->from, rec->header->to, rec->header->msg_type, 
150                                  nt_errstr(status)));
151                 }
152                 DLIST_REMOVE(msg->pending, rec);
153                 talloc_free(rec);
154         }
155         if (msg->pending == NULL) {
156                 EVENT_FD_NOT_WRITEABLE(msg->event.fde);
157         }
158 }
159
160 /*
161   handle a new incoming packet
162 */
163 static void messaging_recv_handler(struct messaging_context *msg)
164 {
165         struct messaging_rec *rec;
166         NTSTATUS status;
167         DATA_BLOB packet;
168         size_t msize;
169
170         /* see how many bytes are in the next packet */
171         status = socket_pending(msg->sock, &msize);
172         if (!NT_STATUS_IS_OK(status)) {
173                 DEBUG(0,("socket_pending failed in messaging - %s\n", 
174                          nt_errstr(status)));
175                 return;
176         }
177         
178         packet = data_blob_talloc(msg, NULL, msize);
179         if (packet.data == NULL) {
180                 /* assume this is temporary and retry */
181                 return;
182         }
183             
184         status = socket_recv(msg->sock, packet.data, msize, &msize, 0);
185         if (!NT_STATUS_IS_OK(status)) {
186                 data_blob_free(&packet);
187                 return;
188         }
189
190         if (msize < sizeof(*rec->header)) {
191                 DEBUG(0,("messaging: bad message of size %d\n", msize));
192                 data_blob_free(&packet);
193                 return;
194         }
195
196         rec = talloc(msg, struct messaging_rec);
197         if (rec == NULL) {
198                 smb_panic("Unable to allocate messaging_rec");
199         }
200
201         talloc_steal(rec, packet.data);
202         rec->msg           = msg;
203         rec->path          = msg->path;
204         rec->header        = (struct messaging_header *)packet.data;
205         rec->packet        = packet;
206
207         if (msize != sizeof(*rec->header) + rec->header->length) {
208                 DEBUG(0,("messaging: bad message header size %d should be %d\n", 
209                          rec->header->length, msize - sizeof(*rec->header)));
210                 talloc_free(rec);
211                 return;
212         }
213
214         messaging_dispatch(msg, rec);
215         talloc_free(rec);
216 }
217
218
219 /*
220   handle a socket event
221 */
222 static void messaging_handler(struct event_context *ev, struct fd_event *fde, 
223                               uint16_t flags, void *private)
224 {
225         struct messaging_context *msg = talloc_get_type(private, 
226                                                         struct messaging_context);
227         if (flags & EVENT_FD_WRITE) {
228                 messaging_send_handler(msg);
229         }
230         if (flags & EVENT_FD_READ) {
231                 messaging_recv_handler(msg);
232         }
233 }
234
235
236 /*
237   Register a dispatch function for a particular message type.
238 */
239 void messaging_register(struct messaging_context *msg, void *private,
240                         uint32_t msg_type, 
241                         void (*fn)(struct messaging_context *, void *, uint32_t, uint32_t, DATA_BLOB *))
242 {
243         struct dispatch_fn *d;
244
245         d = talloc(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(struct messaging_context *msg, uint32_t msg_type, void *private)
256 {
257         struct dispatch_fn *d, *next;
258
259         for (d = msg->dispatch; d; d = next) {
260                 next = d->next;
261                 if (d->msg_type == msg_type && 
262                     d->private == private) {
263                         DLIST_REMOVE(msg->dispatch, d);
264                         talloc_free(d);
265                 }
266         }       
267 }
268
269
270 /*
271   Send a message to a particular server
272 */
273 NTSTATUS messaging_send(struct messaging_context *msg, uint32_t server, 
274                         uint32_t msg_type, DATA_BLOB *data)
275 {
276         struct messaging_rec *rec;
277         NTSTATUS status;
278         size_t dlength = data?data->length:0;
279
280         rec = talloc(msg, struct messaging_rec);
281         if (rec == NULL) {
282                 return NT_STATUS_NO_MEMORY;
283         }
284
285         rec->packet = data_blob_talloc(rec, NULL, sizeof(*rec->header) + dlength);
286         if (rec->packet.data == NULL) {
287                 talloc_free(rec);
288                 return NT_STATUS_NO_MEMORY;
289         }
290
291         rec->msg              = msg;
292         rec->header           = (struct messaging_header *)rec->packet.data;
293         rec->header->version  = MESSAGING_VERSION;
294         rec->header->msg_type = msg_type;
295         rec->header->from     = msg->server_id;
296         rec->header->to       = server;
297         rec->header->length   = dlength;
298         if (dlength != 0) {
299                 memcpy(rec->packet.data + sizeof(*rec->header), 
300                        data->data, dlength);
301         }
302
303         rec->path = messaging_path(rec, server);
304
305         status = try_send(rec);
306         if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
307                 if (msg->pending == NULL) {
308                         EVENT_FD_WRITEABLE(msg->event.fde);
309                 }
310                 DLIST_ADD(msg->pending, rec);
311                 return NT_STATUS_OK;
312         }
313
314         talloc_free(rec);
315
316         return status;
317 }
318
319 /*
320   Send a message to a particular server, with the message containing a single pointer
321 */
322 NTSTATUS messaging_send_ptr(struct messaging_context *msg, uint32_t server, 
323                             uint32_t msg_type, void *ptr)
324 {
325         DATA_BLOB blob;
326
327         blob.data = (void *)&ptr;
328         blob.length = sizeof(void *);
329
330         return messaging_send(msg, server, msg_type, &blob);
331 }
332
333
334 /*
335   destroy the messaging context
336 */
337 static int messaging_destructor(void *ptr)
338 {
339         struct messaging_context *msg = ptr;
340         unlink(msg->path);
341         return 0;
342 }
343
344 /*
345   create the listening socket and setup the dispatcher
346 */
347 struct messaging_context *messaging_init(TALLOC_CTX *mem_ctx, uint32_t server_id, 
348                                          struct event_context *ev)
349 {
350         struct messaging_context *msg;
351         NTSTATUS status;
352         char *path;
353
354         msg = talloc(mem_ctx, struct messaging_context);
355         if (msg == NULL) {
356                 return NULL;
357         }
358
359         /* create the messaging directory if needed */
360         path = smbd_tmp_path(msg, "messaging");
361         mkdir(path, 0700);
362         talloc_free(path);
363
364         msg->path = messaging_path(msg, server_id);
365         msg->server_id = server_id;
366         msg->dispatch = NULL;
367         msg->pending = NULL;
368
369         status = socket_create("unix", SOCKET_TYPE_DGRAM, &msg->sock, 0);
370         if (!NT_STATUS_IS_OK(status)) {
371                 talloc_free(msg);
372                 return NULL;
373         }
374
375         /* by stealing here we ensure that the socket is cleaned up (and even 
376            deleted) on exit */
377         talloc_steal(msg, msg->sock);
378
379         status = socket_listen(msg->sock, msg->path, 0, 50, 0);
380         if (!NT_STATUS_IS_OK(status)) {
381                 DEBUG(0,("Unable to setup messaging listener for '%s'\n", msg->path));
382                 talloc_free(msg);
383                 return NULL;
384         }
385
386         /* it needs to be non blocking for sends */
387         set_blocking(socket_get_fd(msg->sock), False);
388
389         msg->event.ev   = talloc_reference(msg, ev);
390         msg->event.fde  = event_add_fd(ev, msg, socket_get_fd(msg->sock), 
391                                        EVENT_FD_READ, messaging_handler, msg);
392
393         talloc_set_destructor(msg, messaging_destructor);
394         
395         messaging_register(msg, NULL, MSG_PING, ping_message);
396
397         return msg;
398 }