be848ac8baca93903076bcfc888b4b19150a1bd0
[ira/wip.git] / source3 / lib / messages_local.c
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
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;
54 };
55
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);
61
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)
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
82 NTSTATUS 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"),
106                                  0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT,
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         /* Activate the per-hashchain freelist */
133         tdb_set_max_dead(ctx->tdb->tdb, 5);
134
135         *presult = result;
136         return NT_STATUS_OK;
137 }
138
139 /*******************************************************************
140  Form a static tdb key from a pid.
141 ******************************************************************/
142
143 static TDB_DATA message_key_pid(TALLOC_CTX *mem_ctx, struct server_id pid)
144 {
145         char *key;
146         TDB_DATA kbuf;
147
148         key = talloc_asprintf(talloc_tos(), "PID/%s", procid_str_static(&pid));
149
150         SMB_ASSERT(key != NULL);
151         
152         kbuf.dptr = (uint8 *)key;
153         kbuf.dsize = strlen(key)+1;
154         return kbuf;
155 }
156
157 /*
158   Fetch the messaging array for a process
159  */
160
161 static NTSTATUS messaging_tdb_fetch(TDB_CONTEXT *msg_tdb,
162                                     TDB_DATA key,
163                                     TALLOC_CTX *mem_ctx,
164                                     struct messaging_array **presult)
165 {
166         struct messaging_array *result;
167         TDB_DATA data;
168         DATA_BLOB blob;
169         enum ndr_err_code ndr_err;
170
171         if (!(result = TALLOC_ZERO_P(mem_ctx, struct messaging_array))) {
172                 return NT_STATUS_NO_MEMORY;
173         }
174
175         data = tdb_fetch(msg_tdb, key);
176
177         if (data.dptr == NULL) {
178                 *presult = result;
179                 return NT_STATUS_OK;
180         }
181
182         blob = data_blob_const(data.dptr, data.dsize);
183
184         ndr_err = ndr_pull_struct_blob(
185                 &blob, result, NULL, result,
186                 (ndr_pull_flags_fn_t)ndr_pull_messaging_array);
187
188         SAFE_FREE(data.dptr);
189
190         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
191                 TALLOC_FREE(result);
192                 return ndr_map_error2ntstatus(ndr_err);
193         }
194
195         if (DEBUGLEVEL >= 10) {
196                 DEBUG(10, ("messaging_tdb_fetch:\n"));
197                 NDR_PRINT_DEBUG(messaging_array, result);
198         }
199
200         *presult = result;
201         return NT_STATUS_OK;
202 }
203
204 /*
205   Store a messaging array for a pid
206 */
207
208 static NTSTATUS messaging_tdb_store(TDB_CONTEXT *msg_tdb,
209                                     TDB_DATA key,
210                                     struct messaging_array *array)
211 {
212         TDB_DATA data;
213         DATA_BLOB blob;
214         enum ndr_err_code ndr_err;
215         TALLOC_CTX *mem_ctx;
216         int ret;
217
218         if (array->num_messages == 0) {
219                 tdb_delete(msg_tdb, key);
220                 return NT_STATUS_OK;
221         }
222
223         if (!(mem_ctx = talloc_new(array))) {
224                 return NT_STATUS_NO_MEMORY;
225         }
226
227         ndr_err = ndr_push_struct_blob(
228                 &blob, mem_ctx, NULL, array,
229                 (ndr_push_flags_fn_t)ndr_push_messaging_array);
230
231         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
232                 talloc_free(mem_ctx);
233                 return ndr_map_error2ntstatus(ndr_err);
234         }
235
236         if (DEBUGLEVEL >= 10) {
237                 DEBUG(10, ("messaging_tdb_store:\n"));
238                 NDR_PRINT_DEBUG(messaging_array, array);
239         }
240
241         data.dptr = blob.data;
242         data.dsize = blob.length;
243
244         ret = tdb_store(msg_tdb, key, data, TDB_REPLACE);
245         TALLOC_FREE(mem_ctx);
246
247         return (ret == 0) ? NT_STATUS_OK : NT_STATUS_INTERNAL_DB_CORRUPTION;
248 }
249
250 /****************************************************************************
251  Notify a process that it has a message. If the process doesn't exist 
252  then delete its record in the database.
253 ****************************************************************************/
254
255 static NTSTATUS message_notify(struct server_id procid)
256 {
257         pid_t pid = procid.pid;
258         int ret;
259         uid_t euid = geteuid();
260
261         /*
262          * Doing kill with a non-positive pid causes messages to be
263          * sent to places we don't want.
264          */
265
266         SMB_ASSERT(pid > 0);
267
268         if (euid != 0) {
269                 /* If we're not root become so to send the message. */
270                 save_re_uid();
271                 set_effective_uid(0);
272         }
273
274         ret = kill(pid, SIGUSR1);
275
276         if (euid != 0) {
277                 /* Go back to who we were. */
278                 int saved_errno = errno;
279                 restore_re_uid_fromroot();
280                 errno = saved_errno;
281         }
282
283         if (ret == 0) {
284                 return NT_STATUS_OK;
285         }
286
287         /*
288          * Something has gone wrong
289          */
290
291         DEBUG(2,("message to process %d failed - %s\n", (int)pid,
292                  strerror(errno)));
293
294         /*
295          * No call to map_nt_error_from_unix -- don't want to link in
296          * errormap.o into lots of utils.
297          */
298
299         if (errno == ESRCH)  return NT_STATUS_INVALID_HANDLE;
300         if (errno == EINVAL) return NT_STATUS_INVALID_PARAMETER;
301         if (errno == EPERM)  return NT_STATUS_ACCESS_DENIED;
302         return NT_STATUS_UNSUCCESSFUL;
303 }
304
305 /****************************************************************************
306  Send a message to a particular pid.
307 ****************************************************************************/
308
309 static NTSTATUS messaging_tdb_send(struct messaging_context *msg_ctx,
310                                    struct server_id pid, int msg_type,
311                                    const DATA_BLOB *data,
312                                    struct messaging_backend *backend)
313 {
314         struct messaging_tdb_context *ctx = talloc_get_type(backend->private_data,
315                                             struct messaging_tdb_context);
316         struct messaging_array *msg_array;
317         struct messaging_rec *rec;
318         NTSTATUS status;
319         TDB_DATA key;
320         struct tdb_wrap *tdb = ctx->tdb;
321         TALLOC_CTX *frame = talloc_stackframe();
322
323         /* NULL pointer means implicit length zero. */
324         if (!data->data) {
325                 SMB_ASSERT(data->length == 0);
326         }
327
328         /*
329          * Doing kill with a non-positive pid causes messages to be
330          * sent to places we don't want.
331          */
332
333         SMB_ASSERT(procid_to_pid(&pid) > 0);
334
335         key = message_key_pid(frame, pid);
336
337         if (tdb_chainlock(tdb->tdb, key) == -1) {
338                 TALLOC_FREE(frame);
339                 return NT_STATUS_LOCK_NOT_GRANTED;
340         }
341
342         status = messaging_tdb_fetch(tdb->tdb, key, talloc_tos(), &msg_array);
343
344         if (!NT_STATUS_IS_OK(status)) {
345                 goto done;
346         }
347
348         if ((msg_type & MSG_FLAG_LOWPRIORITY)
349             && (msg_array->num_messages > 1000)) {
350                 DEBUG(5, ("Dropping message for PID %s\n",
351                           procid_str_static(&pid)));
352                 status = NT_STATUS_INSUFFICIENT_RESOURCES;
353                 goto done;
354         }
355
356         if (!(rec = TALLOC_REALLOC_ARRAY(talloc_tos(), msg_array->messages,
357                                          struct messaging_rec,
358                                          msg_array->num_messages+1))) {
359                 status = NT_STATUS_NO_MEMORY;
360                 goto done;
361         }
362
363         rec[msg_array->num_messages].msg_version = MESSAGE_VERSION;
364         rec[msg_array->num_messages].msg_type = msg_type & MSG_TYPE_MASK;
365         rec[msg_array->num_messages].dest = pid;
366         rec[msg_array->num_messages].src = procid_self();
367         rec[msg_array->num_messages].buf = *data;
368
369         msg_array->messages = rec;
370         msg_array->num_messages += 1;
371
372         status = messaging_tdb_store(tdb->tdb, key, msg_array);
373
374         if (!NT_STATUS_IS_OK(status)) {
375                 goto done;
376         }
377         
378         status = message_notify(pid);
379
380         if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
381                 DEBUG(2, ("pid %s doesn't exist - deleting messages record\n",
382                           procid_str_static(&pid)));
383                 tdb_delete(tdb->tdb, message_key_pid(talloc_tos(), pid));
384         }
385
386  done:
387         tdb_chainunlock(tdb->tdb, key);
388         TALLOC_FREE(frame);
389         return status;
390 }
391
392 /****************************************************************************
393  Retrieve all messages for the current process.
394 ****************************************************************************/
395
396 static NTSTATUS retrieve_all_messages(TDB_CONTEXT *msg_tdb,
397                                       TALLOC_CTX *mem_ctx,
398                                       struct messaging_array **presult)
399 {
400         struct messaging_array *result;
401         TDB_DATA key = message_key_pid(mem_ctx, procid_self());
402         NTSTATUS status;
403
404         if (tdb_chainlock(msg_tdb, key) == -1) {
405                 TALLOC_FREE(key.dptr);
406                 return NT_STATUS_LOCK_NOT_GRANTED;
407         }
408
409         status = messaging_tdb_fetch(msg_tdb, key, mem_ctx, &result);
410
411         /*
412          * We delete the record here, tdb_set_max_dead keeps it around
413          */
414         tdb_delete(msg_tdb, key);
415         tdb_chainunlock(msg_tdb, key);
416
417         if (NT_STATUS_IS_OK(status)) {
418                 *presult = result;
419         }
420
421         TALLOC_FREE(key.dptr);
422
423         return status;
424 }
425
426 /****************************************************************************
427  Receive and dispatch any messages pending for this process.
428  JRA changed Dec 13 2006. Only one message handler now permitted per type.
429  *NOTE*: Dispatch functions must be able to cope with incoming
430  messages on an *odd* byte boundary.
431 ****************************************************************************/
432
433 static void message_dispatch(struct messaging_context *msg_ctx)
434 {
435         struct messaging_tdb_context *ctx = talloc_get_type(msg_ctx->local->private_data,
436                                             struct messaging_tdb_context);
437         struct messaging_array *msg_array = NULL;
438         struct tdb_wrap *tdb = ctx->tdb;
439         NTSTATUS status;
440         uint32 i;
441
442         if (ctx->received_messages == 0) {
443                 return;
444         }
445
446         DEBUG(10, ("message_dispatch: received_messages = %d\n",
447                    ctx->received_messages));
448
449         status = retrieve_all_messages(tdb->tdb, NULL, &msg_array);
450         if (!NT_STATUS_IS_OK(status)) {
451                 DEBUG(0, ("message_dispatch: failed to retrieve messages: %s\n",
452                            nt_errstr(status)));
453                 return;
454         }
455
456         ctx->received_messages = 0;
457
458         for (i=0; i<msg_array->num_messages; i++) {
459                 messaging_dispatch_rec(msg_ctx, &msg_array->messages[i]);
460         }
461
462         TALLOC_FREE(msg_array);
463 }
464
465 /** @} **/