d535df1be27d70552ba8365a3fc9de70f0e41a23
[obnox/samba/samba-obnox.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 "system/filesys.h"
47 #include "messages.h"
48 #include "serverid.h"
49 #include "lib/tdb_wrap/tdb_wrap.h"
50 #include "lib/param/param.h"
51
52 struct messaging_tdb_context {
53         struct messaging_context *msg_ctx;
54         struct tdb_wrap *tdb;
55         struct tevent_signal *se;
56         int received_messages;
57         bool *have_context;
58 };
59
60 static NTSTATUS messaging_tdb_send(struct messaging_context *msg_ctx,
61                                    struct server_id pid, int msg_type,
62                                    const DATA_BLOB *data,
63                                    struct messaging_backend *backend);
64 static void message_dispatch(struct messaging_context *msg_ctx);
65
66 static void messaging_tdb_signal_handler(struct tevent_context *ev_ctx,
67                                          struct tevent_signal *se,
68                                          int signum, int count,
69                                          void *_info, void *private_data)
70 {
71         struct messaging_tdb_context *ctx = talloc_get_type(private_data,
72                                             struct messaging_tdb_context);
73
74         ctx->received_messages++;
75
76         DEBUG(10, ("messaging_tdb_signal_handler: sig[%d] count[%d] msgs[%d]\n",
77                    signum, count, ctx->received_messages));
78
79         message_dispatch(ctx->msg_ctx);
80 }
81
82 static int messaging_tdb_context_destructor(struct messaging_tdb_context *ctx);
83
84 /****************************************************************************
85  Initialise the messaging functions. 
86 ****************************************************************************/
87
88 NTSTATUS messaging_tdb_init(struct messaging_context *msg_ctx,
89                             TALLOC_CTX *mem_ctx,
90                             struct messaging_backend **presult)
91 {
92         struct messaging_backend *result;
93         struct messaging_tdb_context *ctx;
94         struct loadparm_context *lp_ctx;
95         static bool have_context = false;
96         const char *fname;
97
98         if (have_context) {
99                 DEBUG(0, ("No two messaging contexts per process\n"));
100                 return NT_STATUS_OBJECT_NAME_COLLISION;
101         }
102
103         if (!(result = talloc(mem_ctx, struct messaging_backend))) {
104                 DEBUG(0, ("talloc failed\n"));
105                 return NT_STATUS_NO_MEMORY;
106         }
107
108         lp_ctx = loadparm_init_s3(result, loadparm_s3_helpers());
109         if (lp_ctx == NULL) {
110                 DEBUG(0, ("loadparm_init_s3 failed\n"));
111                 TALLOC_FREE(result);
112                 return NT_STATUS_INTERNAL_ERROR;
113         }
114
115         ctx = talloc_zero(result, struct messaging_tdb_context);
116         if (!ctx) {
117                 DEBUG(0, ("talloc failed\n"));
118                 TALLOC_FREE(result);
119                 return NT_STATUS_NO_MEMORY;
120         }
121         result->private_data = ctx;
122         result->send_fn = messaging_tdb_send;
123
124         ctx->msg_ctx = msg_ctx;
125         ctx->have_context = &have_context;
126
127         fname = lock_path("messages.tdb");
128
129         ctx->tdb = tdb_wrap_open(
130                 ctx, fname, lpcfg_tdb_hash_size(lp_ctx, fname),
131                 lpcfg_tdb_flags(lp_ctx, TDB_CLEAR_IF_FIRST|TDB_DEFAULT|
132                                 TDB_VOLATILE| TDB_INCOMPATIBLE_HASH),
133                 O_RDWR|O_CREAT,0600);
134
135         talloc_unlink(result, lp_ctx);
136
137         if (!ctx->tdb) {
138                 NTSTATUS status = map_nt_error_from_unix(errno);
139                 DEBUG(2, ("ERROR: Failed to initialise messages database: "
140                           "%s\n", strerror(errno)));
141                 TALLOC_FREE(result);
142                 return status;
143         }
144
145         ctx->se = tevent_add_signal(msg_ctx->event_ctx,
146                                     ctx,
147                                     SIGUSR1, 0,
148                                     messaging_tdb_signal_handler,
149                                     ctx);
150         if (!ctx->se) {
151                 NTSTATUS status = map_nt_error_from_unix(errno);
152                 DEBUG(0, ("ERROR: Failed to initialise messages signal handler: "
153                           "%s\n", strerror(errno)));
154                 TALLOC_FREE(result);
155                 return status;
156         }
157
158         sec_init();
159
160         have_context = true;
161         talloc_set_destructor(ctx, messaging_tdb_context_destructor);
162
163         *presult = result;
164         return NT_STATUS_OK;
165 }
166
167 static int messaging_tdb_context_destructor(struct messaging_tdb_context *ctx)
168 {
169         SMB_ASSERT(*ctx->have_context);
170         *ctx->have_context = false;
171         return 0;
172 }
173
174 bool messaging_tdb_parent_init(TALLOC_CTX *mem_ctx)
175 {
176         struct tdb_wrap *db;
177         struct loadparm_context *lp_ctx;
178         const char *fname;
179
180         lp_ctx = loadparm_init_s3(mem_ctx, loadparm_s3_helpers());
181         if (lp_ctx == NULL) {
182                 DEBUG(0, ("loadparm_init_s3 failed\n"));
183                 return false;
184         }
185
186         /*
187          * Open the tdb in the parent process (smbd) so that our
188          * CLEAR_IF_FIRST optimization in tdb_reopen_all can properly
189          * work.
190          */
191
192         fname = lock_path("messages.tdb");
193         db = tdb_wrap_open(
194                 mem_ctx, fname, lpcfg_tdb_hash_size(lp_ctx, fname),
195                 lpcfg_tdb_flags(lp_ctx, TDB_CLEAR_IF_FIRST|TDB_DEFAULT|
196                                 TDB_VOLATILE|TDB_INCOMPATIBLE_HASH),
197                 O_RDWR|O_CREAT,0600);
198         talloc_unlink(mem_ctx, lp_ctx);
199         if (db == NULL) {
200                 DEBUG(1, ("could not open messaging.tdb: %s\n",
201                           strerror(errno)));
202                 return false;
203         }
204         return true;
205 }
206
207 /*******************************************************************
208  Form a static tdb key from a pid.
209 ******************************************************************/
210
211 static TDB_DATA message_key_pid(TALLOC_CTX *mem_ctx, struct server_id pid)
212 {
213         char *key;
214         TDB_DATA kbuf;
215
216         key = talloc_asprintf(mem_ctx, "PID/%s", procid_str_static(&pid));
217
218         SMB_ASSERT(key != NULL);
219
220         kbuf.dptr = (uint8 *)key;
221         kbuf.dsize = strlen(key)+1;
222         return kbuf;
223 }
224
225 /*******************************************************************
226  Called when a process has terminated abnormally. Remove all messages
227  pending for it.
228 ******************************************************************/
229
230 NTSTATUS messaging_tdb_cleanup(struct messaging_context *msg_ctx,
231                                 struct server_id pid)
232 {
233         struct messaging_tdb_context *ctx = talloc_get_type(
234                                         msg_ctx->local->private_data,
235                                         struct messaging_tdb_context);
236         struct tdb_wrap *tdb = ctx->tdb;
237         TDB_DATA key;
238         TALLOC_CTX *frame = talloc_stackframe();
239
240         key = message_key_pid(frame, pid);
241         /*
242          * We have to lock the key to avoid
243          * races in case the server_id was
244          * re-used and is active (a remote
245          * possibility, true). We only
246          * clean up the database if we
247          * know server_id doesn't exist
248          * while checked under the chainlock.
249          */
250         if (tdb_chainlock(tdb->tdb, key) != 0) {
251                 TALLOC_FREE(frame);
252                 return NT_STATUS_LOCK_NOT_GRANTED;
253         }
254         if (!serverid_exists(&pid)) {
255                 (void)tdb_delete(tdb->tdb, key);
256         }
257         tdb_chainunlock(tdb->tdb, key);
258         TALLOC_FREE(frame);
259         return NT_STATUS_OK;
260 }
261
262 /*
263   Fetch the messaging array for a process
264  */
265
266 static NTSTATUS messaging_tdb_fetch(TDB_CONTEXT *msg_tdb,
267                                     TDB_DATA key,
268                                     TALLOC_CTX *mem_ctx,
269                                     struct messaging_array **presult)
270 {
271         struct messaging_array *result;
272         TDB_DATA data;
273         DATA_BLOB blob;
274         enum ndr_err_code ndr_err;
275
276         if (!(result = talloc_zero(mem_ctx, struct messaging_array))) {
277                 return NT_STATUS_NO_MEMORY;
278         }
279
280         data = tdb_fetch(msg_tdb, key);
281
282         if (data.dptr == NULL) {
283                 *presult = result;
284                 return NT_STATUS_OK;
285         }
286
287         blob = data_blob_const(data.dptr, data.dsize);
288
289         ndr_err = ndr_pull_struct_blob_all(
290                 &blob, result, result,
291                 (ndr_pull_flags_fn_t)ndr_pull_messaging_array);
292
293         SAFE_FREE(data.dptr);
294
295         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
296                 TALLOC_FREE(result);
297                 return ndr_map_error2ntstatus(ndr_err);
298         }
299
300         if (DEBUGLEVEL >= 10) {
301                 DEBUG(10, ("messaging_tdb_fetch:\n"));
302                 NDR_PRINT_DEBUG(messaging_array, result);
303         }
304
305         *presult = result;
306         return NT_STATUS_OK;
307 }
308
309 /*
310   Store a messaging array for a pid
311 */
312
313 static NTSTATUS messaging_tdb_store(TDB_CONTEXT *msg_tdb,
314                                     TDB_DATA key,
315                                     struct messaging_array *array)
316 {
317         TDB_DATA data;
318         DATA_BLOB blob;
319         enum ndr_err_code ndr_err;
320         TALLOC_CTX *mem_ctx;
321         int ret;
322
323         if (array->num_messages == 0) {
324                 tdb_delete(msg_tdb, key);
325                 return NT_STATUS_OK;
326         }
327
328         if (!(mem_ctx = talloc_new(array))) {
329                 return NT_STATUS_NO_MEMORY;
330         }
331
332         ndr_err = ndr_push_struct_blob(&blob, mem_ctx, array,
333                 (ndr_push_flags_fn_t)ndr_push_messaging_array);
334
335         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
336                 talloc_free(mem_ctx);
337                 return ndr_map_error2ntstatus(ndr_err);
338         }
339
340         if (DEBUGLEVEL >= 10) {
341                 DEBUG(10, ("messaging_tdb_store:\n"));
342                 NDR_PRINT_DEBUG(messaging_array, array);
343         }
344
345         data.dptr = blob.data;
346         data.dsize = blob.length;
347
348         ret = tdb_store(msg_tdb, key, data, TDB_REPLACE);
349         TALLOC_FREE(mem_ctx);
350
351         return (ret == 0) ? NT_STATUS_OK : NT_STATUS_INTERNAL_DB_CORRUPTION;
352 }
353
354 /****************************************************************************
355  Notify a process that it has a message. If the process doesn't exist 
356  then delete its record in the database.
357 ****************************************************************************/
358
359 static NTSTATUS message_notify(struct server_id procid)
360 {
361         pid_t pid = procid.pid;
362         int ret;
363         uid_t euid = geteuid();
364
365         /*
366          * Doing kill with a non-positive pid causes messages to be
367          * sent to places we don't want.
368          */
369
370         SMB_ASSERT(pid > 0);
371         if (pid <= 0) {
372                 return NT_STATUS_INVALID_HANDLE;
373         }
374
375         if (euid != 0) {
376                 /* If we're not root become so to send the message. */
377                 save_re_uid();
378                 set_effective_uid(0);
379         }
380
381         ret = kill(pid, SIGUSR1);
382
383         if (euid != 0) {
384                 /* Go back to who we were. */
385                 int saved_errno = errno;
386                 restore_re_uid_fromroot();
387                 errno = saved_errno;
388         }
389
390         if (ret == 0) {
391                 return NT_STATUS_OK;
392         }
393
394         /*
395          * Something has gone wrong
396          */
397
398         DEBUG(2,("message to process %d failed - %s\n", (int)pid,
399                  strerror(errno)));
400
401         /*
402          * No call to map_nt_error_from_unix -- don't want to link in
403          * errormap.o into lots of utils.
404          */
405
406         if (errno == ESRCH)  return NT_STATUS_INVALID_HANDLE;
407         if (errno == EINVAL) return NT_STATUS_INVALID_PARAMETER;
408         if (errno == EPERM)  return NT_STATUS_ACCESS_DENIED;
409         return NT_STATUS_UNSUCCESSFUL;
410 }
411
412 /****************************************************************************
413  Send a message to a particular pid.
414 ****************************************************************************/
415
416 static NTSTATUS messaging_tdb_send(struct messaging_context *msg_ctx,
417                                    struct server_id pid, int msg_type,
418                                    const DATA_BLOB *data,
419                                    struct messaging_backend *backend)
420 {
421         struct messaging_tdb_context *ctx = talloc_get_type(backend->private_data,
422                                             struct messaging_tdb_context);
423         struct messaging_array *msg_array;
424         struct messaging_rec *rec;
425         NTSTATUS status;
426         TDB_DATA key;
427         struct tdb_wrap *tdb = ctx->tdb;
428         TALLOC_CTX *frame = talloc_stackframe();
429
430         /* NULL pointer means implicit length zero. */
431         if (!data->data) {
432                 SMB_ASSERT(data->length == 0);
433         }
434
435         /*
436          * Doing kill with a non-positive pid causes messages to be
437          * sent to places we don't want.
438          */
439
440         SMB_ASSERT(procid_to_pid(&pid) > 0);
441
442         key = message_key_pid(frame, pid);
443
444         if (tdb_chainlock(tdb->tdb, key) != 0) {
445                 TALLOC_FREE(frame);
446                 return NT_STATUS_LOCK_NOT_GRANTED;
447         }
448
449         status = messaging_tdb_fetch(tdb->tdb, key, frame, &msg_array);
450
451         if (!NT_STATUS_IS_OK(status)) {
452                 goto done;
453         }
454
455         if ((msg_type & MSG_FLAG_LOWPRIORITY)
456             && (msg_array->num_messages > 1000)) {
457                 DEBUG(5, ("Dropping message for PID %s\n",
458                           procid_str_static(&pid)));
459                 status = NT_STATUS_INSUFFICIENT_RESOURCES;
460                 goto done;
461         }
462
463         if (!(rec = talloc_realloc(frame, msg_array->messages,
464                                          struct messaging_rec,
465                                          msg_array->num_messages+1))) {
466                 status = NT_STATUS_NO_MEMORY;
467                 goto done;
468         }
469
470         rec[msg_array->num_messages].msg_version = MESSAGE_VERSION;
471         rec[msg_array->num_messages].msg_type = msg_type & MSG_TYPE_MASK;
472         rec[msg_array->num_messages].dest = pid;
473         rec[msg_array->num_messages].src = msg_ctx->id;
474         rec[msg_array->num_messages].buf = *data;
475
476         msg_array->messages = rec;
477         msg_array->num_messages += 1;
478
479         status = messaging_tdb_store(tdb->tdb, key, msg_array);
480
481         if (!NT_STATUS_IS_OK(status)) {
482                 goto done;
483         }
484
485         status = message_notify(pid);
486
487         if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
488                 DEBUG(2, ("pid %s doesn't exist - deleting messages record\n",
489                           procid_str_static(&pid)));
490                 tdb_delete(tdb->tdb, message_key_pid(frame, pid));
491         }
492
493  done:
494         tdb_chainunlock(tdb->tdb, key);
495         TALLOC_FREE(frame);
496         return status;
497 }
498
499 /****************************************************************************
500  Retrieve all messages for a process.
501 ****************************************************************************/
502
503 static NTSTATUS retrieve_all_messages(TDB_CONTEXT *msg_tdb,
504                                       struct server_id id,
505                                       TALLOC_CTX *mem_ctx,
506                                       struct messaging_array **presult)
507 {
508         struct messaging_array *result;
509         TDB_DATA key = message_key_pid(mem_ctx, id);
510         NTSTATUS status;
511
512         if (tdb_chainlock(msg_tdb, key) != 0) {
513                 TALLOC_FREE(key.dptr);
514                 return NT_STATUS_LOCK_NOT_GRANTED;
515         }
516
517         status = messaging_tdb_fetch(msg_tdb, key, mem_ctx, &result);
518
519         /*
520          * We delete the record here, tdb_set_max_dead keeps it around
521          */
522         tdb_delete(msg_tdb, key);
523         tdb_chainunlock(msg_tdb, key);
524
525         if (NT_STATUS_IS_OK(status)) {
526                 *presult = result;
527         }
528
529         TALLOC_FREE(key.dptr);
530
531         return status;
532 }
533
534 /****************************************************************************
535  Receive and dispatch any messages pending for this process.
536  JRA changed Dec 13 2006. Only one message handler now permitted per type.
537  *NOTE*: Dispatch functions must be able to cope with incoming
538  messages on an *odd* byte boundary.
539 ****************************************************************************/
540
541 static void message_dispatch(struct messaging_context *msg_ctx)
542 {
543         struct messaging_tdb_context *ctx = talloc_get_type(msg_ctx->local->private_data,
544                                             struct messaging_tdb_context);
545         struct messaging_array *msg_array = NULL;
546         struct tdb_wrap *tdb = ctx->tdb;
547         NTSTATUS status;
548         uint32 i;
549
550         if (ctx->received_messages == 0) {
551                 return;
552         }
553
554         DEBUG(10, ("message_dispatch: received_messages = %d\n",
555                    ctx->received_messages));
556
557         status = retrieve_all_messages(tdb->tdb, msg_ctx->id, NULL, &msg_array);
558         if (!NT_STATUS_IS_OK(status)) {
559                 DEBUG(0, ("message_dispatch: failed to retrieve messages: %s\n",
560                            nt_errstr(status)));
561                 return;
562         }
563
564         ctx->received_messages = 0;
565
566         for (i=0; i<msg_array->num_messages; i++) {
567                 messaging_dispatch_rec(msg_ctx, &msg_array->messages[i]);
568         }
569
570         TALLOC_FREE(msg_array);
571 }
572
573 /** @} **/