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
49struct messaging_tdb_context {
50	struct messaging_context *msg_ctx;
51	struct tdb_wrap *tdb;
52	struct tevent_signal *se;
53	int received_messages;
54};
55
56static 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);
60static void message_dispatch(struct messaging_context *msg_ctx);
61
62static 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)
66{
67	struct messaging_tdb_context *ctx = talloc_get_type(private_data,
68					    struct messaging_tdb_context);
69
70	ctx->received_messages++;
71
72	DEBUG(10, ("messaging_tdb_signal_handler: sig[%d] count[%d] msgs[%d]\n",
73		   signum, count, ctx->received_messages));
74
75	message_dispatch(ctx->msg_ctx);
76}
77
78/****************************************************************************
79 Initialise the messaging functions.
80****************************************************************************/
81
82NTSTATUS messaging_tdb_init(struct messaging_context *msg_ctx,
83			    TALLOC_CTX *mem_ctx,
84			    struct messaging_backend **presult)
85{
86	struct messaging_backend *result;
87	struct messaging_tdb_context *ctx;
88
89	if (!(result = TALLOC_P(mem_ctx, struct messaging_backend))) {
90		DEBUG(0, ("talloc failed\n"));
91		return NT_STATUS_NO_MEMORY;
92	}
93
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;
99	}
100	result->private_data = ctx;
101	result->send_fn = messaging_tdb_send;
102
103	ctx->msg_ctx = msg_ctx;
104
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);
108
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;
115	}
116
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;
128	}
129
130	sec_init();
131
132	*presult = result;
133	return NT_STATUS_OK;
134}
135
136/*******************************************************************
137 Form a static tdb key from a pid.
138******************************************************************/
139
140static TDB_DATA message_key_pid(TALLOC_CTX *mem_ctx, struct server_id pid)
141{
142	char *key;
143	TDB_DATA kbuf;
144
145	key = talloc_asprintf(talloc_tos(), "PID/%s", procid_str_static(&pid));
146
147	SMB_ASSERT(key != NULL);
148
149	kbuf.dptr = (uint8 *)key;
150	kbuf.dsize = strlen(key)+1;
151	return kbuf;
152}
153
154/*
155  Fetch the messaging array for a process
156 */
157
158static NTSTATUS messaging_tdb_fetch(TDB_CONTEXT *msg_tdb,
159				    TDB_DATA key,
160				    TALLOC_CTX *mem_ctx,
161				    struct messaging_array **presult)
162{
163	struct messaging_array *result;
164	TDB_DATA data;
165	DATA_BLOB blob;
166	enum ndr_err_code ndr_err;
167
168	if (!(result = TALLOC_ZERO_P(mem_ctx, struct messaging_array))) {
169		return NT_STATUS_NO_MEMORY;
170	}
171
172	data = tdb_fetch(msg_tdb, key);
173
174	if (data.dptr == NULL) {
175		*presult = result;
176		return NT_STATUS_OK;
177	}
178
179	blob = data_blob_const(data.dptr, data.dsize);
180
181	ndr_err = ndr_pull_struct_blob(
182		&blob, result, NULL, result,
183		(ndr_pull_flags_fn_t)ndr_pull_messaging_array);
184
185	SAFE_FREE(data.dptr);
186
187	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
188		TALLOC_FREE(result);
189		return ndr_map_error2ntstatus(ndr_err);
190	}
191
192	if (DEBUGLEVEL >= 10) {
193		DEBUG(10, ("messaging_tdb_fetch:\n"));
194		NDR_PRINT_DEBUG(messaging_array, result);
195	}
196
197	*presult = result;
198	return NT_STATUS_OK;
199}
200
201/*
202  Store a messaging array for a pid
203*/
204
205static NTSTATUS messaging_tdb_store(TDB_CONTEXT *msg_tdb,
206				    TDB_DATA key,
207				    struct messaging_array *array)
208{
209	TDB_DATA data;
210	DATA_BLOB blob;
211	enum ndr_err_code ndr_err;
212	TALLOC_CTX *mem_ctx;
213	int ret;
214
215	if (array->num_messages == 0) {
216		tdb_delete(msg_tdb, key);
217		return NT_STATUS_OK;
218	}
219
220	if (!(mem_ctx = talloc_new(array))) {
221		return NT_STATUS_NO_MEMORY;
222	}
223
224	ndr_err = ndr_push_struct_blob(
225		&blob, mem_ctx, NULL, array,
226		(ndr_push_flags_fn_t)ndr_push_messaging_array);
227
228	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
229		talloc_free(mem_ctx);
230		return ndr_map_error2ntstatus(ndr_err);
231	}
232
233	if (DEBUGLEVEL >= 10) {
234		DEBUG(10, ("messaging_tdb_store:\n"));
235		NDR_PRINT_DEBUG(messaging_array, array);
236	}
237
238	data.dptr = blob.data;
239	data.dsize = blob.length;
240
241	ret = tdb_store(msg_tdb, key, data, TDB_REPLACE);
242	TALLOC_FREE(mem_ctx);
243
244	return (ret == 0) ? NT_STATUS_OK : NT_STATUS_INTERNAL_DB_CORRUPTION;
245}
246
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****************************************************************************/
251
252static NTSTATUS message_notify(struct server_id procid)
253{
254	pid_t pid = procid.pid;
255	int ret;
256	uid_t euid = geteuid();
257
258	/*
259	 * Doing kill with a non-positive pid causes messages to be
260	 * sent to places we don't want.
261	 */
262
263	SMB_ASSERT(pid > 0);
264
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);
269	}
270
271	ret = kill(pid, SIGUSR1);
272
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;
278	}
279
280	if (ret == 0) {
281		return NT_STATUS_OK;
282	}
283
284	/*
285	 * Something has gone wrong
286	 */
287
288	DEBUG(2,("message to process %d failed - %s\n", (int)pid,
289		 strerror(errno)));
290
291	/*
292	 * No call to map_nt_error_from_unix -- don't want to link in
293	 * errormap.o into lots of utils.
294	 */
295
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;
300}
301
302/****************************************************************************
303 Send a message to a particular pid.
304****************************************************************************/
305
306static 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)
310{
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();
319
320	/* NULL pointer means implicit length zero. */
321	if (!data->data) {
322		SMB_ASSERT(data->length == 0);
323	}
324
325	/*
326	 * Doing kill with a non-positive pid causes messages to be
327	 * sent to places we don't want.
328	 */
329
330	SMB_ASSERT(procid_to_pid(&pid) > 0);
331
332	key = message_key_pid(frame, pid);
333
334	if (tdb_chainlock(tdb->tdb, key) == -1) {
335		TALLOC_FREE(frame);
336		return NT_STATUS_LOCK_NOT_GRANTED;
337	}
338
339	status = messaging_tdb_fetch(tdb->tdb, key, talloc_tos(), &msg_array);
340
341	if (!NT_STATUS_IS_OK(status)) {
342		goto done;
343	}
344
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;
351	}
352
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;
358	}
359
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;
365
366	msg_array->messages = rec;
367	msg_array->num_messages += 1;
368
369	status = messaging_tdb_store(tdb->tdb, key, msg_array);
370
371	if (!NT_STATUS_IS_OK(status)) {
372		goto done;
373	}
374
375	status = message_notify(pid);
376
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));
381	}
382
383 done:
384	tdb_chainunlock(tdb->tdb, key);
385	TALLOC_FREE(frame);
386	return status;
387}
388
389/****************************************************************************
390 Retrieve all messages for the current process.
391****************************************************************************/
392
393static NTSTATUS retrieve_all_messages(TDB_CONTEXT *msg_tdb,
394				      TALLOC_CTX *mem_ctx,
395				      struct messaging_array **presult)
396{
397	struct messaging_array *result;
398	TDB_DATA key = message_key_pid(mem_ctx, procid_self());
399	NTSTATUS status;
400
401	if (tdb_chainlock(msg_tdb, key) == -1) {
402		TALLOC_FREE(key.dptr);
403		return NT_STATUS_LOCK_NOT_GRANTED;
404	}
405
406	status = messaging_tdb_fetch(msg_tdb, key, mem_ctx, &result);
407
408	/*
409	 * We delete the record here, tdb_set_max_dead keeps it around
410	 */
411	tdb_delete(msg_tdb, key);
412	tdb_chainunlock(msg_tdb, key);
413
414	if (NT_STATUS_IS_OK(status)) {
415		*presult = result;
416	}
417
418	TALLOC_FREE(key.dptr);
419
420	return status;
421}
422
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****************************************************************************/
429
430static void message_dispatch(struct messaging_context *msg_ctx)
431{
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;
438
439	if (ctx->received_messages == 0) {
440		return;
441	}
442
443	DEBUG(10, ("message_dispatch: received_messages = %d\n",
444		   ctx->received_messages));
445
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;
451	}
452
453	ctx->received_messages = 0;
454
455	for (i=0; i<msg_array->num_messages; i++) {
456		messaging_dispatch_rec(msg_ctx, &msg_array->messages[i]);
457	}
458
459	TALLOC_FREE(msg_array);
460}
461
462/** @} **/
463