1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-23 17:34:34 +03:00

s3:pylibsmb: add notify() support

The operation is a bit different from others,
as results are returned in an async fashion.
It returns a request handle.

  notify_req = conn.notify(fnum=fnum,
                           buffer_size=0xffff,
                           completion_filter=libsmb.FILE_NOTIFY_CHANGE_ALL,
                           recursive=True)

  # ... do other operations on conn.*() ...

  changes = notify_req.get_changes(wait=False)
  # changes is likely to be None if no result arrived yet

  # ... do other operations on conn.*() ...

  changes = notify_req.get_changes(wait=True)
  # changes is a list of change dictionaries
  # each containing "name" (a string) and
  # "action" (an integer, e.g. libsmb.NOTIFY_ACTION_REMOVED)

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
This commit is contained in:
Stefan Metzmacher 2019-08-27 13:49:04 +00:00
parent fde65c2f29
commit 3ffb817506

View File

@ -1142,6 +1142,237 @@ static PyObject *py_cli_delete_on_close(struct py_cli_state *self,
Py_RETURN_NONE;
}
struct py_cli_notify_state {
PyObject_HEAD
struct py_cli_state *py_cli_state;
struct tevent_req *req;
};
static void py_cli_notify_state_dealloc(struct py_cli_notify_state *self)
{
TALLOC_FREE(self->req);
if (self->py_cli_state != NULL) {
Py_DECREF(self->py_cli_state);
self->py_cli_state = NULL;
}
Py_TYPE(self)->tp_free(self);
}
static PyTypeObject py_cli_notify_state_type;
static PyObject *py_cli_notify(struct py_cli_state *self,
PyObject *args,
PyObject *kwds)
{
static const char *kwlist[] = {
"fnum",
"buffer_size",
"completion_filter",
"recursive",
NULL
};
unsigned fnum = 0;
unsigned buffer_size = 0;
unsigned completion_filter = 0;
PyObject *py_recursive = Py_False;
bool recursive = false;
struct tevent_req *req = NULL;
struct tevent_queue *send_queue = NULL;
struct tevent_req *flush_req = NULL;
bool ok;
struct py_cli_notify_state *py_notify_state = NULL;
struct timeval endtime;
ok = ParseTupleAndKeywords(args,
kwds,
"IIIO",
kwlist,
&fnum,
&buffer_size,
&completion_filter,
&py_recursive);
if (!ok) {
return NULL;
}
recursive = PyObject_IsTrue(py_recursive);
req = cli_notify_send(NULL,
self->ev,
self->cli,
fnum,
buffer_size,
completion_filter,
recursive);
if (req == NULL) {
PyErr_NoMemory();
return NULL;
}
/*
* Just wait for the request being submitted to
* the kernel/socket/wire.
*/
send_queue = smbXcli_conn_send_queue(self->cli->conn);
flush_req = tevent_queue_wait_send(req,
self->ev,
send_queue);
endtime = timeval_current_ofs_msec(self->cli->timeout);
ok = tevent_req_set_endtime(flush_req,
self->ev,
endtime);
if (!ok) {
TALLOC_FREE(req);
PyErr_NoMemory();
return NULL;
}
ok = py_tevent_req_wait_exc(self, flush_req);
if (!ok) {
TALLOC_FREE(req);
return NULL;
}
TALLOC_FREE(flush_req);
py_notify_state = (struct py_cli_notify_state *)
py_cli_notify_state_type.tp_alloc(&py_cli_notify_state_type, 0);
if (py_notify_state == NULL) {
TALLOC_FREE(req);
PyErr_NoMemory();
return NULL;
}
Py_INCREF(self);
py_notify_state->py_cli_state = self;
py_notify_state->req = req;
return (PyObject *)py_notify_state;
}
static PyObject *py_cli_notify_get_changes(struct py_cli_notify_state *self,
PyObject *args,
PyObject *kwds)
{
struct py_cli_state *py_cli_state = self->py_cli_state;
struct tevent_req *req = self->req;
uint32_t i;
uint32_t num_changes = 0;
struct notify_change *changes = NULL;
PyObject *result = NULL;
NTSTATUS status;
bool ok;
static const char *kwlist[] = {
"wait",
NULL
};
PyObject *py_wait = Py_False;
bool wait = false;
bool pending;
ok = ParseTupleAndKeywords(args,
kwds,
"O",
kwlist,
&py_wait);
if (!ok) {
return NULL;
}
wait = PyObject_IsTrue(py_wait);
if (req == NULL) {
PyErr_SetString(PyExc_RuntimeError,
"TODO req == NULL "
"- missing change notify request?");
return NULL;
}
pending = tevent_req_is_in_progress(req);
if (pending && !wait) {
Py_RETURN_NONE;
}
if (pending) {
struct timeval endtime;
endtime = timeval_current_ofs_msec(py_cli_state->cli->timeout);
ok = tevent_req_set_endtime(req,
py_cli_state->ev,
endtime);
if (!ok) {
TALLOC_FREE(req);
PyErr_NoMemory();
return NULL;
}
}
ok = py_tevent_req_wait_exc(py_cli_state, req);
self->req = NULL;
Py_DECREF(self->py_cli_state);
self->py_cli_state = NULL;
if (!ok) {
return NULL;
}
status = cli_notify_recv(req, req, &num_changes, &changes);
if (!NT_STATUS_IS_OK(status)) {
TALLOC_FREE(req);
PyErr_SetNTSTATUS(status);
return NULL;
}
result = Py_BuildValue("[]");
if (result == NULL) {
TALLOC_FREE(req);
return NULL;
}
for (i = 0; i < num_changes; i++) {
PyObject *change = NULL;
int ret;
change = Py_BuildValue("{s:s,s:I}",
"name", changes[i].name,
"action", changes[i].action);
if (change == NULL) {
TALLOC_FREE(req);
return NULL;
}
ret = PyList_Append(result, change);
if (ret == -1) {
TALLOC_FREE(req);
return NULL;
}
}
TALLOC_FREE(req);
return result;
}
static PyMethodDef py_cli_notify_state_methods[] = {
{ "get_changes",
(PyCFunction)py_cli_notify_get_changes,
METH_VARARGS|METH_KEYWORDS,
"Wait for change notifications: \n"
"N.get_changes(wait=BOOLEAN) -> "
"change notifications as a dictionary\n"
"\t\tList contents of a directory. The keys are, \n"
"\t\t\tname: name of changed object\n"
"\t\t\taction: type of the change\n"
"None is returned if there's no response jet and wait=False is passed"
},
{ NULL }
};
static PyTypeObject py_cli_notify_state_type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "libsmb_samba_cwrapper.Notify",
.tp_basicsize = sizeof(struct py_cli_notify_state),
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_doc = "notify request",
.tp_dealloc = (destructor)py_cli_notify_state_dealloc,
.tp_methods = py_cli_notify_state_methods,
};
/*
* Helper to add directory listing entries to an overall Python list
*/
@ -1471,6 +1702,11 @@ static PyMethodDef py_cli_state_methods[] = {
py_cli_delete_on_close),
METH_VARARGS|METH_KEYWORDS,
"Set/Reset the delete on close flag" },
{ "notify", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_notify),
METH_VARARGS|METH_KEYWORDS,
"Wait for change notifications: \n"
"notify(fnum, buffer_size, completion_filter...) -> "
"libsmb_samba_internal.Notify request handle\n" },
{ "list", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_list),
METH_VARARGS|METH_KEYWORDS,
"list(directory, mask='*', attribs=DEFAULT_ATTRS) -> "
@ -1553,6 +1789,10 @@ MODULE_INIT_FUNC(libsmb_samba_cwrapper)
if (PyType_Ready(&py_cli_state_type) < 0) {
return NULL;
}
if (PyType_Ready(&py_cli_notify_state_type) < 0) {
return NULL;
}
Py_INCREF(&py_cli_state_type);
PyModule_AddObject(m, "LibsmbCConn", (PyObject *)&py_cli_state_type);
@ -1579,5 +1819,31 @@ MODULE_INIT_FUNC(libsmb_samba_cwrapper)
ADD_FLAGS(FILE_SHARE_WRITE);
ADD_FLAGS(FILE_SHARE_DELETE);
/* change notify completion filter flags */
ADD_FLAGS(FILE_NOTIFY_CHANGE_FILE_NAME);
ADD_FLAGS(FILE_NOTIFY_CHANGE_DIR_NAME);
ADD_FLAGS(FILE_NOTIFY_CHANGE_ATTRIBUTES);
ADD_FLAGS(FILE_NOTIFY_CHANGE_SIZE);
ADD_FLAGS(FILE_NOTIFY_CHANGE_LAST_WRITE);
ADD_FLAGS(FILE_NOTIFY_CHANGE_LAST_ACCESS);
ADD_FLAGS(FILE_NOTIFY_CHANGE_CREATION);
ADD_FLAGS(FILE_NOTIFY_CHANGE_EA);
ADD_FLAGS(FILE_NOTIFY_CHANGE_SECURITY);
ADD_FLAGS(FILE_NOTIFY_CHANGE_STREAM_NAME);
ADD_FLAGS(FILE_NOTIFY_CHANGE_STREAM_SIZE);
ADD_FLAGS(FILE_NOTIFY_CHANGE_STREAM_WRITE);
ADD_FLAGS(FILE_NOTIFY_CHANGE_NAME);
ADD_FLAGS(FILE_NOTIFY_CHANGE_ALL);
/* change notify action results */
ADD_FLAGS(NOTIFY_ACTION_ADDED);
ADD_FLAGS(NOTIFY_ACTION_REMOVED);
ADD_FLAGS(NOTIFY_ACTION_MODIFIED);
ADD_FLAGS(NOTIFY_ACTION_OLD_NAME);
ADD_FLAGS(NOTIFY_ACTION_NEW_NAME);
ADD_FLAGS(NOTIFY_ACTION_ADDED_STREAM);
ADD_FLAGS(NOTIFY_ACTION_REMOVED_STREAM);
ADD_FLAGS(NOTIFY_ACTION_MODIFIED_STREAM);
return m;
}