2409e5b1e886cb4ce773be83d8e826c63d3a70be
[tprouty/samba.git] / source / lib / messages.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 3.0
4    Samba internal messaging functions
5    Copyright (C) Andrew Tridgell 2000
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 /* this module is used for internal messaging between Samba daemons. 
23
24    The idea is that if a part of Samba wants to do communication with
25    another Samba process then it will do a message_register() of a
26    dispatch function, and use message_send_pid() to send messages to
27    that process.
28
29    This system doesn't have any inherent size limitations but is not
30    very efficient for large messages or when messages are sent in very
31    quick succession.
32
33 */
34
35 #include "includes.h"
36
37 /* the locking database handle */
38 static TDB_CONTEXT *tdb;
39 static int received_signal;
40
41 /* change the message version with any incompatible changes in the protocol */
42 #define MESSAGE_VERSION 1
43
44 struct message_rec {
45         int msg_version;
46         int msg_type;
47         pid_t dest;
48         pid_t src;
49         size_t len;
50 };
51
52 /* we have a linked list of dispatch handlers */
53 static struct dispatch_fns {
54         struct dispatch_fns *next, *prev;
55         int msg_type;
56         void (*fn)(int msg_type, pid_t pid, void *buf, size_t len);
57 } *dispatch_fns;
58
59 /****************************************************************************
60 notifications come in as signals
61 ****************************************************************************/
62 static void sig_usr1(void)
63 {
64         received_signal = 1;
65         sys_select_signal();
66 }
67
68 /****************************************************************************
69 a useful function for testing the message system
70 ****************************************************************************/
71 void ping_message(int msg_type, pid_t src, void *buf, size_t len)
72 {
73         message_send_pid(src, MSG_PONG, buf, len);
74 }
75
76 /****************************************************************************
77  Initialise the messaging functions. 
78 ****************************************************************************/
79 BOOL message_init(void)
80 {
81         if (tdb) return True;
82
83         tdb = tdb_open(lock_path("messages.tdb"), 
84                        0, TDB_CLEAR_IF_FIRST, 
85                        O_RDWR|O_CREAT,0600);
86
87         if (!tdb) {
88                 DEBUG(0,("ERROR: Failed to initialise messages database\n"));
89                 return False;
90         }
91
92         CatchSignal(SIGUSR1, sig_usr1);
93
94         message_register(MSG_PING, ping_message);
95
96         return True;
97 }
98
99
100 /*******************************************************************
101  form a static tdb key from a pid
102 ******************************************************************/
103 static TDB_DATA message_key_pid(pid_t pid)
104 {
105         static char key[20];
106         TDB_DATA kbuf;
107
108         slprintf(key, sizeof(key), "PID/%d", (int)pid);
109
110         kbuf.dptr = (char *)key;
111         kbuf.dsize = sizeof(key);
112         return kbuf;
113 }
114
115
116 /****************************************************************************
117 notify a process that it has a message. If the process doesn't exist 
118 then delete its record in the database
119 ****************************************************************************/
120 static BOOL message_notify(pid_t pid)
121 {
122         if (kill(pid, SIGUSR1) == -1) {
123                 if (errno == ESRCH) {
124                         DEBUG(2,("pid %d doesn't exist - deleting messages record\n", (int)pid));
125                         tdb_delete(tdb, message_key_pid(pid));
126                 } else {
127                         DEBUG(2,("message to process %d failed - %s\n", (int)pid, strerror(errno)));
128                 }
129                 return False;
130         }
131         return True;
132 }
133
134 /****************************************************************************
135 send a message to a particular pid
136 ****************************************************************************/
137 BOOL message_send_pid(pid_t pid, int msg_type, void *buf, size_t len)
138 {
139         TDB_DATA kbuf;
140         TDB_DATA dbuf;
141         struct message_rec rec;
142         void *p;
143
144         rec.msg_version = MESSAGE_VERSION;
145         rec.msg_type = msg_type;
146         rec.dest = pid;
147         rec.src = sys_getpid();
148         rec.len = len;
149
150         kbuf = message_key_pid(pid);
151
152         /* lock the record for the destination */
153         tdb_lockchain(tdb, kbuf);
154
155         dbuf = tdb_fetch(tdb, kbuf);
156
157         if (!dbuf.dptr) {
158                 /* its a new record */
159                 p = (void *)malloc(len + sizeof(rec));
160                 if (!p) goto failed;
161
162                 memcpy(p, &rec, sizeof(rec));
163                 if (len > 0) memcpy((void *)((unsigned)p+sizeof(rec)), buf, len);
164
165                 dbuf.dptr = p;
166                 dbuf.dsize = len + sizeof(rec);
167                 tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
168                 free(p);
169                 goto ok;
170         }
171
172         /* we're adding to an existing entry */
173         p = (void *)malloc(dbuf.dsize + len + sizeof(rec));
174         if (!p) goto failed;
175
176         memcpy(p, dbuf.dptr, dbuf.dsize);
177         memcpy((void *)((unsigned)p+dbuf.dsize), &rec, sizeof(rec));
178         if (len > 0) memcpy((void *)((unsigned)p+dbuf.dsize+sizeof(rec)), buf, len);
179
180         free(dbuf.dptr);
181         dbuf.dptr = p;
182         dbuf.dsize += len + sizeof(rec);
183         tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
184         free(dbuf.dptr);
185
186  ok:
187         tdb_unlockchain(tdb, kbuf);
188         return message_notify(pid);
189
190  failed:
191         tdb_unlockchain(tdb, kbuf);
192         return False;
193 }
194
195
196
197 /****************************************************************************
198 retrieve the next message for the current process
199 ****************************************************************************/
200 static BOOL message_recv(int *msg_type, pid_t *src, void **buf, size_t *len)
201 {
202         TDB_DATA kbuf;
203         TDB_DATA dbuf;
204         struct message_rec rec;
205
206         kbuf = message_key_pid(sys_getpid());
207
208         tdb_lockchain(tdb, kbuf);
209         
210         dbuf = tdb_fetch(tdb, kbuf);
211         if (dbuf.dptr == NULL || dbuf.dsize == 0) goto failed;
212
213         memcpy(&rec, dbuf.dptr, sizeof(rec));
214
215         if (rec.msg_version != MESSAGE_VERSION) {
216                 DEBUG(0,("message version %d received (expected %d)\n", rec.msg_version, MESSAGE_VERSION));
217                 goto failed;
218         }
219
220         if (rec.len > 0) {
221                 (*buf) = (void *)malloc(rec.len);
222                 if (!(*buf)) goto failed;
223
224                 memcpy(*buf, dbuf.dptr+sizeof(rec), rec.len);
225         } else {
226                 *buf = NULL;
227         }
228
229         *len = rec.len;
230         *msg_type = rec.msg_type;
231         *src = rec.src;
232
233         memmove(dbuf.dptr, dbuf.dptr+sizeof(rec)+rec.len, dbuf.dsize - (sizeof(rec)+rec.len));
234         dbuf.dsize -= sizeof(rec)+rec.len;
235         tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
236
237         free(dbuf.dptr);
238         tdb_unlockchain(tdb, kbuf);
239         return True;
240
241  failed:
242         tdb_unlockchain(tdb, kbuf);
243         return False;
244 }
245
246
247 /****************************************************************************
248 receive and dispatch any messages pending for this process
249 notice that all dispatch handlers for a particular msg_type get called,
250 so you can register multiple handlers for a message
251 ****************************************************************************/
252 void message_dispatch(void)
253 {
254         int msg_type;
255         pid_t src;
256         void *buf;
257         size_t len;
258         struct dispatch_fns *dfn;
259
260         if (!received_signal) return;
261         received_signal = 0;
262
263         while (message_recv(&msg_type, &src, &buf, &len)) {
264                 for (dfn = dispatch_fns; dfn; dfn = dfn->next) {
265                         if (dfn->msg_type == msg_type) {
266                                 dfn->fn(msg_type, src, buf, len);
267                         }
268                 }
269                 if (buf) free(buf);
270         }
271 }
272
273
274 /****************************************************************************
275 register a dispatch function for a particular message type
276 ****************************************************************************/
277 void message_register(int msg_type, 
278                       void (*fn)(int msg_type, pid_t pid, void *buf, size_t len))
279 {
280         struct dispatch_fns *dfn;
281
282         dfn = (struct dispatch_fns *)malloc(sizeof(*dfn));
283
284         ZERO_STRUCTP(dfn);
285
286         dfn->msg_type = msg_type;
287         dfn->fn = fn;
288
289         DLIST_ADD(dispatch_fns, dfn);
290 }
291
292 /****************************************************************************
293 de-register the function for a particular message type
294 ****************************************************************************/
295 void message_deregister(int msg_type)
296 {
297         struct dispatch_fns *dfn, *next;
298
299         for (dfn = dispatch_fns; dfn; dfn = next) {
300                 next = dfn->next;
301                 if (dfn->msg_type == msg_type) {
302                         DLIST_REMOVE(dispatch_fns, dfn);
303                         free(dfn);
304                 }
305         }       
306 }
307
308 static struct {
309         int msg_type;
310         void *buf;
311         size_t len;
312 } msg_all;
313
314 /****************************************************************************
315 send one of the messages for the broadcast
316 ****************************************************************************/
317 static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
318 {
319         struct connections_data crec;
320
321         memcpy(&crec, dbuf.dptr, sizeof(crec));
322
323         message_send_pid(crec.pid, msg_all.msg_type, msg_all.buf, msg_all.len);
324         return 0;
325 }
326
327 /****************************************************************************
328 this is a useful function for sending messages to all smbd processes.
329 It isn't very efficient, but should be OK for the sorts of applications that 
330 use it. When we need efficient broadcast we can add it.
331 ****************************************************************************/
332 BOOL message_send_all(int msg_type, void *buf, size_t len)
333 {
334         TDB_CONTEXT *tdb;
335
336         tdb = tdb_open(lock_path("connections.tdb"), 0, 0, O_RDONLY, 0);
337         if (!tdb) {
338                 DEBUG(2,("Failed to open connections database in message_send_all\n"));
339                 return False;
340         }
341
342         msg_all.msg_type = msg_type;
343         msg_all.buf = buf;
344         msg_all.len = len;
345
346         tdb_traverse(tdb, traverse_fn, NULL);
347         tdb_close(tdb);
348         return True;
349 }