s3: Fix some nonempty blank lines
[sfrench/samba-autobuild/.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"), 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
140 static 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
158 static 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
205 static 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
252 static 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
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)
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
393 static 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
430 static 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 /** @} **/