source: branches/samba-3.2.x/source/lib/messages_local.c

Last change on this file was 133, checked in by Paul Smedley, 17 years ago

Update trunk to 3.2.0pre3

File size: 10.9 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3 Samba internal messaging functions
4 Copyright (C) 2007 by Volker Lendecke
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20/**
21 @defgroup messages Internal messaging framework
22 @{
23 @file messages.c
24
25 @brief Module for internal messaging between Samba daemons.
26
27 The idea is that if a part of Samba wants to do communication with
28 another Samba process then it will do a message_register() of a
29 dispatch function, and use message_send_pid() to send messages to
30 that process.
31
32 The dispatch function is given the pid of the sender, and it can
33 use that to reply by message_send_pid(). See ping_message() for a
34 simple example.
35
36 @caution Dispatch functions must be able to cope with incoming
37 messages on an *odd* byte boundary.
38
39 This system doesn't have any inherent size limitations but is not
40 very efficient for large messages or when messages are sent in very
41 quick succession.
42
43*/
44
45#include "includes.h"
46#include "librpc/gen_ndr/messaging.h"
47#include "librpc/gen_ndr/ndr_messaging.h"
48
49static sig_atomic_t received_signal;
50
51static NTSTATUS messaging_tdb_send(struct messaging_context *msg_ctx,
52 struct server_id pid, int msg_type,
53 const DATA_BLOB *data,
54 struct messaging_backend *backend);
55
56/****************************************************************************
57 Notifications come in as signals.
58****************************************************************************/
59
60static void sig_usr1(void)
61{
62 received_signal = 1;
63 sys_select_signal(SIGUSR1);
64}
65
66static int messaging_tdb_destructor(struct messaging_backend *tdb_ctx)
67{
68 TDB_CONTEXT *tdb = (TDB_CONTEXT *)tdb_ctx->private_data;
69 tdb_close(tdb);
70 return 0;
71}
72
73/****************************************************************************
74 Initialise the messaging functions.
75****************************************************************************/
76
77NTSTATUS messaging_tdb_init(struct messaging_context *msg_ctx,
78 TALLOC_CTX *mem_ctx,
79 struct messaging_backend **presult)
80{
81 struct messaging_backend *result;
82 TDB_CONTEXT *tdb;
83
84 if (!(result = TALLOC_P(mem_ctx, struct messaging_backend))) {
85 DEBUG(0, ("talloc failed\n"));
86 return NT_STATUS_NO_MEMORY;
87 }
88
89 tdb = tdb_open_log(lock_path("messages.tdb"),
90 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT,
91 O_RDWR|O_CREAT,0600);
92
93 if (!tdb) {
94 NTSTATUS status = map_nt_error_from_unix(errno);
95 DEBUG(0, ("ERROR: Failed to initialise messages database: "
96 "%s\n", strerror(errno)));
97 TALLOC_FREE(result);
98 return status;
99 }
100
101 sec_init();
102
103 /* Activate the per-hashchain freelist */
104 tdb_set_max_dead(tdb, 5);
105
106 CatchSignal(SIGUSR1, SIGNAL_CAST sig_usr1);
107
108 result->private_data = (void *)tdb;
109 result->send_fn = messaging_tdb_send;
110
111 talloc_set_destructor(result, messaging_tdb_destructor);
112
113 *presult = result;
114 return NT_STATUS_OK;
115}
116
117/*******************************************************************
118 Form a static tdb key from a pid.
119******************************************************************/
120
121static TDB_DATA message_key_pid(TALLOC_CTX *mem_ctx, struct server_id pid)
122{
123 char *key;
124 TDB_DATA kbuf;
125
126 key = talloc_asprintf(talloc_tos(), "PID/%s", procid_str_static(&pid));
127
128 SMB_ASSERT(key != NULL);
129
130 kbuf.dptr = (uint8 *)key;
131 kbuf.dsize = strlen(key)+1;
132 return kbuf;
133}
134
135/*
136 Fetch the messaging array for a process
137 */
138
139static NTSTATUS messaging_tdb_fetch(TDB_CONTEXT *msg_tdb,
140 TDB_DATA key,
141 TALLOC_CTX *mem_ctx,
142 struct messaging_array **presult)
143{
144 struct messaging_array *result;
145 TDB_DATA data;
146 DATA_BLOB blob;
147 enum ndr_err_code ndr_err;
148
149 if (!(result = TALLOC_ZERO_P(mem_ctx, struct messaging_array))) {
150 return NT_STATUS_NO_MEMORY;
151 }
152
153 data = tdb_fetch(msg_tdb, key);
154
155 if (data.dptr == NULL) {
156 *presult = result;
157 return NT_STATUS_OK;
158 }
159
160 blob = data_blob_const(data.dptr, data.dsize);
161
162 ndr_err = ndr_pull_struct_blob(
163 &blob, result, result,
164 (ndr_pull_flags_fn_t)ndr_pull_messaging_array);
165
166 SAFE_FREE(data.dptr);
167
168 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
169 TALLOC_FREE(result);
170 return ndr_map_error2ntstatus(ndr_err);
171 }
172
173 if (DEBUGLEVEL >= 10) {
174 DEBUG(10, ("messaging_tdb_fetch:\n"));
175 NDR_PRINT_DEBUG(messaging_array, result);
176 }
177
178 *presult = result;
179 return NT_STATUS_OK;
180}
181
182/*
183 Store a messaging array for a pid
184*/
185
186static NTSTATUS messaging_tdb_store(TDB_CONTEXT *msg_tdb,
187 TDB_DATA key,
188 struct messaging_array *array)
189{
190 TDB_DATA data;
191 DATA_BLOB blob;
192 enum ndr_err_code ndr_err;
193 TALLOC_CTX *mem_ctx;
194 int ret;
195
196 if (array->num_messages == 0) {
197 tdb_delete(msg_tdb, key);
198 return NT_STATUS_OK;
199 }
200
201 if (!(mem_ctx = talloc_new(array))) {
202 return NT_STATUS_NO_MEMORY;
203 }
204
205 ndr_err = ndr_push_struct_blob(
206 &blob, mem_ctx, array,
207 (ndr_push_flags_fn_t)ndr_push_messaging_array);
208
209 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
210 talloc_free(mem_ctx);
211 return ndr_map_error2ntstatus(ndr_err);
212 }
213
214 if (DEBUGLEVEL >= 10) {
215 DEBUG(10, ("messaging_tdb_store:\n"));
216 NDR_PRINT_DEBUG(messaging_array, array);
217 }
218
219 data.dptr = blob.data;
220 data.dsize = blob.length;
221
222 ret = tdb_store(msg_tdb, key, data, TDB_REPLACE);
223 TALLOC_FREE(mem_ctx);
224
225 return (ret == 0) ? NT_STATUS_OK : NT_STATUS_INTERNAL_DB_CORRUPTION;
226}
227
228/****************************************************************************
229 Notify a process that it has a message. If the process doesn't exist
230 then delete its record in the database.
231****************************************************************************/
232
233static NTSTATUS message_notify(struct server_id procid)
234{
235 pid_t pid = procid.pid;
236 int ret;
237 uid_t euid = geteuid();
238
239 /*
240 * Doing kill with a non-positive pid causes messages to be
241 * sent to places we don't want.
242 */
243
244 SMB_ASSERT(pid > 0);
245
246 if (euid != 0) {
247 /* If we're not root become so to send the message. */
248 save_re_uid();
249 set_effective_uid(0);
250 }
251
252 ret = kill(pid, SIGUSR1);
253
254 if (euid != 0) {
255 /* Go back to who we were. */
256 int saved_errno = errno;
257 restore_re_uid_fromroot();
258 errno = saved_errno;
259 }
260
261 if (ret == 0) {
262 return NT_STATUS_OK;
263 }
264
265 /*
266 * Something has gone wrong
267 */
268
269 DEBUG(2,("message to process %d failed - %s\n", (int)pid,
270 strerror(errno)));
271
272 /*
273 * No call to map_nt_error_from_unix -- don't want to link in
274 * errormap.o into lots of utils.
275 */
276
277 if (errno == ESRCH) return NT_STATUS_INVALID_HANDLE;
278 if (errno == EINVAL) return NT_STATUS_INVALID_PARAMETER;
279 if (errno == EPERM) return NT_STATUS_ACCESS_DENIED;
280 return NT_STATUS_UNSUCCESSFUL;
281}
282
283/****************************************************************************
284 Send a message to a particular pid.
285****************************************************************************/
286
287static NTSTATUS messaging_tdb_send(struct messaging_context *msg_ctx,
288 struct server_id pid, int msg_type,
289 const DATA_BLOB *data,
290 struct messaging_backend *backend)
291{
292 struct messaging_array *msg_array;
293 struct messaging_rec *rec;
294 NTSTATUS status;
295 TDB_DATA key;
296 TDB_CONTEXT *tdb = (TDB_CONTEXT *)backend->private_data;
297 TALLOC_CTX *frame = talloc_stackframe();
298
299 /* NULL pointer means implicit length zero. */
300 if (!data->data) {
301 SMB_ASSERT(data->length == 0);
302 }
303
304 /*
305 * Doing kill with a non-positive pid causes messages to be
306 * sent to places we don't want.
307 */
308
309 SMB_ASSERT(procid_to_pid(&pid) > 0);
310
311 key = message_key_pid(frame, pid);
312
313 if (tdb_chainlock(tdb, key) == -1) {
314 TALLOC_FREE(frame);
315 return NT_STATUS_LOCK_NOT_GRANTED;
316 }
317
318 status = messaging_tdb_fetch(tdb, key, talloc_tos(), &msg_array);
319
320 if (!NT_STATUS_IS_OK(status)) {
321 goto done;
322 }
323
324 if ((msg_type & MSG_FLAG_LOWPRIORITY)
325 && (msg_array->num_messages > 1000)) {
326 DEBUG(5, ("Dropping message for PID %s\n",
327 procid_str_static(&pid)));
328 status = NT_STATUS_INSUFFICIENT_RESOURCES;
329 goto done;
330 }
331
332 if (!(rec = TALLOC_REALLOC_ARRAY(talloc_tos(), msg_array->messages,
333 struct messaging_rec,
334 msg_array->num_messages+1))) {
335 status = NT_STATUS_NO_MEMORY;
336 goto done;
337 }
338
339 rec[msg_array->num_messages].msg_version = MESSAGE_VERSION;
340 rec[msg_array->num_messages].msg_type = msg_type & MSG_TYPE_MASK;
341 rec[msg_array->num_messages].dest = pid;
342 rec[msg_array->num_messages].src = procid_self();
343 rec[msg_array->num_messages].buf = *data;
344
345 msg_array->messages = rec;
346 msg_array->num_messages += 1;
347
348 status = messaging_tdb_store(tdb, key, msg_array);
349
350 if (!NT_STATUS_IS_OK(status)) {
351 goto done;
352 }
353
354 status = message_notify(pid);
355
356 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
357 DEBUG(2, ("pid %s doesn't exist - deleting messages record\n",
358 procid_str_static(&pid)));
359 tdb_delete(tdb, message_key_pid(talloc_tos(), pid));
360 }
361
362 done:
363 tdb_chainunlock(tdb, key);
364 TALLOC_FREE(frame);
365 return status;
366}
367
368/****************************************************************************
369 Retrieve all messages for the current process.
370****************************************************************************/
371
372static NTSTATUS retrieve_all_messages(TDB_CONTEXT *msg_tdb,
373 TALLOC_CTX *mem_ctx,
374 struct messaging_array **presult)
375{
376 struct messaging_array *result;
377 TDB_DATA key = message_key_pid(mem_ctx, procid_self());
378 NTSTATUS status;
379
380 if (tdb_chainlock(msg_tdb, key) == -1) {
381 TALLOC_FREE(key.dptr);
382 return NT_STATUS_LOCK_NOT_GRANTED;
383 }
384
385 status = messaging_tdb_fetch(msg_tdb, key, mem_ctx, &result);
386
387 /*
388 * We delete the record here, tdb_set_max_dead keeps it around
389 */
390 tdb_delete(msg_tdb, key);
391 tdb_chainunlock(msg_tdb, key);
392
393 if (NT_STATUS_IS_OK(status)) {
394 *presult = result;
395 }
396
397 TALLOC_FREE(key.dptr);
398
399 return status;
400}
401
402/****************************************************************************
403 Receive and dispatch any messages pending for this process.
404 JRA changed Dec 13 2006. Only one message handler now permitted per type.
405 *NOTE*: Dispatch functions must be able to cope with incoming
406 messages on an *odd* byte boundary.
407****************************************************************************/
408
409void message_dispatch(struct messaging_context *msg_ctx)
410{
411 struct messaging_array *msg_array = NULL;
412 TDB_CONTEXT *tdb = (TDB_CONTEXT *)(msg_ctx->local->private_data);
413 uint32 i;
414
415 if (!received_signal)
416 return;
417
418 DEBUG(10, ("message_dispatch: received_signal = %d\n",
419 received_signal));
420
421 received_signal = 0;
422
423 if (!NT_STATUS_IS_OK(retrieve_all_messages(tdb, NULL, &msg_array))) {
424 return;
425 }
426
427 for (i=0; i<msg_array->num_messages; i++) {
428 messaging_dispatch_rec(msg_ctx, &msg_array->messages[i]);
429 }
430
431 TALLOC_FREE(msg_array);
432}
433
434/** @} **/
Note: See TracBrowser for help on using the repository browser.