mirror of
https://github.com/samba-team/samba.git
synced 2024-12-23 17:34:34 +03:00
61c951e063
If a query is still being processed on the server and there no results yet, macOS returns 0x23. For now just implements this as dumb polling once a second in mdsearch and the Python bindings. Signed-off-by: Ralph Boehme <slow@samba.org> Reviewed-by: Noel Power <noel.power@suse.com> Autobuild-User(master): Ralph Böhme <slow@samba.org> Autobuild-Date(master): Mon Jul 24 16:15:16 UTC 2023 on atb-devel-224
573 lines
12 KiB
C
573 lines
12 KiB
C
/*
|
|
Python interface to cli_mdssvc
|
|
|
|
Copyright (C) Ralph Boehme 2019
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <Python.h>
|
|
#include <pytalloc.h>
|
|
#include "includes.h"
|
|
#include "python/py3compat.h"
|
|
#include "python/modules.h"
|
|
#include "lib/util/talloc_stack.h"
|
|
#include "lib/util/tevent_ntstatus.h"
|
|
#include "librpc/rpc/rpc_common.h"
|
|
#include "librpc/rpc/pyrpc_util.h"
|
|
#include "rpc_client/cli_mdssvc.h"
|
|
#include "rpc_client/cli_mdssvc_private.h"
|
|
|
|
static PyObject *search_get_results(PyObject *self,
|
|
PyObject *args,
|
|
PyObject *kwargs)
|
|
{
|
|
TALLOC_CTX *frame = talloc_stackframe();
|
|
const char * const kwnames[] = {"pipe", NULL};
|
|
PyObject *pypipe = NULL;
|
|
PyObject *result = NULL;
|
|
dcerpc_InterfaceObject *pipe = NULL;
|
|
struct tevent_req *req = NULL;
|
|
struct mdscli_search_ctx *search = NULL;
|
|
uint64_t *cnids = NULL;
|
|
size_t i;
|
|
size_t ncnids;
|
|
NTSTATUS status;
|
|
int ret;
|
|
bool ok;
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args,
|
|
kwargs,
|
|
"O",
|
|
discard_const_p(char *, kwnames),
|
|
&pypipe)) {
|
|
PyErr_SetString(PyExc_RuntimeError, "Failed to parse args");
|
|
goto out;
|
|
}
|
|
|
|
ok = py_check_dcerpc_type(pypipe,
|
|
"samba.dcerpc.base",
|
|
"ClientConnection");
|
|
if (!ok) {
|
|
goto out;
|
|
}
|
|
|
|
pipe = (dcerpc_InterfaceObject *)pypipe;
|
|
|
|
search = pytalloc_get_type(self, struct mdscli_search_ctx);
|
|
if (search == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* We must use the async send/recv versions in order to pass the correct
|
|
* tevent context, here and any other place we call mdscli_*
|
|
* functions. Using the sync version we would be polling a temporary
|
|
* event context, but unfortunately the s4 Python RPC bindings dispatch
|
|
* events through
|
|
*
|
|
* dcerpc_bh_raw_call_send()
|
|
* -> dcerpc_request_send()
|
|
* -> dcerpc_schedule_io_trigger()
|
|
* -> dcerpc_send_request()
|
|
* -> tstream_writev_queue_send()
|
|
*
|
|
* on an hardcoded event context allocated via
|
|
*
|
|
* py_dcerpc_interface_init_helper()
|
|
* -> dcerpc_pipe_connect()
|
|
*/
|
|
again:
|
|
req = mdscli_get_results_send(frame,
|
|
pipe->ev,
|
|
search);
|
|
if (req == NULL) {
|
|
PyErr_NoMemory();
|
|
goto out;
|
|
}
|
|
|
|
if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) {
|
|
PyErr_SetNTSTATUS(status);
|
|
goto out;
|
|
}
|
|
|
|
status = mdscli_get_results_recv(req, frame, &cnids);
|
|
TALLOC_FREE(req);
|
|
if (NT_STATUS_EQUAL(status, NT_STATUS_PENDING)) {
|
|
sleep(1);
|
|
goto again;
|
|
}
|
|
if (!NT_STATUS_IS_OK(status) &&
|
|
!NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_MATCHES))
|
|
{
|
|
PyErr_SetNTSTATUS(status);
|
|
goto out;
|
|
}
|
|
|
|
result = Py_BuildValue("[]");
|
|
|
|
ncnids = talloc_array_length(cnids);
|
|
for (i = 0; i < ncnids; i++) {
|
|
char *path = NULL;
|
|
PyObject *pypath = NULL;
|
|
|
|
req = mdscli_get_path_send(frame,
|
|
pipe->ev,
|
|
search->mdscli_ctx,
|
|
cnids[i]);
|
|
if (req == NULL) {
|
|
PyErr_NoMemory();
|
|
Py_DECREF(result);
|
|
result = NULL;
|
|
goto out;
|
|
}
|
|
|
|
if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) {
|
|
PyErr_SetNTSTATUS(status);
|
|
Py_DECREF(result);
|
|
result = NULL;
|
|
goto out;
|
|
}
|
|
|
|
status = mdscli_get_path_recv(req, frame, &path);
|
|
TALLOC_FREE(req);
|
|
PyErr_NTSTATUS_NOT_OK_RAISE(status);
|
|
|
|
pypath = PyUnicode_FromString(path);
|
|
if (pypath == NULL) {
|
|
PyErr_NoMemory();
|
|
Py_DECREF(result);
|
|
result = NULL;
|
|
goto out;
|
|
}
|
|
|
|
ret = PyList_Append(result, pypath);
|
|
Py_DECREF(pypath);
|
|
if (ret == -1) {
|
|
PyErr_SetString(PyExc_RuntimeError,
|
|
"list append failed");
|
|
Py_DECREF(result);
|
|
result = NULL;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
out:
|
|
talloc_free(frame);
|
|
return result;
|
|
}
|
|
|
|
static PyObject *search_close(PyObject *self,
|
|
PyObject *args,
|
|
PyObject *kwargs)
|
|
{
|
|
TALLOC_CTX *frame = talloc_stackframe();
|
|
const char * const kwnames[] = {"pipe", NULL};
|
|
PyObject *pypipe = NULL;
|
|
dcerpc_InterfaceObject *pipe = NULL;
|
|
struct tevent_req *req = NULL;
|
|
struct mdscli_search_ctx *search = NULL;
|
|
NTSTATUS status;
|
|
bool ok;
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args,
|
|
kwargs,
|
|
"O",
|
|
discard_const_p(char *, kwnames),
|
|
&pypipe)) {
|
|
PyErr_SetString(PyExc_RuntimeError, "Failed to parse args");
|
|
goto fail;
|
|
}
|
|
|
|
ok = py_check_dcerpc_type(pypipe,
|
|
"samba.dcerpc.base",
|
|
"ClientConnection");
|
|
if (!ok) {
|
|
goto fail;
|
|
}
|
|
|
|
pipe = (dcerpc_InterfaceObject *)pypipe;
|
|
|
|
search = pytalloc_get_type(self, struct mdscli_search_ctx);
|
|
if (search == NULL) {
|
|
goto fail;
|
|
}
|
|
|
|
req = mdscli_close_search_send(frame,
|
|
pipe->ev,
|
|
&search);
|
|
if (req == NULL) {
|
|
PyErr_NoMemory();
|
|
goto fail;
|
|
}
|
|
|
|
if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) {
|
|
PyErr_SetNTSTATUS(status);
|
|
goto fail;
|
|
}
|
|
|
|
status = mdscli_close_search_recv(req);
|
|
if (!NT_STATUS_IS_OK(status) &&
|
|
!NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_MATCHES))
|
|
{
|
|
PyErr_SetNTSTATUS(status);
|
|
goto fail;
|
|
}
|
|
TALLOC_FREE(req);
|
|
|
|
talloc_free(frame);
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
|
|
fail:
|
|
talloc_free(frame);
|
|
return NULL;
|
|
}
|
|
|
|
static PyMethodDef search_methods[] = {
|
|
{
|
|
.ml_name = "get_results",
|
|
.ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, search_get_results),
|
|
.ml_flags = METH_VARARGS|METH_KEYWORDS,
|
|
.ml_doc = "",
|
|
},
|
|
{
|
|
.ml_name = "close",
|
|
.ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, search_close),
|
|
.ml_flags = METH_VARARGS|METH_KEYWORDS,
|
|
.ml_doc = "",
|
|
},
|
|
{0},
|
|
};
|
|
|
|
static PyObject *search_new(PyTypeObject *type,
|
|
PyObject *args,
|
|
PyObject *kwds)
|
|
{
|
|
TALLOC_CTX *frame = talloc_stackframe();
|
|
struct mdscli_search_ctx *search = NULL;
|
|
PyObject *self = NULL;
|
|
|
|
search = talloc_zero(frame, struct mdscli_search_ctx);
|
|
if (search == NULL) {
|
|
PyErr_NoMemory();
|
|
talloc_free(frame);
|
|
return NULL;
|
|
}
|
|
|
|
self = pytalloc_steal(type, search);
|
|
talloc_free(frame);
|
|
return self;
|
|
}
|
|
|
|
static PyTypeObject search_type = {
|
|
.tp_name = "mdscli.ctx.search",
|
|
.tp_new = search_new,
|
|
.tp_flags = Py_TPFLAGS_DEFAULT,
|
|
.tp_doc = "search([....]) -> mdssvc client search context\n",
|
|
.tp_methods = search_methods,
|
|
};
|
|
|
|
static PyObject *conn_sharepath(PyObject *self,
|
|
PyObject *unused)
|
|
{
|
|
TALLOC_CTX *frame = talloc_stackframe();
|
|
struct mdscli_ctx *ctx = NULL;
|
|
char *sharepath = NULL;
|
|
PyObject *result = NULL;
|
|
|
|
ctx = pytalloc_get_type(self, struct mdscli_ctx);
|
|
if (ctx == NULL) {
|
|
goto fail;
|
|
}
|
|
|
|
sharepath = mdscli_get_basepath(frame, ctx);
|
|
if (sharepath == NULL) {
|
|
PyErr_NoMemory();
|
|
goto fail;
|
|
}
|
|
|
|
result = PyUnicode_FromString(sharepath);
|
|
|
|
fail:
|
|
talloc_free(frame);
|
|
return result;
|
|
}
|
|
|
|
static PyObject *conn_search(PyObject *self,
|
|
PyObject *args,
|
|
PyObject *kwargs)
|
|
{
|
|
TALLOC_CTX *frame = talloc_stackframe();
|
|
PyObject *pypipe = NULL;
|
|
dcerpc_InterfaceObject *pipe = NULL;
|
|
struct mdscli_ctx *ctx = NULL;
|
|
PyObject *result = NULL;
|
|
char *query = NULL;
|
|
char *basepath = NULL;
|
|
struct tevent_req *req = NULL;
|
|
struct mdscli_search_ctx *search = NULL;
|
|
const char * const kwnames[] = {
|
|
"pipe", "query", "basepath", NULL
|
|
};
|
|
NTSTATUS status;
|
|
bool ok;
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args,
|
|
kwargs,
|
|
"Oss",
|
|
discard_const_p(char *, kwnames),
|
|
&pypipe,
|
|
&query,
|
|
&basepath)) {
|
|
PyErr_SetString(PyExc_RuntimeError, "Failed to parse args");
|
|
goto fail;
|
|
}
|
|
|
|
ok = py_check_dcerpc_type(pypipe,
|
|
"samba.dcerpc.base",
|
|
"ClientConnection");
|
|
if (!ok) {
|
|
goto fail;
|
|
}
|
|
|
|
pipe = (dcerpc_InterfaceObject *)pypipe;
|
|
|
|
ctx = pytalloc_get_type(self, struct mdscli_ctx);
|
|
if (ctx == NULL) {
|
|
goto fail;
|
|
}
|
|
|
|
req = mdscli_search_send(frame,
|
|
pipe->ev,
|
|
ctx,
|
|
query,
|
|
basepath,
|
|
false);
|
|
if (req == NULL) {
|
|
PyErr_NoMemory();
|
|
goto fail;
|
|
}
|
|
|
|
if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) {
|
|
PyErr_SetNTSTATUS(status);
|
|
goto fail;
|
|
}
|
|
|
|
status = mdscli_search_recv(req, frame, &search);
|
|
PyErr_NTSTATUS_IS_ERR_RAISE(status);
|
|
|
|
result = pytalloc_steal(&search_type, search);
|
|
|
|
fail:
|
|
talloc_free(frame);
|
|
return result;
|
|
}
|
|
|
|
static PyObject *conn_disconnect(PyObject *self,
|
|
PyObject *args,
|
|
PyObject *kwargs)
|
|
{
|
|
TALLOC_CTX *frame = talloc_stackframe();
|
|
PyObject *pypipe = NULL;
|
|
dcerpc_InterfaceObject *pipe = NULL;
|
|
struct mdscli_ctx *ctx = NULL;
|
|
struct tevent_req *req = NULL;
|
|
const char * const kwnames[] = {"pipe", NULL};
|
|
NTSTATUS status;
|
|
bool ok;
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args,
|
|
kwargs,
|
|
"O",
|
|
discard_const_p(char *, kwnames),
|
|
&pypipe)) {
|
|
PyErr_SetString(PyExc_RuntimeError, "Failed to parse args");
|
|
goto fail;
|
|
}
|
|
|
|
ok = py_check_dcerpc_type(pypipe,
|
|
"samba.dcerpc.base",
|
|
"ClientConnection");
|
|
if (!ok) {
|
|
goto fail;
|
|
}
|
|
|
|
pipe = (dcerpc_InterfaceObject *)pypipe;
|
|
|
|
ctx = pytalloc_get_type(self, struct mdscli_ctx);
|
|
if (ctx == NULL) {
|
|
goto fail;
|
|
}
|
|
|
|
req = mdscli_disconnect_send(frame, pipe->ev, ctx);
|
|
if (req == NULL) {
|
|
PyErr_NoMemory();
|
|
goto fail;
|
|
}
|
|
|
|
if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) {
|
|
PyErr_SetNTSTATUS(status);
|
|
goto fail;
|
|
}
|
|
|
|
status = mdscli_disconnect_recv(req);
|
|
PyErr_NTSTATUS_IS_ERR_RAISE(status);
|
|
|
|
talloc_free(frame);
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
|
|
fail:
|
|
talloc_free(frame);
|
|
return NULL;
|
|
}
|
|
|
|
static PyMethodDef conn_methods[] = {
|
|
{
|
|
.ml_name = "sharepath",
|
|
.ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, conn_sharepath),
|
|
.ml_flags = METH_NOARGS,
|
|
.ml_doc = "mdscli.conn.sharepath(...) -> get share basepath",
|
|
},
|
|
{
|
|
.ml_name = "search",
|
|
.ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, conn_search),
|
|
.ml_flags = METH_VARARGS|METH_KEYWORDS,
|
|
.ml_doc = "mdscli.conn.search(...) -> run mdssvc query",
|
|
},
|
|
{
|
|
.ml_name = "disconnect",
|
|
.ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, conn_disconnect),
|
|
.ml_flags = METH_VARARGS|METH_KEYWORDS,
|
|
.ml_doc = "mdscli.conn.disconnect(...) -> disconnect",
|
|
},
|
|
{0},
|
|
};
|
|
|
|
static PyObject *conn_new(PyTypeObject *type,
|
|
PyObject *args,
|
|
PyObject *kwargs)
|
|
{
|
|
TALLOC_CTX *frame = talloc_stackframe();
|
|
const char * const kwnames[] = { "pipe", "share", "mountpoint", NULL };
|
|
PyObject *pypipe = NULL;
|
|
dcerpc_InterfaceObject *pipe = NULL;
|
|
struct tevent_req *req = NULL;
|
|
char *share = NULL;
|
|
char *mountpoint = NULL;
|
|
struct mdscli_ctx *ctx = NULL;
|
|
PyObject *self = NULL;
|
|
NTSTATUS status;
|
|
bool ok;
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args,
|
|
kwargs,
|
|
"Oss",
|
|
discard_const_p(char *, kwnames),
|
|
&pypipe,
|
|
&share,
|
|
&mountpoint)) {
|
|
PyErr_SetString(PyExc_RuntimeError, "Failed to parse args");
|
|
goto fail;
|
|
}
|
|
|
|
ok = py_check_dcerpc_type(pypipe,
|
|
"samba.dcerpc.base",
|
|
"ClientConnection");
|
|
if (!ok) {
|
|
goto fail;
|
|
}
|
|
|
|
pipe = (dcerpc_InterfaceObject *)pypipe;
|
|
|
|
req = mdscli_connect_send(frame,
|
|
pipe->ev,
|
|
pipe->binding_handle,
|
|
share,
|
|
mountpoint);
|
|
if (req == NULL) {
|
|
PyErr_NoMemory();
|
|
goto fail;
|
|
}
|
|
|
|
if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) {
|
|
PyErr_SetNTSTATUS(status);
|
|
goto fail;
|
|
}
|
|
|
|
status = mdscli_connect_recv(req, frame, &ctx);
|
|
PyErr_NTSTATUS_IS_ERR_RAISE(status);
|
|
|
|
self = pytalloc_steal(type, ctx);
|
|
|
|
fail:
|
|
talloc_free(frame);
|
|
return self;
|
|
}
|
|
|
|
static PyTypeObject conn_type = {
|
|
.tp_name = "mdscli.conn",
|
|
.tp_new = conn_new,
|
|
.tp_flags = Py_TPFLAGS_DEFAULT,
|
|
.tp_doc = "conn([....]) -> mdssvc connection\n",
|
|
.tp_methods = conn_methods,
|
|
};
|
|
|
|
static PyMethodDef mdscli_methods[] = {
|
|
{0},
|
|
};
|
|
|
|
static struct PyModuleDef moduledef = {
|
|
PyModuleDef_HEAD_INIT,
|
|
.m_name = "mdscli",
|
|
.m_doc = "RPC mdssvc client",
|
|
.m_size = -1,
|
|
.m_methods = mdscli_methods,
|
|
};
|
|
|
|
MODULE_INIT_FUNC(mdscli)
|
|
{
|
|
TALLOC_CTX *frame = talloc_stackframe();
|
|
PyObject *m = NULL;
|
|
int ret;
|
|
|
|
ret = pytalloc_BaseObject_PyType_Ready(&conn_type);
|
|
if (ret < 0) {
|
|
TALLOC_FREE(frame);
|
|
return NULL;
|
|
}
|
|
|
|
ret = pytalloc_BaseObject_PyType_Ready(&search_type);
|
|
if (ret < 0) {
|
|
TALLOC_FREE(frame);
|
|
return NULL;
|
|
}
|
|
|
|
m = PyModule_Create(&moduledef);
|
|
if (m == NULL) {
|
|
TALLOC_FREE(frame);
|
|
return NULL;
|
|
}
|
|
|
|
Py_INCREF(&conn_type);
|
|
PyModule_AddObject(m, "conn", (PyObject *)&conn_type);
|
|
|
|
Py_INCREF(&search_type);
|
|
PyModule_AddObject(m, "search", (PyObject *)&search_type);
|
|
|
|
TALLOC_FREE(frame);
|
|
return m;
|
|
}
|