pidl/NDR/Parser: split off ParseArrayPullGetSize() and ParseArrayPullGetLength()
[Samba.git] / source3 / lib / messages_local.c
blob0da05466a2f2ce52287d10eec51722c171d7cbc6
1 /*
2 Unix SMB/CIFS implementation.
3 Samba internal messaging functions
4 Copyright (C) 2007 by Volker Lendecke
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.
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.
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/>.
20 /**
21 @defgroup messages Internal messaging framework
23 @file messages.c
25 @brief Module for internal messaging between Samba daemons.
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.
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.
36 @caution Dispatch functions must be able to cope with incoming
37 messages on an *odd* byte boundary.
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.
45 #include "includes.h"
46 #include "librpc/gen_ndr/messaging.h"
47 #include "librpc/gen_ndr/ndr_messaging.h"
49 struct messaging_tdb_context {
50 struct messaging_context *msg_ctx;
51 struct tdb_wrap *tdb;
52 struct tevent_signal *se;
53 int received_messages;
56 static NTSTATUS messaging_tdb_send(struct messaging_context *msg_ctx,
57 struct server_id pid, int msg_type,
58 const DATA_BLOB *data,
59 struct messaging_backend *backend);
60 static void message_dispatch(struct messaging_context *msg_ctx);
62 static void messaging_tdb_signal_handler(struct tevent_context *ev_ctx,
63 struct tevent_signal *se,
64 int signum, int count,
65 void *_info, void *private_data)
67 struct messaging_tdb_context *ctx = talloc_get_type(private_data,
68 struct messaging_tdb_context);
70 ctx->received_messages++;
72 DEBUG(10, ("messaging_tdb_signal_handler: sig[%d] count[%d] msgs[%d]\n",
73 signum, count, ctx->received_messages));
75 message_dispatch(ctx->msg_ctx);
78 /****************************************************************************
79 Initialise the messaging functions.
80 ****************************************************************************/
82 NTSTATUS messaging_tdb_init(struct messaging_context *msg_ctx,
83 TALLOC_CTX *mem_ctx,
84 struct messaging_backend **presult)
86 struct messaging_backend *result;
87 struct messaging_tdb_context *ctx;
89 if (!(result = TALLOC_P(mem_ctx, struct messaging_backend))) {
90 DEBUG(0, ("talloc failed\n"));
91 return NT_STATUS_NO_MEMORY;
94 ctx = TALLOC_ZERO_P(result, struct messaging_tdb_context);
95 if (!ctx) {
96 DEBUG(0, ("talloc failed\n"));
97 TALLOC_FREE(result);
98 return NT_STATUS_NO_MEMORY;
100 result->private_data = ctx;
101 result->send_fn = messaging_tdb_send;
103 ctx->msg_ctx = msg_ctx;
105 ctx->tdb = tdb_wrap_open(ctx, lock_path("messages.tdb"), 0,
106 TDB_CLEAR_IF_FIRST|TDB_DEFAULT|TDB_VOLATILE,
107 O_RDWR|O_CREAT,0600);
109 if (!ctx->tdb) {
110 NTSTATUS status = map_nt_error_from_unix(errno);
111 DEBUG(0, ("ERROR: Failed to initialise messages database: "
112 "%s\n", strerror(errno)));
113 TALLOC_FREE(result);
114 return status;
117 ctx->se = tevent_add_signal(msg_ctx->event_ctx,
118 ctx,
119 SIGUSR1, 0,
120 messaging_tdb_signal_handler,
121 ctx);
122 if (!ctx->se) {
123 NTSTATUS status = map_nt_error_from_unix(errno);
124 DEBUG(0, ("ERROR: Failed to initialise messages signal handler: "
125 "%s\n", strerror(errno)));
126 TALLOC_FREE(result);
127 return status;
130 sec_init();
132 *presult = result;
133 return NT_STATUS_OK;
136 /*******************************************************************
137 Form a static tdb key from a pid.
138 ******************************************************************/
140 static TDB_DATA message_key_pid(TALLOC_CTX *mem_ctx, struct server_id pid)
142 char *key;
143 TDB_DATA kbuf;
145 key = talloc_asprintf(talloc_tos(), "PID/%s", procid_str_static(&pid));
147 SMB_ASSERT(key != NULL);
149 kbuf.dptr = (uint8 *)key;
150 kbuf.dsize = strlen(key)+1;
151 return kbuf;
155 Fetch the messaging array for a process
158 static NTSTATUS messaging_tdb_fetch(TDB_CONTEXT *msg_tdb,
159 TDB_DATA key,
160 TALLOC_CTX *mem_ctx,
161 struct messaging_array **presult)
163 struct messaging_array *result;
164 TDB_DATA data;
165 DATA_BLOB blob;
166 enum ndr_err_code ndr_err;
168 if (!(result = TALLOC_ZERO_P(mem_ctx, struct messaging_array))) {
169 return NT_STATUS_NO_MEMORY;
172 data = tdb_fetch(msg_tdb, key);
174 if (data.dptr == NULL) {
175 *presult = result;
176 return NT_STATUS_OK;
179 blob = data_blob_const(data.dptr, data.dsize);
181 ndr_err = ndr_pull_struct_blob(
182 &blob, result, NULL, result,
183 (ndr_pull_flags_fn_t)ndr_pull_messaging_array);
185 SAFE_FREE(data.dptr);
187 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
188 TALLOC_FREE(result);
189 return ndr_map_error2ntstatus(ndr_err);
192 if (DEBUGLEVEL >= 10) {
193 DEBUG(10, ("messaging_tdb_fetch:\n"));
194 NDR_PRINT_DEBUG(messaging_array, result);
197 *presult = result;
198 return NT_STATUS_OK;
202 Store a messaging array for a pid
205 static NTSTATUS messaging_tdb_store(TDB_CONTEXT *msg_tdb,
206 TDB_DATA key,
207 struct messaging_array *array)
209 TDB_DATA data;
210 DATA_BLOB blob;
211 enum ndr_err_code ndr_err;
212 TALLOC_CTX *mem_ctx;
213 int ret;
215 if (array->num_messages == 0) {
216 tdb_delete(msg_tdb, key);
217 return NT_STATUS_OK;
220 if (!(mem_ctx = talloc_new(array))) {
221 return NT_STATUS_NO_MEMORY;
224 ndr_err = ndr_push_struct_blob(
225 &blob, mem_ctx, NULL, array,
226 (ndr_push_flags_fn_t)ndr_push_messaging_array);
228 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
229 talloc_free(mem_ctx);
230 return ndr_map_error2ntstatus(ndr_err);
233 if (DEBUGLEVEL >= 10) {
234 DEBUG(10, ("messaging_tdb_store:\n"));
235 NDR_PRINT_DEBUG(messaging_array, array);
238 data.dptr = blob.data;
239 data.dsize = blob.length;
241 ret = tdb_store(msg_tdb, key, data, TDB_REPLACE);
242 TALLOC_FREE(mem_ctx);
244 return (ret == 0) ? NT_STATUS_OK : NT_STATUS_INTERNAL_DB_CORRUPTION;
247 /****************************************************************************
248 Notify a process that it has a message. If the process doesn't exist
249 then delete its record in the database.
250 ****************************************************************************/
252 static NTSTATUS message_notify(struct server_id procid)
254 pid_t pid = procid.pid;
255 int ret;
256 uid_t euid = geteuid();
259 * Doing kill with a non-positive pid causes messages to be
260 * sent to places we don't want.
263 SMB_ASSERT(pid > 0);
265 if (euid != 0) {
266 /* If we're not root become so to send the message. */
267 save_re_uid();
268 set_effective_uid(0);
271 ret = kill(pid, SIGUSR1);
273 if (euid != 0) {
274 /* Go back to who we were. */
275 int saved_errno = errno;
276 restore_re_uid_fromroot();
277 errno = saved_errno;
280 if (ret == 0) {
281 return NT_STATUS_OK;
285 * Something has gone wrong
288 DEBUG(2,("message to process %d failed - %s\n", (int)pid,
289 strerror(errno)));
292 * No call to map_nt_error_from_unix -- don't want to link in
293 * errormap.o into lots of utils.
296 if (errno == ESRCH) return NT_STATUS_INVALID_HANDLE;
297 if (errno == EINVAL) return NT_STATUS_INVALID_PARAMETER;
298 if (errno == EPERM) return NT_STATUS_ACCESS_DENIED;
299 return NT_STATUS_UNSUCCESSFUL;
302 /****************************************************************************
303 Send a message to a particular pid.
304 ****************************************************************************/
306 static NTSTATUS messaging_tdb_send(struct messaging_context *msg_ctx,
307 struct server_id pid, int msg_type,
308 const DATA_BLOB *data,
309 struct messaging_backend *backend)
311 struct messaging_tdb_context *ctx = talloc_get_type(backend->private_data,
312 struct messaging_tdb_context);
313 struct messaging_array *msg_array;
314 struct messaging_rec *rec;
315 NTSTATUS status;
316 TDB_DATA key;
317 struct tdb_wrap *tdb = ctx->tdb;
318 TALLOC_CTX *frame = talloc_stackframe();
320 /* NULL pointer means implicit length zero. */
321 if (!data->data) {
322 SMB_ASSERT(data->length == 0);
326 * Doing kill with a non-positive pid causes messages to be
327 * sent to places we don't want.
330 SMB_ASSERT(procid_to_pid(&pid) > 0);
332 key = message_key_pid(frame, pid);
334 if (tdb_chainlock(tdb->tdb, key) == -1) {
335 TALLOC_FREE(frame);
336 return NT_STATUS_LOCK_NOT_GRANTED;
339 status = messaging_tdb_fetch(tdb->tdb, key, talloc_tos(), &msg_array);
341 if (!NT_STATUS_IS_OK(status)) {
342 goto done;
345 if ((msg_type & MSG_FLAG_LOWPRIORITY)
346 && (msg_array->num_messages > 1000)) {
347 DEBUG(5, ("Dropping message for PID %s\n",
348 procid_str_static(&pid)));
349 status = NT_STATUS_INSUFFICIENT_RESOURCES;
350 goto done;
353 if (!(rec = TALLOC_REALLOC_ARRAY(talloc_tos(), msg_array->messages,
354 struct messaging_rec,
355 msg_array->num_messages+1))) {
356 status = NT_STATUS_NO_MEMORY;
357 goto done;
360 rec[msg_array->num_messages].msg_version = MESSAGE_VERSION;
361 rec[msg_array->num_messages].msg_type = msg_type & MSG_TYPE_MASK;
362 rec[msg_array->num_messages].dest = pid;
363 rec[msg_array->num_messages].src = procid_self();
364 rec[msg_array->num_messages].buf = *data;
366 msg_array->messages = rec;
367 msg_array->num_messages += 1;
369 status = messaging_tdb_store(tdb->tdb, key, msg_array);
371 if (!NT_STATUS_IS_OK(status)) {
372 goto done;
375 status = message_notify(pid);
377 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
378 DEBUG(2, ("pid %s doesn't exist - deleting messages record\n",
379 procid_str_static(&pid)));
380 tdb_delete(tdb->tdb, message_key_pid(talloc_tos(), pid));
383 done:
384 tdb_chainunlock(tdb->tdb, key);
385 TALLOC_FREE(frame);
386 return status;
389 /****************************************************************************
390 Retrieve all messages for the current process.
391 ****************************************************************************/
393 static NTSTATUS retrieve_all_messages(TDB_CONTEXT *msg_tdb,
394 TALLOC_CTX *mem_ctx,
395 struct messaging_array **presult)
397 struct messaging_array *result;
398 TDB_DATA key = message_key_pid(mem_ctx, procid_self());
399 NTSTATUS status;
401 if (tdb_chainlock(msg_tdb, key) == -1) {
402 TALLOC_FREE(key.dptr);
403 return NT_STATUS_LOCK_NOT_GRANTED;
406 status = messaging_tdb_fetch(msg_tdb, key, mem_ctx, &result);
409 * We delete the record here, tdb_set_max_dead keeps it around
411 tdb_delete(msg_tdb, key);
412 tdb_chainunlock(msg_tdb, key);
414 if (NT_STATUS_IS_OK(status)) {
415 *presult = result;
418 TALLOC_FREE(key.dptr);
420 return status;
423 /****************************************************************************
424 Receive and dispatch any messages pending for this process.
425 JRA changed Dec 13 2006. Only one message handler now permitted per type.
426 *NOTE*: Dispatch functions must be able to cope with incoming
427 messages on an *odd* byte boundary.
428 ****************************************************************************/
430 static void message_dispatch(struct messaging_context *msg_ctx)
432 struct messaging_tdb_context *ctx = talloc_get_type(msg_ctx->local->private_data,
433 struct messaging_tdb_context);
434 struct messaging_array *msg_array = NULL;
435 struct tdb_wrap *tdb = ctx->tdb;
436 NTSTATUS status;
437 uint32 i;
439 if (ctx->received_messages == 0) {
440 return;
443 DEBUG(10, ("message_dispatch: received_messages = %d\n",
444 ctx->received_messages));
446 status = retrieve_all_messages(tdb->tdb, NULL, &msg_array);
447 if (!NT_STATUS_IS_OK(status)) {
448 DEBUG(0, ("message_dispatch: failed to retrieve messages: %s\n",
449 nt_errstr(status)));
450 return;
453 ctx->received_messages = 0;
455 for (i=0; i<msg_array->num_messages; i++) {
456 messaging_dispatch_rec(msg_ctx, &msg_array->messages[i]);
459 TALLOC_FREE(msg_array);
462 /** @} **/