Merge of Herb's profiling code.
[samba.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);
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));
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 = sizeof(key);
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)
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_lockchain(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         /* we're adding to an existing entry */
187         p = (void *)malloc(dbuf.dsize + len + sizeof(rec));
188         if (!p) goto failed;
189
190         memcpy(p, dbuf.dptr, dbuf.dsize);
191         memcpy((void *)((char*)p+dbuf.dsize), &rec, sizeof(rec));
192         if (len > 0) memcpy((void *)((char*)p+dbuf.dsize+sizeof(rec)), buf, len);
193
194         free(dbuf.dptr);
195         dbuf.dptr = p;
196         dbuf.dsize += len + sizeof(rec);
197         tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
198         free(dbuf.dptr);
199
200  ok:
201         tdb_unlockchain(tdb, kbuf);
202         return message_notify(pid);
203
204  failed:
205         tdb_unlockchain(tdb, kbuf);
206         return False;
207 }
208
209
210
211 /****************************************************************************
212 retrieve the next message for the current process
213 ****************************************************************************/
214 static BOOL message_recv(int *msg_type, pid_t *src, void **buf, size_t *len)
215 {
216         TDB_DATA kbuf;
217         TDB_DATA dbuf;
218         struct message_rec rec;
219
220         kbuf = message_key_pid(sys_getpid());
221
222         tdb_lockchain(tdb, kbuf);
223         
224         dbuf = tdb_fetch(tdb, kbuf);
225         if (dbuf.dptr == NULL || dbuf.dsize == 0) goto failed;
226
227         memcpy(&rec, dbuf.dptr, sizeof(rec));
228
229         if (rec.msg_version != MESSAGE_VERSION) {
230                 DEBUG(0,("message version %d received (expected %d)\n", rec.msg_version, MESSAGE_VERSION));
231                 goto failed;
232         }
233
234         if (rec.len > 0) {
235                 (*buf) = (void *)malloc(rec.len);
236                 if (!(*buf)) goto failed;
237
238                 memcpy(*buf, dbuf.dptr+sizeof(rec), rec.len);
239         } else {
240                 *buf = NULL;
241         }
242
243         *len = rec.len;
244         *msg_type = rec.msg_type;
245         *src = rec.src;
246
247         memmove(dbuf.dptr, dbuf.dptr+sizeof(rec)+rec.len, dbuf.dsize - (sizeof(rec)+rec.len));
248         dbuf.dsize -= sizeof(rec)+rec.len;
249         tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
250
251         free(dbuf.dptr);
252         tdb_unlockchain(tdb, kbuf);
253         return True;
254
255  failed:
256         tdb_unlockchain(tdb, kbuf);
257         return False;
258 }
259
260
261 /****************************************************************************
262 receive and dispatch any messages pending for this process
263 notice that all dispatch handlers for a particular msg_type get called,
264 so you can register multiple handlers for a message
265 ****************************************************************************/
266 void message_dispatch(void)
267 {
268         int msg_type;
269         pid_t src;
270         void *buf;
271         size_t len;
272         struct dispatch_fns *dfn;
273
274         if (!received_signal) return;
275         received_signal = 0;
276
277         while (message_recv(&msg_type, &src, &buf, &len)) {
278                 for (dfn = dispatch_fns; dfn; dfn = dfn->next) {
279                         if (dfn->msg_type == msg_type) {
280                                 dfn->fn(msg_type, src, buf, len);
281                         }
282                 }
283                 if (buf) free(buf);
284         }
285 }
286
287
288 /****************************************************************************
289 register a dispatch function for a particular message type
290 ****************************************************************************/
291 void message_register(int msg_type, 
292                       void (*fn)(int msg_type, pid_t pid, void *buf, size_t len))
293 {
294         struct dispatch_fns *dfn;
295
296         dfn = (struct dispatch_fns *)malloc(sizeof(*dfn));
297
298         ZERO_STRUCTP(dfn);
299
300         dfn->msg_type = msg_type;
301         dfn->fn = fn;
302
303         DLIST_ADD(dispatch_fns, dfn);
304 }
305
306 /****************************************************************************
307 de-register the function for a particular message type
308 ****************************************************************************/
309 void message_deregister(int msg_type)
310 {
311         struct dispatch_fns *dfn, *next;
312
313         for (dfn = dispatch_fns; dfn; dfn = next) {
314                 next = dfn->next;
315                 if (dfn->msg_type == msg_type) {
316                         DLIST_REMOVE(dispatch_fns, dfn);
317                         free(dfn);
318                 }
319         }       
320 }
321
322 static struct {
323         int msg_type;
324         void *buf;
325         size_t len;
326 } msg_all;
327
328 /****************************************************************************
329 send one of the messages for the broadcast
330 ****************************************************************************/
331 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
332 {
333         struct connections_data crec;
334
335         memcpy(&crec, dbuf.dptr, sizeof(crec));
336
337         if (crec.cnum == -1) return 0;
338         message_send_pid(crec.pid, msg_all.msg_type, msg_all.buf, msg_all.len);
339         return 0;
340 }
341
342 /****************************************************************************
343 this is a useful function for sending messages to all smbd processes.
344 It isn't very efficient, but should be OK for the sorts of applications that 
345 use it. When we need efficient broadcast we can add it.
346 ****************************************************************************/
347 BOOL message_send_all(int msg_type, void *buf, size_t len)
348 {
349         TDB_CONTEXT *the_tdb;
350
351         the_tdb = tdb_open(lock_path("connections.tdb"), 0, 0, O_RDONLY, 0);
352         if (!the_tdb) {
353                 DEBUG(2,("Failed to open connections database in message_send_all\n"));
354                 return False;
355         }
356
357         msg_all.msg_type = msg_type;
358         msg_all.buf = buf;
359         msg_all.len = len;
360
361         tdb_traverse(the_tdb, traverse_fn, NULL);
362         tdb_close(the_tdb);
363         return True;
364 }