source: branches/samba-3.5.x/source4/lib/messaging/pymessaging.c

Last change on this file was 414, checked in by Herwig Bauernfeind, 15 years ago

Samba 3.5.0: Initial import

File size: 15.7 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3 Copyright © Jelmer Vernooij <jelmer@samba.org> 2008
4
5 Based on the equivalent for EJS:
6 Copyright © Andrew Tridgell <tridge@samba.org> 2005
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
20*/
21
22#include "includes.h"
23#include <Python.h>
24#include "scripting/python/modules.h"
25#include "libcli/util/pyerrors.h"
26#include "librpc/rpc/pyrpc.h"
27#include "lib/messaging/irpc.h"
28#include "lib/messaging/messaging.h"
29#include "lib/events/events.h"
30#include "cluster/cluster.h"
31#include "param/param.h"
32#include "param/pyparam.h"
33
34#ifndef Py_RETURN_NONE
35#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
36#endif
37
38PyAPI_DATA(PyTypeObject) messaging_Type;
39PyAPI_DATA(PyTypeObject) irpc_ClientConnectionType;
40
41/* FIXME: This prototype should be in py_irpc.h, or shared otherwise */
42extern const struct PyNdrRpcMethodDef py_ndr_irpc_methods[];
43
44static bool server_id_from_py(PyObject *object, struct server_id *server_id)
45{
46 if (!PyTuple_Check(object)) {
47 PyErr_SetString(PyExc_ValueError, "Expected tuple");
48 return false;
49 }
50
51 if (PyTuple_Size(object) == 3) {
52 return PyArg_ParseTuple(object, "iii", &server_id->id, &server_id->id2, &server_id->node);
53 } else {
54 int id, id2;
55 if (!PyArg_ParseTuple(object, "ii", &id, &id2))
56 return false;
57 *server_id = cluster_id(id, id2);
58 return true;
59 }
60}
61
62typedef struct {
63 PyObject_HEAD
64 TALLOC_CTX *mem_ctx;
65 struct messaging_context *msg_ctx;
66} messaging_Object;
67
68PyObject *py_messaging_connect(PyTypeObject *self, PyObject *args, PyObject *kwargs)
69{
70 struct tevent_context *ev;
71 const char *kwnames[] = { "own_id", "messaging_path", NULL };
72 PyObject *own_id = Py_None;
73 const char *messaging_path = NULL;
74 messaging_Object *ret;
75
76 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Oz:connect",
77 discard_const_p(char *, kwnames), &own_id, &messaging_path)) {
78 return NULL;
79 }
80
81 ret = PyObject_New(messaging_Object, &messaging_Type);
82 if (ret == NULL)
83 return NULL;
84
85 ret->mem_ctx = talloc_new(NULL);
86
87 ev = s4_event_context_init(ret->mem_ctx);
88
89 if (messaging_path == NULL) {
90 messaging_path = lp_messaging_path(ret->mem_ctx,
91 py_default_loadparm_context(ret->mem_ctx));
92 } else {
93 messaging_path = talloc_strdup(ret->mem_ctx, messaging_path);
94 }
95
96 if (own_id != Py_None) {
97 struct server_id server_id;
98
99 if (!server_id_from_py(own_id, &server_id))
100 return NULL;
101
102 ret->msg_ctx = messaging_init(ret->mem_ctx,
103 messaging_path,
104 server_id,
105 py_iconv_convenience(ret->mem_ctx),
106 ev);
107 } else {
108 ret->msg_ctx = messaging_client_init(ret->mem_ctx,
109 messaging_path,
110 py_iconv_convenience(ret->mem_ctx),
111 ev);
112 }
113
114 if (ret->msg_ctx == NULL) {
115 PyErr_SetString(PyExc_RuntimeError, "messaging_connect unable to create a messaging context");
116 talloc_free(ret->mem_ctx);
117 return NULL;
118 }
119
120 return (PyObject *)ret;
121}
122
123static void py_messaging_dealloc(PyObject *self)
124{
125 messaging_Object *iface = (messaging_Object *)self;
126 talloc_free(iface->msg_ctx);
127 PyObject_Del(self);
128}
129
130static PyObject *py_messaging_send(PyObject *self, PyObject *args, PyObject *kwargs)
131{
132 messaging_Object *iface = (messaging_Object *)self;
133 uint32_t msg_type;
134 DATA_BLOB data;
135 PyObject *target;
136 NTSTATUS status;
137 struct server_id server;
138 const char *kwnames[] = { "target", "msg_type", "data", NULL };
139 int length;
140
141 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Ois#|:send",
142 discard_const_p(char *, kwnames), &target, &msg_type, &data.data, &length)) {
143 return NULL;
144 }
145
146 data.length = length;
147
148 if (!server_id_from_py(target, &server))
149 return NULL;
150
151 status = messaging_send(iface->msg_ctx, server, msg_type, &data);
152 if (NT_STATUS_IS_ERR(status)) {
153 PyErr_SetNTSTATUS(status);
154 return NULL;
155 }
156
157 Py_RETURN_NONE;
158}
159
160static void py_msg_callback_wrapper(struct messaging_context *msg, void *private_data,
161 uint32_t msg_type,
162 struct server_id server_id, DATA_BLOB *data)
163{
164 PyObject *callback = (PyObject *)private_data;
165
166 PyObject_CallFunction(callback, discard_const_p(char, "i(iii)s#"), msg_type,
167 server_id.id, server_id.id2, server_id.node,
168 data->data, data->length);
169}
170
171static PyObject *py_messaging_register(PyObject *self, PyObject *args, PyObject *kwargs)
172{
173 messaging_Object *iface = (messaging_Object *)self;
174 int msg_type = -1;
175 PyObject *callback;
176 NTSTATUS status;
177 const char *kwnames[] = { "callback", "msg_type", NULL };
178
179 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|i:send",
180 discard_const_p(char *, kwnames), &callback, &msg_type)) {
181 return NULL;
182 }
183
184 Py_INCREF(callback);
185
186 if (msg_type == -1) {
187 uint32_t msg_type32 = msg_type;
188 status = messaging_register_tmp(iface->msg_ctx, callback,
189 py_msg_callback_wrapper, &msg_type32);
190 msg_type = msg_type32;
191 } else {
192 status = messaging_register(iface->msg_ctx, callback,
193 msg_type, py_msg_callback_wrapper);
194 }
195 if (NT_STATUS_IS_ERR(status)) {
196 PyErr_SetNTSTATUS(status);
197 return NULL;
198 }
199
200 return PyLong_FromLong(msg_type);
201}
202
203static PyObject *py_messaging_deregister(PyObject *self, PyObject *args, PyObject *kwargs)
204{
205 messaging_Object *iface = (messaging_Object *)self;
206 int msg_type = -1;
207 PyObject *callback;
208 const char *kwnames[] = { "callback", "msg_type", NULL };
209
210 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|i:send",
211 discard_const_p(char *, kwnames), &callback, &msg_type)) {
212 return NULL;
213 }
214
215 messaging_deregister(iface->msg_ctx, msg_type, callback);
216
217 Py_DECREF(callback);
218
219 Py_RETURN_NONE;
220}
221
222static PyObject *py_messaging_add_name(PyObject *self, PyObject *args, PyObject *kwargs)
223{
224 messaging_Object *iface = (messaging_Object *)self;
225 NTSTATUS status;
226 char *name;
227 const char *kwnames[] = { "name", NULL };
228
229 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|:send",
230 discard_const_p(char *, kwnames), &name)) {
231 return NULL;
232 }
233
234 status = irpc_add_name(iface->msg_ctx, name);
235 if (NT_STATUS_IS_ERR(status)) {
236 PyErr_SetNTSTATUS(status);
237 return NULL;
238 }
239
240 Py_RETURN_NONE;
241}
242
243
244static PyObject *py_messaging_remove_name(PyObject *self, PyObject *args, PyObject *kwargs)
245{
246 messaging_Object *iface = (messaging_Object *)self;
247 char *name;
248 const char *kwnames[] = { "name", NULL };
249
250 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|:send",
251 discard_const_p(char *, kwnames), &name)) {
252 return NULL;
253 }
254
255 irpc_remove_name(iface->msg_ctx, name);
256
257 Py_RETURN_NONE;
258}
259
260static PyMethodDef py_messaging_methods[] = {
261 { "send", (PyCFunction)py_messaging_send, METH_VARARGS|METH_KEYWORDS,
262 "S.send(target, msg_type, data) -> None\nSend a message" },
263 { "register", (PyCFunction)py_messaging_register, METH_VARARGS|METH_KEYWORDS,
264 "S.register(callback, msg_type=None) -> msg_type\nRegister a message handler" },
265 { "deregister", (PyCFunction)py_messaging_deregister, METH_VARARGS|METH_KEYWORDS,
266 "S.deregister(callback, msg_type) -> None\nDeregister a message handler" },
267 { "add_name", (PyCFunction)py_messaging_add_name, METH_VARARGS|METH_KEYWORDS, "S.add_name(name) -> None\nListen on another name" },
268 { "remove_name", (PyCFunction)py_messaging_remove_name, METH_VARARGS|METH_KEYWORDS, "S.remove_name(name) -> None\nStop listening on a name" },
269 { NULL, NULL, 0, NULL }
270};
271
272static PyObject *py_messaging_server_id(PyObject *obj, void *closure)
273{
274 messaging_Object *iface = (messaging_Object *)obj;
275 struct server_id server_id = messaging_get_server_id(iface->msg_ctx);
276
277 return Py_BuildValue("(iii)", server_id.id, server_id.id2,
278 server_id.node);
279}
280
281static PyGetSetDef py_messaging_getset[] = {
282 { discard_const_p(char, "server_id"), py_messaging_server_id, NULL,
283 discard_const_p(char, "local server id") },
284 { NULL },
285};
286
287
288PyTypeObject messaging_Type = {
289 PyObject_HEAD_INIT(NULL) 0,
290 .tp_name = "irpc.Messaging",
291 .tp_basicsize = sizeof(messaging_Object),
292 .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
293 .tp_new = py_messaging_connect,
294 .tp_dealloc = py_messaging_dealloc,
295 .tp_methods = py_messaging_methods,
296 .tp_getset = py_messaging_getset,
297 .tp_doc = "Messaging(own_id=None, messaging_path=None)\n" \
298 "Create a new object that can be used to communicate with the peers in the specified messaging path.\n" \
299 "If no path is specified, the default path from smb.conf will be used."
300};
301
302
303/*
304 state of a irpc 'connection'
305*/
306typedef struct {
307 PyObject_HEAD
308 const char *server_name;
309 struct server_id *dest_ids;
310 struct messaging_context *msg_ctx;
311 TALLOC_CTX *mem_ctx;
312} irpc_ClientConnectionObject;
313
314/*
315 setup a context for talking to a irpc server
316 example:
317 status = irpc.connect("smb_server");
318*/
319
320PyObject *py_irpc_connect(PyTypeObject *self, PyObject *args, PyObject *kwargs)
321{
322 struct tevent_context *ev;
323 const char *kwnames[] = { "server", "own_id", "messaging_path", NULL };
324 char *server;
325 const char *messaging_path = NULL;
326 PyObject *own_id = Py_None;
327 irpc_ClientConnectionObject *ret;
328
329 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|Oz:connect",
330 discard_const_p(char *, kwnames), &server, &own_id, &messaging_path)) {
331 return NULL;
332 }
333
334 ret = PyObject_New(irpc_ClientConnectionObject, &irpc_ClientConnectionType);
335 if (ret == NULL)
336 return NULL;
337
338 ret->mem_ctx = talloc_new(NULL);
339
340 ret->server_name = server;
341
342 ev = s4_event_context_init(ret->mem_ctx);
343
344 if (messaging_path == NULL) {
345 messaging_path = lp_messaging_path(ret->mem_ctx,
346 py_default_loadparm_context(ret->mem_ctx));
347 } else {
348 messaging_path = talloc_strdup(ret->mem_ctx, messaging_path);
349 }
350
351 if (own_id != Py_None) {
352 struct server_id server_id;
353
354 if (!server_id_from_py(own_id, &server_id))
355 return NULL;
356
357 ret->msg_ctx = messaging_init(ret->mem_ctx,
358 messaging_path,
359 server_id,
360 py_iconv_convenience(ret->mem_ctx),
361 ev);
362 } else {
363 ret->msg_ctx = messaging_client_init(ret->mem_ctx,
364 messaging_path,
365 py_iconv_convenience(ret->mem_ctx),
366 ev);
367 }
368
369 if (ret->msg_ctx == NULL) {
370 PyErr_SetString(PyExc_RuntimeError, "irpc_connect unable to create a messaging context");
371 talloc_free(ret->mem_ctx);
372 return NULL;
373 }
374
375 ret->dest_ids = irpc_servers_byname(ret->msg_ctx, ret->mem_ctx, ret->server_name);
376 if (ret->dest_ids == NULL || ret->dest_ids[0].id == 0) {
377 talloc_free(ret->mem_ctx);
378 PyErr_SetNTSTATUS(NT_STATUS_OBJECT_NAME_NOT_FOUND);
379 return NULL;
380 } else {
381 return (PyObject *)ret;
382 }
383}
384
385typedef struct {
386 PyObject_HEAD
387 struct irpc_request **reqs;
388 int count;
389 int current;
390 TALLOC_CTX *mem_ctx;
391 py_data_unpack_fn unpack_fn;
392} irpc_ResultObject;
393
394
395static PyObject *irpc_result_next(irpc_ResultObject *iterator)
396{
397 NTSTATUS status;
398
399 if (iterator->current >= iterator->count) {
400 PyErr_SetString(PyExc_StopIteration, "No more results");
401 return NULL;
402 }
403
404 status = irpc_call_recv(iterator->reqs[iterator->current]);
405 iterator->current++;
406 if (!NT_STATUS_IS_OK(status)) {
407 PyErr_SetNTSTATUS(status);
408 return NULL;
409 }
410
411 return iterator->unpack_fn(iterator->reqs[iterator->current-1]->r);
412}
413
414static PyObject *irpc_result_len(irpc_ResultObject *self)
415{
416 return PyLong_FromLong(self->count);
417}
418
419static PyMethodDef irpc_result_methods[] = {
420 { "__len__", (PyCFunction)irpc_result_len, METH_NOARGS,
421 "Number of elements returned"},
422 { NULL }
423};
424
425static void irpc_result_dealloc(PyObject *self)
426{
427 talloc_free(((irpc_ResultObject *)self)->mem_ctx);
428 PyObject_Del(self);
429}
430
431PyTypeObject irpc_ResultIteratorType = {
432 PyObject_HEAD_INIT(NULL) 0,
433 .tp_name = "irpc.ResultIterator",
434 .tp_basicsize = sizeof(irpc_ResultObject),
435 .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
436 .tp_iternext = (iternextfunc)irpc_result_next,
437 .tp_iter = PyObject_SelfIter,
438 .tp_methods = irpc_result_methods,
439 .tp_dealloc = irpc_result_dealloc,
440};
441
442static PyObject *py_irpc_call(irpc_ClientConnectionObject *p, struct PyNdrRpcMethodDef *method_def, PyObject *args, PyObject *kwargs)
443{
444 void *ptr;
445 struct irpc_request **reqs;
446 int i, count;
447 NTSTATUS status;
448 TALLOC_CTX *mem_ctx = talloc_new(NULL);
449 irpc_ResultObject *ret;
450
451 /* allocate the C structure */
452 ptr = talloc_zero_size(mem_ctx, method_def->table->calls[method_def->opnum].struct_size);
453 if (ptr == NULL) {
454 status = NT_STATUS_NO_MEMORY;
455 goto done;
456 }
457
458 /* convert the mpr object into a C structure */
459 if (!method_def->pack_in_data(args, kwargs, ptr)) {
460 talloc_free(mem_ctx);
461 return NULL;
462 }
463
464 for (count=0;p->dest_ids[count].id;count++) /* noop */ ;
465
466 /* we need to make a call per server */
467 reqs = talloc_array(mem_ctx, struct irpc_request *, count);
468 if (reqs == NULL) {
469 status = NT_STATUS_NO_MEMORY;
470 goto done;
471 }
472
473 /* make the actual calls */
474 for (i=0;i<count;i++) {
475 reqs[i] = irpc_call_send(p->msg_ctx, p->dest_ids[i],
476 method_def->table, method_def->opnum, ptr, ptr);
477 if (reqs[i] == NULL) {
478 status = NT_STATUS_NO_MEMORY;
479 goto done;
480 }
481 talloc_steal(reqs, reqs[i]);
482 }
483
484 ret = PyObject_New(irpc_ResultObject, &irpc_ResultIteratorType);
485 ret->mem_ctx = mem_ctx;
486 ret->reqs = reqs;
487 ret->count = count;
488 ret->current = 0;
489 ret->unpack_fn = method_def->unpack_out_data;
490
491 return (PyObject *)ret;
492done:
493 talloc_free(mem_ctx);
494 PyErr_SetNTSTATUS(status);
495 return NULL;
496}
497
498static PyObject *py_irpc_call_wrapper(PyObject *self, PyObject *args, void *wrapped, PyObject *kwargs)
499{
500 irpc_ClientConnectionObject *iface = (irpc_ClientConnectionObject *)self;
501 struct PyNdrRpcMethodDef *md = wrapped;
502
503 return py_irpc_call(iface, md, args, kwargs);
504}
505
506static void py_irpc_dealloc(PyObject *self)
507{
508 irpc_ClientConnectionObject *iface = (irpc_ClientConnectionObject *)self;
509 talloc_free(iface->mem_ctx);
510 PyObject_Del(self);
511}
512
513PyTypeObject irpc_ClientConnectionType = {
514 PyObject_HEAD_INIT(NULL) 0,
515 .tp_name = "irpc.ClientConnection",
516 .tp_basicsize = sizeof(irpc_ClientConnectionObject),
517 .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
518 .tp_new = py_irpc_connect,
519 .tp_dealloc = py_irpc_dealloc,
520 .tp_doc = "ClientConnection(server, own_id=None, messaging_path=None)\n" \
521 "Create a new IRPC client connection to communicate with the servers in the specified path.\n" \
522 "If no path is specified, the default path from smb.conf will be used."
523};
524
525static bool irpc_AddNdrRpcMethods(PyTypeObject *ifacetype, const struct PyNdrRpcMethodDef *mds)
526{
527 int i;
528 for (i = 0; mds[i].name; i++) {
529 PyObject *ret;
530 struct wrapperbase *wb = calloc(sizeof(struct wrapperbase), 1);
531
532 wb->name = discard_const_p(char, mds[i].name);
533 wb->flags = PyWrapperFlag_KEYWORDS;
534 wb->wrapper = (wrapperfunc)py_irpc_call_wrapper;
535 wb->doc = discard_const_p(char, mds[i].doc);
536
537 ret = PyDescr_NewWrapper(ifacetype, wb, discard_const_p(void, &mds[i]));
538
539 PyDict_SetItemString(ifacetype->tp_dict, mds[i].name,
540 (PyObject *)ret);
541 }
542
543 return true;
544}
545
546void initmessaging(void)
547{
548 PyObject *mod;
549 PyObject *dep_irpc;
550
551 dep_irpc = PyImport_ImportModule("samba.dcerpc.irpc");
552 if (dep_irpc == NULL)
553 return;
554
555 if (PyType_Ready(&irpc_ClientConnectionType) < 0)
556 return;
557
558 if (PyType_Ready(&messaging_Type) < 0)
559 return;
560
561 if (PyType_Ready(&irpc_ResultIteratorType) < 0)
562 return;
563
564 if (!irpc_AddNdrRpcMethods(&irpc_ClientConnectionType, py_ndr_irpc_methods))
565 return;
566
567 mod = Py_InitModule3("messaging", NULL, "Internal RPC");
568 if (mod == NULL)
569 return;
570
571 Py_INCREF((PyObject *)&irpc_ClientConnectionType);
572 PyModule_AddObject(mod, "ClientConnection", (PyObject *)&irpc_ClientConnectionType);
573
574 Py_INCREF((PyObject *)&messaging_Type);
575 PyModule_AddObject(mod, "Messaging", (PyObject *)&messaging_Type);
576}
Note: See TracBrowser for help on using the repository browser.