s3:rpc_client: Add missing newlines to logging messages
[samba.git] / source3 / rpc_client / py_mdscli.c
1 /*
2    Python interface to cli_mdssvc
3
4    Copyright (C) Ralph Boehme 2019
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 #include <Python.h>
21 #include <pytalloc.h>
22 #include "includes.h"
23 #include "python/py3compat.h"
24 #include "python/modules.h"
25 #include "lib/util/talloc_stack.h"
26 #include "lib/util/tevent_ntstatus.h"
27 #include "librpc/rpc/rpc_common.h"
28 #include "librpc/rpc/pyrpc_util.h"
29 #include "rpc_client/cli_mdssvc.h"
30 #include "rpc_client/cli_mdssvc_private.h"
31
32 static PyObject *search_get_results(PyObject *self,
33                                     PyObject *args,
34                                     PyObject *kwargs)
35 {
36         TALLOC_CTX *frame = talloc_stackframe();
37         const char * const kwnames[] = {"pipe", NULL};
38         PyObject *pypipe = NULL;
39         PyObject *result = NULL;
40         dcerpc_InterfaceObject *pipe = NULL;
41         struct tevent_req *req = NULL;
42         struct mdscli_search_ctx *search = NULL;
43         uint64_t *cnids = NULL;
44         size_t i;
45         size_t ncnids;
46         NTSTATUS status;
47         int ret;
48         bool ok;
49
50         if (!PyArg_ParseTupleAndKeywords(args,
51                                          kwargs,
52                                          "O",
53                                          discard_const_p(char *, kwnames),
54                                          &pypipe)) {
55                 PyErr_SetString(PyExc_RuntimeError, "Failed to parse args");
56                 goto out;
57         }
58
59         ok = py_check_dcerpc_type(pypipe,
60                                   "samba.dcerpc.base",
61                                   "ClientConnection");
62         if (!ok) {
63                 goto out;
64         }
65
66         pipe = (dcerpc_InterfaceObject *)pypipe;
67
68         search = pytalloc_get_type(self, struct mdscli_search_ctx);
69         if (search == NULL) {
70                 goto out;
71         }
72
73         /*
74          * We must use the async send/recv versions in order to pass the correct
75          * tevent context, here and any other place we call mdscli_*
76          * functions. Using the sync version we would be polling a temporary
77          * event context, but unfortunately the s4 Python RPC bindings dispatch
78          * events through
79          *
80          *    dcerpc_bh_raw_call_send()
81          *    -> dcerpc_request_send()
82          *    -> dcerpc_schedule_io_trigger()
83          *    -> dcerpc_send_request()
84          *    -> tstream_writev_queue_send()
85          *
86          * on an hardcoded event context allocated via
87          *
88          *   py_dcerpc_interface_init_helper()
89          *   -> dcerpc_pipe_connect()
90          */
91 again:
92         req = mdscli_get_results_send(frame,
93                                       pipe->ev,
94                                       search);
95         if (req == NULL) {
96                 PyErr_NoMemory();
97                 goto out;
98         }
99
100         if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) {
101                 PyErr_SetNTSTATUS(status);
102                 goto out;
103         }
104
105         status = mdscli_get_results_recv(req, frame, &cnids);
106         TALLOC_FREE(req);
107         if (NT_STATUS_EQUAL(status, NT_STATUS_PENDING)) {
108                 sleep(1);
109                 goto again;
110         }
111         if (!NT_STATUS_IS_OK(status) &&
112             !NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_MATCHES))
113         {
114                 PyErr_SetNTSTATUS(status);
115                 goto out;
116         }
117
118         result = Py_BuildValue("[]");
119
120         ncnids = talloc_array_length(cnids);
121         for (i = 0; i < ncnids; i++) {
122                 char *path = NULL;
123                 PyObject *pypath = NULL;
124
125                 req = mdscli_get_path_send(frame,
126                                            pipe->ev,
127                                            search->mdscli_ctx,
128                                            cnids[i]);
129                 if (req == NULL) {
130                         PyErr_NoMemory();
131                         Py_DECREF(result);
132                         result = NULL;
133                         goto out;
134                 }
135
136                 if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) {
137                         PyErr_SetNTSTATUS(status);
138                         Py_DECREF(result);
139                         result = NULL;
140                         goto out;
141                 }
142
143                 status = mdscli_get_path_recv(req, frame, &path);
144                 TALLOC_FREE(req);
145                 PyErr_NTSTATUS_NOT_OK_RAISE(status);
146
147                 pypath = PyUnicode_FromString(path);
148                 if (pypath == NULL) {
149                         PyErr_NoMemory();
150                         Py_DECREF(result);
151                         result = NULL;
152                         goto out;
153                 }
154
155                 ret = PyList_Append(result, pypath);
156                 Py_DECREF(pypath);
157                 if (ret == -1) {
158                         PyErr_SetString(PyExc_RuntimeError,
159                                         "list append failed");
160                         Py_DECREF(result);
161                         result = NULL;
162                         goto out;
163                 }
164         }
165
166 out:
167         talloc_free(frame);
168         return result;
169 }
170
171 static PyObject *search_close(PyObject *self,
172                               PyObject *args,
173                               PyObject *kwargs)
174 {
175         TALLOC_CTX *frame = talloc_stackframe();
176         const char * const kwnames[] = {"pipe", NULL};
177         PyObject *pypipe = NULL;
178         dcerpc_InterfaceObject *pipe = NULL;
179         struct tevent_req *req = NULL;
180         struct mdscli_search_ctx *search = NULL;
181         NTSTATUS status;
182         bool ok;
183
184         if (!PyArg_ParseTupleAndKeywords(args,
185                                          kwargs,
186                                          "O",
187                                          discard_const_p(char *, kwnames),
188                                          &pypipe)) {
189                 PyErr_SetString(PyExc_RuntimeError, "Failed to parse args");
190                 goto fail;
191         }
192
193         ok = py_check_dcerpc_type(pypipe,
194                                   "samba.dcerpc.base",
195                                   "ClientConnection");
196         if (!ok) {
197                 goto fail;
198         }
199
200         pipe = (dcerpc_InterfaceObject *)pypipe;
201
202         search = pytalloc_get_type(self, struct mdscli_search_ctx);
203         if (search == NULL) {
204                 goto fail;
205         }
206
207         req = mdscli_close_search_send(frame,
208                                        pipe->ev,
209                                        &search);
210         if (req == NULL) {
211                 PyErr_NoMemory();
212                 goto fail;
213         }
214
215         if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) {
216                 PyErr_SetNTSTATUS(status);
217                 goto fail;
218         }
219
220         status = mdscli_close_search_recv(req);
221         if (!NT_STATUS_IS_OK(status) &&
222             !NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_MATCHES))
223         {
224                 PyErr_SetNTSTATUS(status);
225                 goto fail;
226         }
227         TALLOC_FREE(req);
228
229         talloc_free(frame);
230         Py_INCREF(Py_None);
231         return Py_None;
232
233 fail:
234         talloc_free(frame);
235         return NULL;
236 }
237
238 static PyMethodDef search_methods[] = {
239         {
240                 .ml_name  = "get_results",
241                 .ml_meth  = PY_DISCARD_FUNC_SIG(PyCFunction, search_get_results),
242                 .ml_flags = METH_VARARGS|METH_KEYWORDS,
243                 .ml_doc   = "",
244         },
245         {
246                 .ml_name  = "close",
247                 .ml_meth  = PY_DISCARD_FUNC_SIG(PyCFunction, search_close),
248                 .ml_flags = METH_VARARGS|METH_KEYWORDS,
249                 .ml_doc   = "",
250         },
251         {0},
252 };
253
254 static PyObject *search_new(PyTypeObject *type,
255                             PyObject *args,
256                             PyObject *kwds)
257 {
258         TALLOC_CTX *frame = talloc_stackframe();
259         struct mdscli_search_ctx *search = NULL;
260         PyObject *self = NULL;
261
262         search = talloc_zero(frame, struct mdscli_search_ctx);
263         if (search == NULL) {
264                 PyErr_NoMemory();
265                 talloc_free(frame);
266                 return NULL;
267         }
268
269         self = pytalloc_steal(type, search);
270         talloc_free(frame);
271         return self;
272 }
273
274 static PyTypeObject search_type = {
275         .tp_name = "mdscli.ctx.search",
276         .tp_new = search_new,
277         .tp_flags = Py_TPFLAGS_DEFAULT,
278         .tp_doc = "search([....]) -> mdssvc client search context\n",
279         .tp_methods = search_methods,
280 };
281
282 static PyObject *conn_sharepath(PyObject *self,
283                                 PyObject *unused)
284 {
285         TALLOC_CTX *frame = talloc_stackframe();
286         struct mdscli_ctx *ctx = NULL;
287         char *sharepath = NULL;
288         PyObject *result = NULL;
289
290         ctx = pytalloc_get_type(self, struct mdscli_ctx);
291         if (ctx == NULL) {
292                 goto fail;
293         }
294
295         sharepath = mdscli_get_basepath(frame, ctx);
296         if (sharepath == NULL) {
297                 PyErr_NoMemory();
298                 goto fail;
299         }
300
301         result = PyUnicode_FromString(sharepath);
302
303 fail:
304         talloc_free(frame);
305         return result;
306 }
307
308 static PyObject *conn_search(PyObject *self,
309                              PyObject *args,
310                              PyObject *kwargs)
311 {
312         TALLOC_CTX *frame = talloc_stackframe();
313         PyObject *pypipe = NULL;
314         dcerpc_InterfaceObject *pipe = NULL;
315         struct mdscli_ctx *ctx = NULL;
316         PyObject *result = NULL;
317         char *query = NULL;
318         char *basepath = NULL;
319         struct tevent_req *req = NULL;
320         struct mdscli_search_ctx *search = NULL;
321         const char * const kwnames[] = {
322                 "pipe", "query", "basepath", NULL
323         };
324         NTSTATUS status;
325         bool ok;
326
327         if (!PyArg_ParseTupleAndKeywords(args,
328                                          kwargs,
329                                          "Oss",
330                                          discard_const_p(char *, kwnames),
331                                          &pypipe,
332                                          &query,
333                                          &basepath)) {
334                 PyErr_SetString(PyExc_RuntimeError, "Failed to parse args");
335                 goto fail;
336         }
337
338         ok = py_check_dcerpc_type(pypipe,
339                                   "samba.dcerpc.base",
340                                   "ClientConnection");
341         if (!ok) {
342                 goto fail;
343         }
344
345         pipe = (dcerpc_InterfaceObject *)pypipe;
346
347         ctx = pytalloc_get_type(self, struct mdscli_ctx);
348         if (ctx == NULL) {
349                 goto fail;
350         }
351
352         req = mdscli_search_send(frame,
353                                  pipe->ev,
354                                  ctx,
355                                  query,
356                                  basepath,
357                                  false);
358         if (req == NULL) {
359                 PyErr_NoMemory();
360                 goto fail;
361         }
362
363         if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) {
364                 PyErr_SetNTSTATUS(status);
365                 goto fail;
366         }
367
368         status = mdscli_search_recv(req, frame, &search);
369         PyErr_NTSTATUS_IS_ERR_RAISE(status);
370
371         result = pytalloc_steal(&search_type, search);
372
373 fail:
374         talloc_free(frame);
375         return result;
376 }
377
378 static PyObject *conn_disconnect(PyObject *self,
379                                  PyObject *args,
380                                  PyObject *kwargs)
381 {
382         TALLOC_CTX *frame = talloc_stackframe();
383         PyObject *pypipe = NULL;
384         dcerpc_InterfaceObject *pipe = NULL;
385         struct mdscli_ctx *ctx = NULL;
386         struct tevent_req *req = NULL;
387         const char * const kwnames[] = {"pipe", NULL};
388         NTSTATUS status;
389         bool ok;
390
391         if (!PyArg_ParseTupleAndKeywords(args,
392                                          kwargs,
393                                          "O",
394                                          discard_const_p(char *, kwnames),
395                                          &pypipe)) {
396                 PyErr_SetString(PyExc_RuntimeError, "Failed to parse args");
397                 goto fail;
398         }
399
400         ok = py_check_dcerpc_type(pypipe,
401                                   "samba.dcerpc.base",
402                                   "ClientConnection");
403         if (!ok) {
404                 goto fail;
405         }
406
407         pipe = (dcerpc_InterfaceObject *)pypipe;
408
409         ctx = pytalloc_get_type(self, struct mdscli_ctx);
410         if (ctx == NULL) {
411                 goto fail;
412         }
413
414         req = mdscli_disconnect_send(frame, pipe->ev, ctx);
415         if (req == NULL) {
416                 PyErr_NoMemory();
417                 goto fail;
418         }
419
420         if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) {
421                 PyErr_SetNTSTATUS(status);
422                 goto fail;
423         }
424
425         status = mdscli_disconnect_recv(req);
426         PyErr_NTSTATUS_IS_ERR_RAISE(status);
427
428         talloc_free(frame);
429         Py_INCREF(Py_None);
430         return Py_None;
431
432 fail:
433         talloc_free(frame);
434         return NULL;
435 }
436
437 static PyMethodDef conn_methods[] = {
438         {
439                 .ml_name  = "sharepath",
440                 .ml_meth  = PY_DISCARD_FUNC_SIG(PyCFunction, conn_sharepath),
441                 .ml_flags = METH_NOARGS,
442                 .ml_doc   = "mdscli.conn.sharepath(...) -> get share basepath",
443         },
444         {
445                 .ml_name  = "search",
446                 .ml_meth  = PY_DISCARD_FUNC_SIG(PyCFunction, conn_search),
447                 .ml_flags = METH_VARARGS|METH_KEYWORDS,
448                 .ml_doc   = "mdscli.conn.search(...) -> run mdssvc query",
449         },
450         {
451                 .ml_name  = "disconnect",
452                 .ml_meth  = PY_DISCARD_FUNC_SIG(PyCFunction, conn_disconnect),
453                 .ml_flags = METH_VARARGS|METH_KEYWORDS,
454                 .ml_doc   = "mdscli.conn.disconnect(...) -> disconnect",
455         },
456         {0},
457 };
458
459 static PyObject *conn_new(PyTypeObject *type,
460                           PyObject *args,
461                           PyObject *kwargs)
462 {
463         TALLOC_CTX *frame = talloc_stackframe();
464         const char * const kwnames[] = { "pipe", "share", "mountpoint", NULL };
465         PyObject *pypipe = NULL;
466         dcerpc_InterfaceObject *pipe = NULL;
467         struct tevent_req *req = NULL;
468         char *share = NULL;
469         char *mountpoint = NULL;
470         struct mdscli_ctx *ctx = NULL;
471         PyObject *self = NULL;
472         NTSTATUS status;
473         bool ok;
474
475         if (!PyArg_ParseTupleAndKeywords(args,
476                                          kwargs,
477                                          "Oss",
478                                          discard_const_p(char *, kwnames),
479                                          &pypipe,
480                                          &share,
481                                          &mountpoint)) {
482                 PyErr_SetString(PyExc_RuntimeError, "Failed to parse args");
483                 goto fail;
484         }
485
486         ok = py_check_dcerpc_type(pypipe,
487                                   "samba.dcerpc.base",
488                                   "ClientConnection");
489         if (!ok) {
490                 goto fail;
491         }
492
493         pipe = (dcerpc_InterfaceObject *)pypipe;
494
495         req = mdscli_connect_send(frame,
496                                   pipe->ev,
497                                   pipe->binding_handle,
498                                   share,
499                                   mountpoint);
500         if (req == NULL) {
501                 PyErr_NoMemory();
502                 goto fail;
503         }
504
505         if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) {
506                 PyErr_SetNTSTATUS(status);
507                 goto fail;
508         }
509
510         status = mdscli_connect_recv(req, frame, &ctx);
511         PyErr_NTSTATUS_IS_ERR_RAISE(status);
512
513         self = pytalloc_steal(type, ctx);
514
515 fail:
516         talloc_free(frame);
517         return self;
518 }
519
520 static PyTypeObject conn_type = {
521         .tp_name = "mdscli.conn",
522         .tp_new = conn_new,
523         .tp_flags = Py_TPFLAGS_DEFAULT,
524         .tp_doc = "conn([....]) -> mdssvc connection\n",
525         .tp_methods = conn_methods,
526 };
527
528 static PyMethodDef mdscli_methods[] = {
529         {0},
530 };
531
532 static struct PyModuleDef moduledef = {
533         PyModuleDef_HEAD_INIT,
534         .m_name = "mdscli",
535         .m_doc = "RPC mdssvc client",
536         .m_size = -1,
537         .m_methods = mdscli_methods,
538 };
539
540 MODULE_INIT_FUNC(mdscli)
541 {
542         TALLOC_CTX *frame = talloc_stackframe();
543         PyObject *m = NULL;
544         int ret;
545
546         ret = pytalloc_BaseObject_PyType_Ready(&conn_type);
547         if (ret < 0) {
548                 TALLOC_FREE(frame);
549                 return NULL;
550         }
551
552         ret = pytalloc_BaseObject_PyType_Ready(&search_type);
553         if (ret < 0) {
554                 TALLOC_FREE(frame);
555                 return NULL;
556         }
557
558         m = PyModule_Create(&moduledef);
559         if (m == NULL) {
560                 TALLOC_FREE(frame);
561                 return NULL;
562         }
563
564         Py_INCREF(&conn_type);
565         PyModule_AddObject(m, "conn", (PyObject *)&conn_type);
566
567         Py_INCREF(&search_type);
568         PyModule_AddObject(m, "search", (PyObject *)&search_type);
569
570         TALLOC_FREE(frame);
571         return m;
572 }