removed unnecessary process_exists() call in message_send_pid()
[vlendec/samba-autobuild/.git] / source3 / 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         DEBUG(1,("INFO: Received PING message from PID %d\n",src));
74         message_send_pid(src, MSG_PONG, buf, len, True);
75 }
76
77 /****************************************************************************
78 return current debug level
79 ****************************************************************************/
80 void debuglevel_message(int msg_type, pid_t src, void *buf, size_t len)
81 {
82         int level;
83         
84         DEBUG(1,("INFO: Received REQ_DEBUGLEVEL message from PID %d\n",src));
85         level = DEBUGLEVEL;
86         message_send_pid(src, MSG_DEBUGLEVEL, &level, sizeof(int), True);
87 }
88
89 /****************************************************************************
90  Initialise the messaging functions. 
91 ****************************************************************************/
92 BOOL message_init(void)
93 {
94         if (tdb) return True;
95
96         tdb = tdb_open(lock_path("messages.tdb"), 
97                        0, TDB_CLEAR_IF_FIRST, 
98                        O_RDWR|O_CREAT,0600);
99
100         if (!tdb) {
101                 DEBUG(0,("ERROR: Failed to initialise messages database\n"));
102                 return False;
103         }
104
105         CatchSignal(SIGUSR1, SIGNAL_CAST sig_usr1);
106
107         message_register(MSG_PING, ping_message);
108         message_register(MSG_REQ_DEBUGLEVEL, debuglevel_message);
109
110         return True;
111 }
112
113
114 /*******************************************************************
115  form a static tdb key from a pid
116 ******************************************************************/
117 static TDB_DATA message_key_pid(pid_t pid)
118 {
119         static char key[20];
120         TDB_DATA kbuf;
121
122         slprintf(key, sizeof(key), "PID/%d", (int)pid);
123         
124         kbuf.dptr = (char *)key;
125         kbuf.dsize = strlen(key)+1;
126         return kbuf;
127 }
128
129
130 /****************************************************************************
131 notify a process that it has a message. If the process doesn't exist 
132 then delete its record in the database
133 ****************************************************************************/
134 static BOOL message_notify(pid_t pid)
135 {
136         if (kill(pid, SIGUSR1) == -1) {
137                 if (errno == ESRCH) {
138                         DEBUG(2,("pid %d doesn't exist - deleting messages record\n", (int)pid));
139                         tdb_delete(tdb, message_key_pid(pid));
140                 } else {
141                         DEBUG(2,("message to process %d failed - %s\n", (int)pid, strerror(errno)));
142                 }
143                 return False;
144         }
145         return True;
146 }
147
148 /****************************************************************************
149 send a message to a particular pid
150 ****************************************************************************/
151 BOOL message_send_pid(pid_t pid, int msg_type, void *buf, size_t len, BOOL duplicates_allowed)
152 {
153         TDB_DATA kbuf;
154         TDB_DATA dbuf;
155         struct message_rec rec;
156         void *p;
157
158         rec.msg_version = MESSAGE_VERSION;
159         rec.msg_type = msg_type;
160         rec.dest = pid;
161         rec.src = sys_getpid();
162         rec.len = len;
163
164         kbuf = message_key_pid(pid);
165
166         /* lock the record for the destination */
167         tdb_chainlock(tdb, kbuf);
168
169         dbuf = tdb_fetch(tdb, kbuf);
170
171         if (!dbuf.dptr) {
172                 /* its a new record */
173                 p = (void *)malloc(len + sizeof(rec));
174                 if (!p) goto failed;
175
176                 memcpy(p, &rec, sizeof(rec));
177                 if (len > 0) memcpy((void *)((char*)p+sizeof(rec)), buf, len);
178
179                 dbuf.dptr = p;
180                 dbuf.dsize = len + sizeof(rec);
181                 tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
182                 free(p);
183                 goto ok;
184         }
185
186         if (!duplicates_allowed) {
187                 char *ptr;
188                 struct message_rec prec;
189                 
190                 for(ptr = (char *)dbuf.dptr; ptr < dbuf.dptr + dbuf.dsize; ) {
191                         /*
192                          * First check if the message header matches, then, if it's a non-zero
193                          * sized message, check if the data matches. If so it's a duplicate and
194                          * we can discard it. JRA.
195                          */
196
197                         if (!memcmp(ptr, &rec, sizeof(rec))) {
198                                 if (!len || (len && !memcmp( ptr + sizeof(rec), (char *)buf, len))) {
199                                         DEBUG(10,("message_send_pid: discarding duplicate message.\n"));
200                                         free(dbuf.dptr);
201                                         tdb_chainunlock(tdb, kbuf);
202                                         return True;
203                                 }
204                         }
205                         memcpy(&prec, ptr, sizeof(prec));
206                         ptr += sizeof(rec) + prec.len;
207                 }
208         }
209
210         /* we're adding to an existing entry */
211         p = (void *)malloc(dbuf.dsize + len + sizeof(rec));
212         if (!p) goto failed;
213
214         memcpy(p, dbuf.dptr, dbuf.dsize);
215         memcpy((void *)((char*)p+dbuf.dsize), &rec, sizeof(rec));
216         if (len > 0) memcpy((void *)((char*)p+dbuf.dsize+sizeof(rec)), buf, len);
217
218         free(dbuf.dptr);
219         dbuf.dptr = p;
220         dbuf.dsize += len + sizeof(rec);
221         tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
222         free(dbuf.dptr);
223
224  ok:
225         tdb_chainunlock(tdb, kbuf);
226         return message_notify(pid);
227
228  failed:
229         tdb_chainunlock(tdb, kbuf);
230         return False;
231 }
232
233
234
235 /****************************************************************************
236 retrieve the next message for the current process
237 ****************************************************************************/
238 static BOOL message_recv(int *msg_type, pid_t *src, void **buf, size_t *len)
239 {
240         TDB_DATA kbuf;
241         TDB_DATA dbuf;
242         struct message_rec rec;
243
244         kbuf = message_key_pid(sys_getpid());
245
246         tdb_chainlock(tdb, kbuf);
247         
248         dbuf = tdb_fetch(tdb, kbuf);
249         if (dbuf.dptr == NULL || dbuf.dsize == 0) goto failed;
250
251         memcpy(&rec, dbuf.dptr, sizeof(rec));
252
253         if (rec.msg_version != MESSAGE_VERSION) {
254                 DEBUG(0,("message version %d received (expected %d)\n", rec.msg_version, MESSAGE_VERSION));
255                 goto failed;
256         }
257
258         if (rec.len > 0) {
259                 (*buf) = (void *)malloc(rec.len);
260                 if (!(*buf)) goto failed;
261
262                 memcpy(*buf, dbuf.dptr+sizeof(rec), rec.len);
263         } else {
264                 *buf = NULL;
265         }
266
267         *len = rec.len;
268         *msg_type = rec.msg_type;
269         *src = rec.src;
270
271         if (dbuf.dsize - (sizeof(rec)+rec.len) > 0)
272                 memmove(dbuf.dptr, dbuf.dptr+sizeof(rec)+rec.len, dbuf.dsize - (sizeof(rec)+rec.len));
273         dbuf.dsize -= sizeof(rec)+rec.len;
274
275         if (dbuf.dsize == 0)
276                 tdb_delete(tdb, kbuf);
277         else
278                 tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
279
280         free(dbuf.dptr);
281         tdb_chainunlock(tdb, kbuf);
282         return True;
283
284  failed:
285         tdb_chainunlock(tdb, kbuf);
286         return False;
287 }
288
289
290 /****************************************************************************
291 receive and dispatch any messages pending for this process
292 notice that all dispatch handlers for a particular msg_type get called,
293 so you can register multiple handlers for a message
294 ****************************************************************************/
295 void message_dispatch(void)
296 {
297         int msg_type;
298         pid_t src;
299         void *buf;
300         size_t len;
301         struct dispatch_fns *dfn;
302
303         if (!received_signal) return;
304         received_signal = 0;
305
306         while (message_recv(&msg_type, &src, &buf, &len)) {
307                 for (dfn = dispatch_fns; dfn; dfn = dfn->next) {
308                         if (dfn->msg_type == msg_type) {
309                                 dfn->fn(msg_type, src, buf, len);
310                         }
311                 }
312                 if (buf) free(buf);
313         }
314 }
315
316
317 /****************************************************************************
318 register a dispatch function for a particular message type
319 ****************************************************************************/
320 void message_register(int msg_type, 
321                       void (*fn)(int msg_type, pid_t pid, void *buf, size_t len))
322 {
323         struct dispatch_fns *dfn;
324
325         dfn = (struct dispatch_fns *)malloc(sizeof(*dfn));
326
327         ZERO_STRUCTP(dfn);
328
329         dfn->msg_type = msg_type;
330         dfn->fn = fn;
331
332         DLIST_ADD(dispatch_fns, dfn);
333 }
334
335 /****************************************************************************
336 de-register the function for a particular message type
337 ****************************************************************************/
338 void message_deregister(int msg_type)
339 {
340         struct dispatch_fns *dfn, *next;
341
342         for (dfn = dispatch_fns; dfn; dfn = next) {
343                 next = dfn->next;
344                 if (dfn->msg_type == msg_type) {
345                         DLIST_REMOVE(dispatch_fns, dfn);
346                         free(dfn);
347                 }
348         }       
349 }
350
351 static struct {
352         int msg_type;
353         void *buf;
354         size_t len;
355         BOOL duplicates;
356 } msg_all;
357
358 /****************************************************************************
359 send one of the messages for the broadcast
360 ****************************************************************************/
361 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
362 {
363         struct connections_data crec;
364
365         memcpy(&crec, dbuf.dptr, sizeof(crec));
366
367         if (crec.cnum != -1) return 0;
368         message_send_pid(crec.pid, msg_all.msg_type, msg_all.buf, msg_all.len, msg_all.duplicates);
369         return 0;
370 }
371
372 /****************************************************************************
373 this is a useful function for sending messages to all smbd processes.
374 It isn't very efficient, but should be OK for the sorts of applications that 
375 use it. When we need efficient broadcast we can add it.
376 ****************************************************************************/
377 BOOL message_send_all(TDB_CONTEXT *conn_tdb, int msg_type, void *buf, size_t len, BOOL duplicates_allowed)
378 {
379         msg_all.msg_type = msg_type;
380         msg_all.buf = buf;
381         msg_all.len = len;
382         msg_all.duplicates = duplicates_allowed;
383
384         tdb_traverse(conn_tdb, traverse_fn, NULL);
385         return True;
386 }