check_parent_exists() can change errno. Ensure we preserve it across calls.
[Samba.git] / source3 / lib / messages_local.c
blobf82b107bcc5559877c508e91204d02b42dce20c0
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 "system/filesys.h"
47 #include "messages.h"
48 #include "lib/util/tdb_wrap.h"
50 struct messaging_tdb_context {
51 struct messaging_context *msg_ctx;
52 struct tdb_wrap *tdb;
53 struct tevent_signal *se;
54 int received_messages;
57 static NTSTATUS messaging_tdb_send(struct messaging_context *msg_ctx,
58 struct server_id pid, int msg_type,
59 const DATA_BLOB *data,
60 struct messaging_backend *backend);
61 static void message_dispatch(struct messaging_context *msg_ctx);
63 static void messaging_tdb_signal_handler(struct tevent_context *ev_ctx,
64 struct tevent_signal *se,
65 int signum, int count,
66 void *_info, void *private_data)
68 struct messaging_tdb_context *ctx = talloc_get_type(private_data,
69 struct messaging_tdb_context);
71 ctx->received_messages++;
73 DEBUG(10, ("messaging_tdb_signal_handler: sig[%d] count[%d] msgs[%d]\n",
74 signum, count, ctx->received_messages));
76 message_dispatch(ctx->msg_ctx);
79 /****************************************************************************
80 Initialise the messaging functions.
81 ****************************************************************************/
83 NTSTATUS messaging_tdb_init(struct messaging_context *msg_ctx,
84 TALLOC_CTX *mem_ctx,
85 struct messaging_backend **presult)
87 struct messaging_backend *result;
88 struct messaging_tdb_context *ctx;
90 if (!(result = TALLOC_P(mem_ctx, struct messaging_backend))) {
91 DEBUG(0, ("talloc failed\n"));
92 return NT_STATUS_NO_MEMORY;
95 ctx = TALLOC_ZERO_P(result, struct messaging_tdb_context);
96 if (!ctx) {
97 DEBUG(0, ("talloc failed\n"));
98 TALLOC_FREE(result);
99 return NT_STATUS_NO_MEMORY;
101 result->private_data = ctx;
102 result->send_fn = messaging_tdb_send;
104 ctx->msg_ctx = msg_ctx;
106 ctx->tdb = tdb_wrap_open(ctx, lock_path("messages.tdb"), 0,
107 TDB_CLEAR_IF_FIRST|TDB_DEFAULT|TDB_VOLATILE|TDB_INCOMPATIBLE_HASH,
108 O_RDWR|O_CREAT,0600);
110 if (!ctx->tdb) {
111 NTSTATUS status = map_nt_error_from_unix(errno);
112 DEBUG(2, ("ERROR: Failed to initialise messages database: "
113 "%s\n", strerror(errno)));
114 TALLOC_FREE(result);
115 return status;
118 ctx->se = tevent_add_signal(msg_ctx->event_ctx,
119 ctx,
120 SIGUSR1, 0,
121 messaging_tdb_signal_handler,
122 ctx);
123 if (!ctx->se) {
124 NTSTATUS status = map_nt_error_from_unix(errno);
125 DEBUG(0, ("ERROR: Failed to initialise messages signal handler: "
126 "%s\n", strerror(errno)));
127 TALLOC_FREE(result);
128 return status;
131 sec_init();
133 *presult = result;
134 return NT_STATUS_OK;
137 bool messaging_tdb_parent_init(TALLOC_CTX *mem_ctx)
139 struct tdb_wrap *db;
142 * Open the tdb in the parent process (smbd) so that our
143 * CLEAR_IF_FIRST optimization in tdb_reopen_all can properly
144 * work.
147 db = tdb_wrap_open(mem_ctx, lock_path("messages.tdb"), 0,
148 TDB_CLEAR_IF_FIRST|TDB_DEFAULT|TDB_VOLATILE|TDB_INCOMPATIBLE_HASH,
149 O_RDWR|O_CREAT,0600);
150 if (db == NULL) {
151 DEBUG(1, ("could not open messaging.tdb: %s\n",
152 strerror(errno)));
153 return false;
155 return true;
158 /*******************************************************************
159 Form a static tdb key from a pid.
160 ******************************************************************/
162 static TDB_DATA message_key_pid(TALLOC_CTX *mem_ctx, struct server_id pid)
164 char *key;
165 TDB_DATA kbuf;
167 key = talloc_asprintf(talloc_tos(), "PID/%s", procid_str_static(&pid));
169 SMB_ASSERT(key != NULL);
171 kbuf.dptr = (uint8 *)key;
172 kbuf.dsize = strlen(key)+1;
173 return kbuf;
177 Fetch the messaging array for a process
180 static NTSTATUS messaging_tdb_fetch(TDB_CONTEXT *msg_tdb,
181 TDB_DATA key,
182 TALLOC_CTX *mem_ctx,
183 struct messaging_array **presult)
185 struct messaging_array *result;
186 TDB_DATA data;
187 DATA_BLOB blob;
188 enum ndr_err_code ndr_err;
190 if (!(result = TALLOC_ZERO_P(mem_ctx, struct messaging_array))) {
191 return NT_STATUS_NO_MEMORY;
194 data = tdb_fetch(msg_tdb, key);
196 if (data.dptr == NULL) {
197 *presult = result;
198 return NT_STATUS_OK;
201 blob = data_blob_const(data.dptr, data.dsize);
203 ndr_err = ndr_pull_struct_blob(
204 &blob, result, result,
205 (ndr_pull_flags_fn_t)ndr_pull_messaging_array);
207 SAFE_FREE(data.dptr);
209 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
210 TALLOC_FREE(result);
211 return ndr_map_error2ntstatus(ndr_err);
214 if (DEBUGLEVEL >= 10) {
215 DEBUG(10, ("messaging_tdb_fetch:\n"));
216 NDR_PRINT_DEBUG(messaging_array, result);
219 *presult = result;
220 return NT_STATUS_OK;
224 Store a messaging array for a pid
227 static NTSTATUS messaging_tdb_store(TDB_CONTEXT *msg_tdb,
228 TDB_DATA key,
229 struct messaging_array *array)
231 TDB_DATA data;
232 DATA_BLOB blob;
233 enum ndr_err_code ndr_err;
234 TALLOC_CTX *mem_ctx;
235 int ret;
237 if (array->num_messages == 0) {
238 tdb_delete(msg_tdb, key);
239 return NT_STATUS_OK;
242 if (!(mem_ctx = talloc_new(array))) {
243 return NT_STATUS_NO_MEMORY;
246 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, array,
247 (ndr_push_flags_fn_t)ndr_push_messaging_array);
249 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
250 talloc_free(mem_ctx);
251 return ndr_map_error2ntstatus(ndr_err);
254 if (DEBUGLEVEL >= 10) {
255 DEBUG(10, ("messaging_tdb_store:\n"));
256 NDR_PRINT_DEBUG(messaging_array, array);
259 data.dptr = blob.data;
260 data.dsize = blob.length;
262 ret = tdb_store(msg_tdb, key, data, TDB_REPLACE);
263 TALLOC_FREE(mem_ctx);
265 return (ret == 0) ? NT_STATUS_OK : NT_STATUS_INTERNAL_DB_CORRUPTION;
268 /****************************************************************************
269 Notify a process that it has a message. If the process doesn't exist
270 then delete its record in the database.
271 ****************************************************************************/
273 static NTSTATUS message_notify(struct server_id procid)
275 pid_t pid = procid.pid;
276 int ret;
277 uid_t euid = geteuid();
280 * Doing kill with a non-positive pid causes messages to be
281 * sent to places we don't want.
284 SMB_ASSERT(pid > 0);
286 if (euid != 0) {
287 /* If we're not root become so to send the message. */
288 save_re_uid();
289 set_effective_uid(0);
292 ret = kill(pid, SIGUSR1);
294 if (euid != 0) {
295 /* Go back to who we were. */
296 int saved_errno = errno;
297 restore_re_uid_fromroot();
298 errno = saved_errno;
301 if (ret == 0) {
302 return NT_STATUS_OK;
306 * Something has gone wrong
309 DEBUG(2,("message to process %d failed - %s\n", (int)pid,
310 strerror(errno)));
313 * No call to map_nt_error_from_unix -- don't want to link in
314 * errormap.o into lots of utils.
317 if (errno == ESRCH) return NT_STATUS_INVALID_HANDLE;
318 if (errno == EINVAL) return NT_STATUS_INVALID_PARAMETER;
319 if (errno == EPERM) return NT_STATUS_ACCESS_DENIED;
320 return NT_STATUS_UNSUCCESSFUL;
323 /****************************************************************************
324 Send a message to a particular pid.
325 ****************************************************************************/
327 static NTSTATUS messaging_tdb_send(struct messaging_context *msg_ctx,
328 struct server_id pid, int msg_type,
329 const DATA_BLOB *data,
330 struct messaging_backend *backend)
332 struct messaging_tdb_context *ctx = talloc_get_type(backend->private_data,
333 struct messaging_tdb_context);
334 struct messaging_array *msg_array;
335 struct messaging_rec *rec;
336 NTSTATUS status;
337 TDB_DATA key;
338 struct tdb_wrap *tdb = ctx->tdb;
339 TALLOC_CTX *frame = talloc_stackframe();
341 /* NULL pointer means implicit length zero. */
342 if (!data->data) {
343 SMB_ASSERT(data->length == 0);
347 * Doing kill with a non-positive pid causes messages to be
348 * sent to places we don't want.
351 SMB_ASSERT(procid_to_pid(&pid) > 0);
353 key = message_key_pid(frame, pid);
355 if (tdb_chainlock(tdb->tdb, key) == -1) {
356 TALLOC_FREE(frame);
357 return NT_STATUS_LOCK_NOT_GRANTED;
360 status = messaging_tdb_fetch(tdb->tdb, key, talloc_tos(), &msg_array);
362 if (!NT_STATUS_IS_OK(status)) {
363 goto done;
366 if ((msg_type & MSG_FLAG_LOWPRIORITY)
367 && (msg_array->num_messages > 1000)) {
368 DEBUG(5, ("Dropping message for PID %s\n",
369 procid_str_static(&pid)));
370 status = NT_STATUS_INSUFFICIENT_RESOURCES;
371 goto done;
374 if (!(rec = TALLOC_REALLOC_ARRAY(talloc_tos(), msg_array->messages,
375 struct messaging_rec,
376 msg_array->num_messages+1))) {
377 status = NT_STATUS_NO_MEMORY;
378 goto done;
381 rec[msg_array->num_messages].msg_version = MESSAGE_VERSION;
382 rec[msg_array->num_messages].msg_type = msg_type & MSG_TYPE_MASK;
383 rec[msg_array->num_messages].dest = pid;
384 rec[msg_array->num_messages].src = msg_ctx->id;
385 rec[msg_array->num_messages].buf = *data;
387 msg_array->messages = rec;
388 msg_array->num_messages += 1;
390 status = messaging_tdb_store(tdb->tdb, key, msg_array);
392 if (!NT_STATUS_IS_OK(status)) {
393 goto done;
396 status = message_notify(pid);
398 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
399 DEBUG(2, ("pid %s doesn't exist - deleting messages record\n",
400 procid_str_static(&pid)));
401 tdb_delete(tdb->tdb, message_key_pid(talloc_tos(), pid));
404 done:
405 tdb_chainunlock(tdb->tdb, key);
406 TALLOC_FREE(frame);
407 return status;
410 /****************************************************************************
411 Retrieve all messages for a process.
412 ****************************************************************************/
414 static NTSTATUS retrieve_all_messages(TDB_CONTEXT *msg_tdb,
415 struct server_id id,
416 TALLOC_CTX *mem_ctx,
417 struct messaging_array **presult)
419 struct messaging_array *result;
420 TDB_DATA key = message_key_pid(mem_ctx, id);
421 NTSTATUS status;
423 if (tdb_chainlock(msg_tdb, key) == -1) {
424 TALLOC_FREE(key.dptr);
425 return NT_STATUS_LOCK_NOT_GRANTED;
428 status = messaging_tdb_fetch(msg_tdb, key, mem_ctx, &result);
431 * We delete the record here, tdb_set_max_dead keeps it around
433 tdb_delete(msg_tdb, key);
434 tdb_chainunlock(msg_tdb, key);
436 if (NT_STATUS_IS_OK(status)) {
437 *presult = result;
440 TALLOC_FREE(key.dptr);
442 return status;
445 /****************************************************************************
446 Receive and dispatch any messages pending for this process.
447 JRA changed Dec 13 2006. Only one message handler now permitted per type.
448 *NOTE*: Dispatch functions must be able to cope with incoming
449 messages on an *odd* byte boundary.
450 ****************************************************************************/
452 static void message_dispatch(struct messaging_context *msg_ctx)
454 struct messaging_tdb_context *ctx = talloc_get_type(msg_ctx->local->private_data,
455 struct messaging_tdb_context);
456 struct messaging_array *msg_array = NULL;
457 struct tdb_wrap *tdb = ctx->tdb;
458 NTSTATUS status;
459 uint32 i;
461 if (ctx->received_messages == 0) {
462 return;
465 DEBUG(10, ("message_dispatch: received_messages = %d\n",
466 ctx->received_messages));
468 status = retrieve_all_messages(tdb->tdb, msg_ctx->id, NULL, &msg_array);
469 if (!NT_STATUS_IS_OK(status)) {
470 DEBUG(0, ("message_dispatch: failed to retrieve messages: %s\n",
471 nt_errstr(status)));
472 return;
475 ctx->received_messages = 0;
477 for (i=0; i<msg_array->num_messages; i++) {
478 messaging_dispatch_rec(msg_ctx, &msg_array->messages[i]);
481 TALLOC_FREE(msg_array);
484 /** @} **/