From fbebd7530ed365ac6a7c7ae004975850b4f4d162 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Thu, 2 Aug 2012 23:21:27 +0200 Subject: s3-libsmb: Add a python wrapper Please note that this is not finished and only for internal use. Signed-off-by: Stefan Metzmacher --- source3/libsmb/pylibsmb.c | 671 ++++++++++++++++++++++++++++++++++++++++++++++ source3/smbd/pysmbd.c | 2 +- source3/wscript_build | 6 + 3 files changed, 678 insertions(+), 1 deletion(-) create mode 100644 source3/libsmb/pylibsmb.c (limited to 'source3') diff --git a/source3/libsmb/pylibsmb.c b/source3/libsmb/pylibsmb.c new file mode 100644 index 0000000000..be04e5b7db --- /dev/null +++ b/source3/libsmb/pylibsmb.c @@ -0,0 +1,671 @@ +/* + * Unix SMB/CIFS implementation. + * Samba-internal work in progress Python binding for libsmbclient + * + * Copyright (C) Volker Lendecke 2012 + * + * 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 . +*/ + +#include +#include "includes.h" +#include "libsmb/libsmb.h" +#include "libcli/security/security.h" +#include "system/select.h" +#include "source4/libcli/util/pyerrors.h" +#include "auth/credentials/pycredentials.h" + +static PyTypeObject *get_pytype(const char *module, const char *type) +{ + PyObject *mod; + PyTypeObject *result; + + mod = PyImport_ImportModule(module); + if (mod == NULL) { + PyErr_Format(PyExc_RuntimeError, + "Unable to import %s to check type %s", + module, type); + return NULL; + } + result = (PyTypeObject *)PyObject_GetAttrString(mod, type); + Py_DECREF(mod); + if (result == NULL) { + PyErr_Format(PyExc_RuntimeError, + "Unable to find type %s in module %s", + module, type); + return NULL; + } + return result; +} + +struct py_cli_thread; + +struct py_cli_state { + PyObject_HEAD + struct cli_state *cli; + struct tevent_context *ev; + struct py_cli_thread *thread_state; +}; + +#if HAVE_PTHREAD + +#include + +struct py_cli_thread { + + /* + * Pipe to make the poll thread wake up in our destructor, so + * that we can exit and join the thread. + */ + int shutdown_pipe[2]; + struct tevent_fd *shutdown_fde; + bool do_shutdown; + pthread_t id; + + /* + * Thread state to release the GIL during the poll(2) syscall + */ + PyThreadState *py_threadstate; +}; + +static void *py_cli_state_poll_thread(void *private_data) +{ + struct py_cli_state *self = (struct py_cli_state *)private_data; + struct py_cli_thread *t = self->thread_state; + PyGILState_STATE gstate; + + gstate = PyGILState_Ensure(); + + while (!t->do_shutdown) { + int ret; + ret = tevent_loop_once(self->ev); + assert(ret == 0); + } + PyGILState_Release(gstate); + return NULL; +} + +static void py_cli_state_trace_callback(enum tevent_trace_point point, + void *private_data) +{ + struct py_cli_state *self = (struct py_cli_state *)private_data; + struct py_cli_thread *t = self->thread_state; + + switch(point) { + case TEVENT_TRACE_BEFORE_WAIT: + assert(t->py_threadstate == NULL); + t->py_threadstate = PyEval_SaveThread(); + break; + case TEVENT_TRACE_AFTER_WAIT: + assert(t->py_threadstate != NULL); + PyEval_RestoreThread(t->py_threadstate); + t->py_threadstate = NULL; + break; + default: + break; + } +} + +static void py_cli_state_shutdown_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, + void *private_data) +{ + struct py_cli_state *self = (struct py_cli_state *)private_data; + struct py_cli_thread *t = self->thread_state; + + if ((flags & TEVENT_FD_READ) == 0) { + return; + } + TALLOC_FREE(t->shutdown_fde); + t->do_shutdown = true; +} + +static int py_cli_thread_destructor(struct py_cli_thread *t) +{ + char c = 0; + ssize_t written; + int ret; + + do { + /* + * This will wake the poll thread from the poll(2) + */ + written = write(t->shutdown_pipe[1], &c, 1); + } while ((written == -1) && (errno == EINTR)); + + /* + * Allow the poll thread to do its own cleanup under the GIL + */ + Py_BEGIN_ALLOW_THREADS + ret = pthread_join(t->id, NULL); + Py_END_ALLOW_THREADS + assert(ret == 0); + + if (t->shutdown_pipe[0] != -1) { + close(t->shutdown_pipe[0]); + t->shutdown_pipe[0] = -1; + } + if (t->shutdown_pipe[1] != -1) { + close(t->shutdown_pipe[1]); + t->shutdown_pipe[1] = -1; + } + return 0; +} + +static bool py_cli_state_setup_ev(struct py_cli_state *self) +{ + struct py_cli_thread *t; + int ret; + + self->ev = tevent_context_init_byname(NULL, "poll_mt"); + if (self->ev == NULL) { + goto fail; + } + tevent_set_trace_callback(self->ev, py_cli_state_trace_callback, self); + + self->thread_state = talloc_zero(NULL, struct py_cli_thread); + if (self->thread_state == NULL) { + goto fail; + } + t = self->thread_state; + + ret = pipe(t->shutdown_pipe); + if (ret == -1) { + goto fail; + } + t->shutdown_fde = tevent_add_fd( + self->ev, self->ev, t->shutdown_pipe[0], TEVENT_FD_READ, + py_cli_state_shutdown_handler, self); + if (t->shutdown_fde == NULL) { + goto fail; + } + + PyEval_InitThreads(); + + ret = pthread_create(&t->id, NULL, py_cli_state_poll_thread, self); + if (ret != 0) { + goto fail; + } + talloc_set_destructor(self->thread_state, py_cli_thread_destructor); + return true; + +fail: + TALLOC_FREE(t->shutdown_fde); + + if (t->shutdown_pipe[0] != -1) { + close(t->shutdown_pipe[0]); + t->shutdown_pipe[0] = -1; + } + if (t->shutdown_pipe[1] != -1) { + close(t->shutdown_pipe[1]); + t->shutdown_pipe[1] = -1; + } + + TALLOC_FREE(self->thread_state); + TALLOC_FREE(self->ev); + return false; +} + +struct py_tevent_cond { + pthread_mutex_t mutex; + pthread_cond_t cond; + bool is_done; +}; + +static void py_tevent_signalme(struct tevent_req *req); + +static int py_tevent_req_wait(struct tevent_context *ev, + struct tevent_req *req) +{ + struct py_tevent_cond cond; + int ret, result; + + result = pthread_mutex_init(&cond.mutex, NULL); + if (result != 0) { + goto fail; + } + result = pthread_cond_init(&cond.cond, NULL); + if (result != 0) { + goto fail_mutex; + } + + cond.is_done = false; + tevent_req_set_callback(req, py_tevent_signalme, &cond); + + result = pthread_mutex_lock(&cond.mutex); + if (result != 0) { + goto fail_cond; + } + + while (!cond.is_done) { + + Py_BEGIN_ALLOW_THREADS + result = pthread_cond_wait(&cond.cond, &cond.mutex); + Py_END_ALLOW_THREADS + + if (result != 0) { + goto fail_unlock; + } + } + +fail_unlock: + ret = pthread_mutex_unlock(&cond.mutex); + assert(ret == 0); +fail_cond: + ret = pthread_cond_destroy(&cond.cond); + assert(ret == 0); +fail_mutex: + ret = pthread_mutex_destroy(&cond.mutex); + assert(ret == 0); +fail: + return result; +} + +static void py_tevent_signalme(struct tevent_req *req) +{ + struct py_tevent_cond *cond = (struct py_tevent_cond *) + tevent_req_callback_data_void(req); + int ret; + + ret = pthread_mutex_lock(&cond->mutex); + assert(ret == 0); + + cond->is_done = true; + + ret = pthread_cond_signal(&cond->cond); + assert(ret == 0); + ret = pthread_mutex_unlock(&cond->mutex); + assert(ret == 0); +} + +#else + +static bool py_cli_state_setup_ev(struct py_cli_state *self) +{ + self->ev = tevent_context_init(NULL); + return (self->ev != NULL); +} + +static int py_tevent_req_wait(struct tevent_context *ev, + struct tevent_req *req) +{ + while (tevent_req_is_in_progress(req)) { + int ret; + + ret = tevent_loop_once(ev); + if (ret != 0) { + return ret; + } + } + return 0; +} + +#endif + +static PyObject *py_cli_state_new(PyTypeObject *type, PyObject *args, + PyObject *kwds) +{ + struct py_cli_state *self; + + self = (struct py_cli_state *)type->tp_alloc(type, 0); + if (self == NULL) { + return NULL; + } + self->cli = NULL; + self->ev = NULL; + self->thread_state = NULL; + return (PyObject *)self; +} + +static int py_cli_state_init(struct py_cli_state *self, PyObject *args, + PyObject *kwds) +{ + NTSTATUS status; + char *host, *share; + PyObject *creds; + struct cli_credentials *cli_creds; + bool ret; + + static const char *kwlist[] = { + "host", "share", "credentials", NULL + }; + + PyTypeObject *py_type_Credentials = get_pytype( + "samba.credentials", "Credentials"); + if (py_type_Credentials == NULL) { + return -1; + } + + ret = PyArg_ParseTupleAndKeywords( + args, kwds, "ss|O!", (char **)kwlist, + &host, &share, py_type_Credentials, &creds); + + Py_DECREF(py_type_Credentials); + + if (!ret) { + return -1; + } + + if (!py_cli_state_setup_ev(self)) { + return -1; + } + + cli_creds = cli_credentials_from_py_object(creds); + if (cli_creds == NULL) { + PyErr_SetString(PyExc_TypeError, "Expected credentials"); + return -1; + } + + status = cli_full_connection( + &self->cli, "myname", host, NULL, 0, share, "?????", + cli_credentials_get_username(cli_creds), + cli_credentials_get_domain(cli_creds), + cli_credentials_get_password(cli_creds), + 0, 0); + if (!NT_STATUS_IS_OK(status)) { + PyErr_SetNTSTATUS(status); + return -1; + } + return 0; +} + +static void py_cli_state_dealloc(struct py_cli_state *self) +{ + TALLOC_FREE(self->thread_state); + TALLOC_FREE(self->ev); + + if (self->cli != NULL) { + cli_shutdown(self->cli); + self->cli = NULL; + } + self->ob_type->tp_free((PyObject *)self); +} + +static bool py_tevent_req_wait_exc(struct tevent_context *ev, + struct tevent_req *req) +{ + int ret; + + if (req == NULL) { + PyErr_NoMemory(); + return false; + } + ret = py_tevent_req_wait(ev, req); + if (ret != 0) { + TALLOC_FREE(req); + errno = ret; + PyErr_SetFromErrno(PyExc_RuntimeError); + return false; + } + return true; +} + +static PyObject *py_cli_create(struct py_cli_state *self, PyObject *args, + PyObject *kwds) +{ + char *fname; + unsigned CreateFlags = 0; + unsigned DesiredAccess = FILE_GENERIC_READ; + unsigned FileAttributes = 0; + unsigned ShareAccess = 0; + unsigned CreateDisposition = FILE_OPEN; + unsigned CreateOptions = 0; + unsigned SecurityFlags = 0; + uint16_t fnum; + struct tevent_req *req; + NTSTATUS status; + + static const char *kwlist[] = { + "Name", "CreateFlags", "DesiredAccess", "FileAttributes", + "ShareAccess", "CreateDisposition", "CreateOptions", + "SecurityFlags", NULL }; + + if (!PyArg_ParseTupleAndKeywords( + args, kwds, "s|IIIIIII", (char **)kwlist, + &fname, &CreateFlags, &DesiredAccess, &FileAttributes, + &ShareAccess, &CreateDisposition, &CreateOptions, + &SecurityFlags)) { + return NULL; + } + + req = cli_ntcreate_send(NULL, self->ev, self->cli, fname, CreateFlags, + DesiredAccess, FileAttributes, ShareAccess, + CreateDisposition, CreateOptions, + SecurityFlags); + if (!py_tevent_req_wait_exc(self->ev, req)) { + return NULL; + } + status = cli_ntcreate_recv(req, &fnum); + TALLOC_FREE(req); + + if (!NT_STATUS_IS_OK(status)) { + PyErr_SetNTSTATUS(status); + return NULL; + } + return Py_BuildValue("I", (unsigned)fnum); +} + +static PyObject *py_cli_close(struct py_cli_state *self, PyObject *args) +{ + struct tevent_req *req; + int fnum; + NTSTATUS status; + + if (!PyArg_ParseTuple(args, "i", &fnum)) { + return NULL; + } + + req = cli_close_send(NULL, self->ev, self->cli, fnum); + if (!py_tevent_req_wait_exc(self->ev, req)) { + return NULL; + } + status = cli_close_recv(req); + TALLOC_FREE(req); + + if (!NT_STATUS_IS_OK(status)) { + PyErr_SetNTSTATUS(status); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *py_cli_write(struct py_cli_state *self, PyObject *args, + PyObject *kwds) +{ + int fnum; + unsigned mode = 0; + char *buf; + int buflen; + unsigned long long offset; + struct tevent_req *req; + NTSTATUS status; + size_t written; + + static const char *kwlist[] = { + "fnum", "buffer", "offset", "mode", NULL }; + + if (!PyArg_ParseTupleAndKeywords( + args, kwds, "Is#K|I", (char **)kwlist, + &fnum, &buf, &buflen, &offset, &mode)) { + return NULL; + } + + req = cli_write_andx_send(NULL, self->ev, self->cli, fnum, mode, + (uint8_t *)buf, offset, buflen); + if (!py_tevent_req_wait_exc(self->ev, req)) { + return NULL; + } + status = cli_write_andx_recv(req, &written); + TALLOC_FREE(req); + + if (!NT_STATUS_IS_OK(status)) { + PyErr_SetNTSTATUS(status); + return NULL; + } + return Py_BuildValue("K", (unsigned long long)written); +} + +static PyObject *py_cli_read(struct py_cli_state *self, PyObject *args, + PyObject *kwds) +{ + int fnum; + unsigned long long offset; + unsigned size; + struct tevent_req *req; + NTSTATUS status; + uint8_t *buf; + ssize_t buflen; + PyObject *result; + + static const char *kwlist[] = { + "fnum", "offset", "size", NULL }; + + if (!PyArg_ParseTupleAndKeywords( + args, kwds, "IKI", (char **)kwlist, &fnum, &offset, + &size)) { + return NULL; + } + + req = cli_read_andx_send(NULL, self->ev, self->cli, fnum, + offset, size); + if (!py_tevent_req_wait_exc(self->ev, req)) { + return NULL; + } + status = cli_read_andx_recv(req, &buflen, &buf); + + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(req); + PyErr_SetNTSTATUS(status); + return NULL; + } + result = Py_BuildValue("s#", (char *)buf, (int)buflen); + TALLOC_FREE(req); + return result; +} + +static PyObject *py_cli_ftruncate(struct py_cli_state *self, PyObject *args, + PyObject *kwds) +{ + int fnum; + unsigned long long size; + struct tevent_req *req; + NTSTATUS status; + + static const char *kwlist[] = { + "fnum", "size", NULL }; + + if (!PyArg_ParseTupleAndKeywords( + args, kwds, "IK", (char **)kwlist, &fnum, &size)) { + return NULL; + } + + req = cli_ftruncate_send(NULL, self->ev, self->cli, fnum, size); + if (!py_tevent_req_wait_exc(self->ev, req)) { + return NULL; + } + status = cli_ftruncate_recv(req); + + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(req); + PyErr_SetNTSTATUS(status); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *py_cli_delete_on_close(struct py_cli_state *self, + PyObject *args, + PyObject *kwds) +{ + unsigned fnum, flag; + struct tevent_req *req; + NTSTATUS status; + + static const char *kwlist[] = { + "fnum", "flag", NULL }; + + if (!PyArg_ParseTupleAndKeywords( + args, kwds, "II", (char **)kwlist, &fnum, &flag)) { + return NULL; + } + + req = cli_nt_delete_on_close_send(NULL, self->ev, self->cli, fnum, + flag); + if (!py_tevent_req_wait_exc(self->ev, req)) { + return NULL; + } + status = cli_nt_delete_on_close_recv(req); + + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(req); + PyErr_SetNTSTATUS(status); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef py_cli_state_methods[] = { + { "create", (PyCFunction)py_cli_create, METH_VARARGS|METH_KEYWORDS, + "Open a file" }, + { "close", (PyCFunction)py_cli_close, METH_VARARGS, + "Close a file handle" }, + { "write", (PyCFunction)py_cli_write, METH_VARARGS|METH_KEYWORDS, + "Write to a file handle" }, + { "read", (PyCFunction)py_cli_read, METH_VARARGS|METH_KEYWORDS, + "Read from a file handle" }, + { "truncate", (PyCFunction)py_cli_ftruncate, + METH_VARARGS|METH_KEYWORDS, + "Truncate a file" }, + { "delete_on_close", (PyCFunction)py_cli_delete_on_close, + METH_VARARGS|METH_KEYWORDS, + "Set/Reset the delete on close flag" }, + { NULL, NULL, 0, NULL } +}; + +static PyTypeObject py_cli_state_type = { + PyObject_HEAD_INIT(NULL) + .tp_name = "libsmb_samba_internal.Conn", + .tp_basicsize = sizeof(struct py_cli_state), + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_doc = "libsmb connection", + .tp_new = py_cli_state_new, + .tp_init = (initproc)py_cli_state_init, + .tp_dealloc = (destructor)py_cli_state_dealloc, + .tp_methods = py_cli_state_methods, +}; + +static PyMethodDef py_libsmb_methods[] = { + { NULL }, +}; + +void initlibsmb_samba_internal(void); +void initlibsmb_samba_internal(void) +{ + PyObject *m; + + talloc_stackframe(); + + m = Py_InitModule3("libsmb_samba_internal", py_libsmb_methods, + "libsmb wrapper"); + + if (PyType_Ready(&py_cli_state_type) < 0) { + return; + } + Py_INCREF(&py_cli_state_type); + PyModule_AddObject(m, "Conn", (PyObject *)&py_cli_state_type); +} diff --git a/source3/smbd/pysmbd.c b/source3/smbd/pysmbd.c index 890e66e26f..0004887720 100644 --- a/source3/smbd/pysmbd.c +++ b/source3/smbd/pysmbd.c @@ -90,7 +90,7 @@ static NTSTATUS set_nt_acl_no_snum(const char *fname, } if (!(conn->params = talloc(conn, struct share_params))) { - DEBUG(0,("get_nt_acl_no_snum: talloc() failed!\n")); + DEBUG(0,("set_nt_acl_no_snum: talloc() failed!\n")); TALLOC_FREE(frame); return NT_STATUS_NO_MEMORY; } diff --git a/source3/wscript_build b/source3/wscript_build index f6994ce57c..a6cccac55d 100755 --- a/source3/wscript_build +++ b/source3/wscript_build @@ -1580,6 +1580,12 @@ bld.SAMBA3_PYTHON('pysmbd', realname='samba/samba3/smbd.so' ) +bld.SAMBA3_PYTHON('pylibsmb', + source='libsmb/pylibsmb.c', + deps='smbclient samba-credentials', + realname='samba/samba3/libsmb_samba_internal.so' + ) + swat_dir = os.path.join(bld.curdir, '../swat') swat_files = recursive_dirlist(swat_dir, swat_dir, '*') bld.INSTALL_FILES('${SWATDIR}', swat_files, base_name='../swat') -- cgit