More debug %d -> %u issues.
[Samba/gbeck.git] / source / lib / messages.c
blob3b45a9c305cac8176cf0da143b16bf5f480f3bfe
1 /*
2 Unix SMB/Netbios implementation.
3 Version 3.0
4 Samba internal messaging functions
5 Copyright (C) Andrew Tridgell 2000
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.
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.
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.
22 /* this module is used for internal messaging between Samba daemons.
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.
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.
35 #include "includes.h"
37 /* the locking database handle */
38 static TDB_CONTEXT *tdb;
39 static int received_signal;
41 /* change the message version with any incompatible changes in the protocol */
42 #define MESSAGE_VERSION 1
44 struct message_rec {
45 int msg_version;
46 int msg_type;
47 pid_t dest;
48 pid_t src;
49 size_t len;
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;
59 /****************************************************************************
60 notifications come in as signals
61 ****************************************************************************/
62 static void sig_usr1(void)
64 received_signal = 1;
65 sys_select_signal();
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)
73 DEBUG(1,("INFO: Received PING message from PID %u\n",(unsigned int)src));
74 message_send_pid(src, MSG_PONG, buf, len, True);
76 /****************************************************************************
77 return current debug level
78 ****************************************************************************/
79 void debuglevel_message(int msg_type, pid_t src, void *buf, size_t len)
81 DEBUG(1,("INFO: Received REQ_DEBUGLEVEL message from PID %u\n",(unsigned int)src));
82 message_send_pid(src, MSG_DEBUGLEVEL, DEBUGLEVEL_CLASS, sizeof(DEBUGLEVEL_CLASS), True);
85 /****************************************************************************
86 Initialise the messaging functions.
87 ****************************************************************************/
88 BOOL message_init(void)
90 if (tdb) return True;
92 tdb = tdb_open(lock_path("messages.tdb"),
93 0, TDB_CLEAR_IF_FIRST,
94 O_RDWR|O_CREAT,0600);
96 if (!tdb) {
97 DEBUG(0,("ERROR: Failed to initialise messages database\n"));
98 return False;
101 CatchSignal(SIGUSR1, SIGNAL_CAST sig_usr1);
103 message_register(MSG_PING, ping_message);
104 message_register(MSG_REQ_DEBUGLEVEL, debuglevel_message);
106 return True;
110 /*******************************************************************
111 form a static tdb key from a pid
112 ******************************************************************/
113 static TDB_DATA message_key_pid(pid_t pid)
115 static char key[20];
116 TDB_DATA kbuf;
118 slprintf(key, sizeof(key)-1, "PID/%d", (int)pid);
120 kbuf.dptr = (char *)key;
121 kbuf.dsize = strlen(key)+1;
122 return kbuf;
126 /****************************************************************************
127 notify a process that it has a message. If the process doesn't exist
128 then delete its record in the database
129 ****************************************************************************/
130 static BOOL message_notify(pid_t pid)
132 if (kill(pid, SIGUSR1) == -1) {
133 if (errno == ESRCH) {
134 DEBUG(2,("pid %d doesn't exist - deleting messages record\n", (int)pid));
135 tdb_delete(tdb, message_key_pid(pid));
136 } else {
137 DEBUG(2,("message to process %d failed - %s\n", (int)pid, strerror(errno)));
139 return False;
141 return True;
144 /****************************************************************************
145 send a message to a particular pid
146 ****************************************************************************/
147 BOOL message_send_pid(pid_t pid, int msg_type, void *buf, size_t len, BOOL duplicates_allowed)
149 TDB_DATA kbuf;
150 TDB_DATA dbuf;
151 struct message_rec rec;
152 void *p;
154 rec.msg_version = MESSAGE_VERSION;
155 rec.msg_type = msg_type;
156 rec.dest = pid;
157 rec.src = sys_getpid();
158 rec.len = len;
160 kbuf = message_key_pid(pid);
162 /* lock the record for the destination */
163 tdb_chainlock(tdb, kbuf);
165 dbuf = tdb_fetch(tdb, kbuf);
167 if (!dbuf.dptr) {
168 /* its a new record */
169 p = (void *)malloc(len + sizeof(rec));
170 if (!p) goto failed;
172 memcpy(p, &rec, sizeof(rec));
173 if (len > 0) memcpy((void *)((char*)p+sizeof(rec)), buf, len);
175 dbuf.dptr = p;
176 dbuf.dsize = len + sizeof(rec);
177 tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
178 free(p);
179 goto ok;
182 if (!duplicates_allowed) {
183 char *ptr;
184 struct message_rec prec;
186 for(ptr = (char *)dbuf.dptr; ptr < dbuf.dptr + dbuf.dsize; ) {
188 * First check if the message header matches, then, if it's a non-zero
189 * sized message, check if the data matches. If so it's a duplicate and
190 * we can discard it. JRA.
193 if (!memcmp(ptr, &rec, sizeof(rec))) {
194 if (!len || (len && !memcmp( ptr + sizeof(rec), (char *)buf, len))) {
195 DEBUG(10,("message_send_pid: discarding duplicate message.\n"));
196 free(dbuf.dptr);
197 tdb_chainunlock(tdb, kbuf);
198 return True;
201 memcpy(&prec, ptr, sizeof(prec));
202 ptr += sizeof(rec) + prec.len;
206 /* we're adding to an existing entry */
207 p = (void *)malloc(dbuf.dsize + len + sizeof(rec));
208 if (!p) goto failed;
210 memcpy(p, dbuf.dptr, dbuf.dsize);
211 memcpy((void *)((char*)p+dbuf.dsize), &rec, sizeof(rec));
212 if (len > 0) memcpy((void *)((char*)p+dbuf.dsize+sizeof(rec)), buf, len);
214 free(dbuf.dptr);
215 dbuf.dptr = p;
216 dbuf.dsize += len + sizeof(rec);
217 tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
218 free(dbuf.dptr);
221 tdb_chainunlock(tdb, kbuf);
222 errno = 0; /* paranoia */
223 return message_notify(pid);
225 failed:
226 tdb_chainunlock(tdb, kbuf);
227 errno = 0; /* paranoia */
228 return False;
233 /****************************************************************************
234 retrieve the next message for the current process
235 ****************************************************************************/
236 static BOOL message_recv(int *msg_type, pid_t *src, void **buf, size_t *len)
238 TDB_DATA kbuf;
239 TDB_DATA dbuf;
240 struct message_rec rec;
242 kbuf = message_key_pid(sys_getpid());
244 tdb_chainlock(tdb, kbuf);
246 dbuf = tdb_fetch(tdb, kbuf);
247 if (dbuf.dptr == NULL || dbuf.dsize == 0) goto failed;
249 memcpy(&rec, dbuf.dptr, sizeof(rec));
251 if (rec.msg_version != MESSAGE_VERSION) {
252 DEBUG(0,("message version %d received (expected %d)\n", rec.msg_version, MESSAGE_VERSION));
253 goto failed;
256 if (rec.len > 0) {
257 (*buf) = (void *)malloc(rec.len);
258 if (!(*buf)) goto failed;
260 memcpy(*buf, dbuf.dptr+sizeof(rec), rec.len);
261 } else {
262 *buf = NULL;
265 *len = rec.len;
266 *msg_type = rec.msg_type;
267 *src = rec.src;
269 if (dbuf.dsize - (sizeof(rec)+rec.len) > 0)
270 memmove(dbuf.dptr, dbuf.dptr+sizeof(rec)+rec.len, dbuf.dsize - (sizeof(rec)+rec.len));
271 dbuf.dsize -= sizeof(rec)+rec.len;
273 if (dbuf.dsize == 0)
274 tdb_delete(tdb, kbuf);
275 else
276 tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
278 free(dbuf.dptr);
279 tdb_chainunlock(tdb, kbuf);
280 return True;
282 failed:
283 tdb_chainunlock(tdb, kbuf);
284 return False;
288 /****************************************************************************
289 receive and dispatch any messages pending for this process
290 notice that all dispatch handlers for a particular msg_type get called,
291 so you can register multiple handlers for a message
292 ****************************************************************************/
293 void message_dispatch(void)
295 int msg_type;
296 pid_t src;
297 void *buf;
298 size_t len;
299 struct dispatch_fns *dfn;
301 if (!received_signal) return;
302 received_signal = 0;
304 while (message_recv(&msg_type, &src, &buf, &len)) {
305 for (dfn = dispatch_fns; dfn; dfn = dfn->next) {
306 if (dfn->msg_type == msg_type) {
307 dfn->fn(msg_type, src, buf, len);
310 if (buf) free(buf);
315 /****************************************************************************
316 register a dispatch function for a particular message type
317 ****************************************************************************/
318 void message_register(int msg_type,
319 void (*fn)(int msg_type, pid_t pid, void *buf, size_t len))
321 struct dispatch_fns *dfn;
323 dfn = (struct dispatch_fns *)malloc(sizeof(*dfn));
325 ZERO_STRUCTP(dfn);
327 dfn->msg_type = msg_type;
328 dfn->fn = fn;
330 DLIST_ADD(dispatch_fns, dfn);
333 /****************************************************************************
334 de-register the function for a particular message type
335 ****************************************************************************/
336 void message_deregister(int msg_type)
338 struct dispatch_fns *dfn, *next;
340 for (dfn = dispatch_fns; dfn; dfn = next) {
341 next = dfn->next;
342 if (dfn->msg_type == msg_type) {
343 DLIST_REMOVE(dispatch_fns, dfn);
344 free(dfn);
349 struct msg_all {
350 int msg_type;
351 void *buf;
352 size_t len;
353 BOOL duplicates;
356 /****************************************************************************
357 send one of the messages for the broadcast
358 ****************************************************************************/
359 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
361 struct connections_data crec;
362 struct msg_all *msg_all = (struct msg_all *)state;
364 memcpy(&crec, dbuf.dptr, sizeof(crec));
366 if (crec.cnum != -1) return 0;
368 /* if the msg send fails because the pid was not found (i.e. smbd died),
369 * the msg has already been deleted from the messages.tdb.*/
370 if (!message_send_pid(crec.pid, msg_all->msg_type, msg_all->buf, msg_all->len,
371 msg_all->duplicates)) {
373 /* if the pid was not found delete the entry from connections.tdb */
374 if (errno == ESRCH) {
375 DEBUG(2,("pid %u doesn't exist - deleting connections %d [%s]\n",
376 (unsigned int)crec.pid, crec.cnum, crec.name));
377 tdb_delete(the_tdb, kbuf);
380 return 0;
383 /****************************************************************************
384 this is a useful function for sending messages to all smbd processes.
385 It isn't very efficient, but should be OK for the sorts of applications that
386 use it. When we need efficient broadcast we can add it.
387 ****************************************************************************/
388 BOOL message_send_all(TDB_CONTEXT *conn_tdb, int msg_type, void *buf, size_t len, BOOL duplicates_allowed)
390 struct msg_all msg_all;
392 msg_all.msg_type = msg_type;
393 msg_all.buf = buf;
394 msg_all.len = len;
395 msg_all.duplicates = duplicates_allowed;
397 tdb_traverse(conn_tdb, traverse_fn, &msg_all);
398 return True;