summaryrefslogtreecommitdiff
path: root/source3/libsmb/pylibsmb.c
diff options
context:
space:
mode:
authorVolker Lendecke <vl@samba.org>2012-08-02 23:21:27 +0200
committerStefan Metzmacher <metze@samba.org>2012-08-16 21:10:32 +0200
commitfbebd7530ed365ac6a7c7ae004975850b4f4d162 (patch)
tree6bab639643525f5817cc7498b8977414d32c319c /source3/libsmb/pylibsmb.c
parentd7d8646e6352c3e28012e52bd926283b1d1a09c9 (diff)
downloadsamba-fbebd7530ed365ac6a7c7ae004975850b4f4d162.tar.gz
samba-fbebd7530ed365ac6a7c7ae004975850b4f4d162.tar.bz2
samba-fbebd7530ed365ac6a7c7ae004975850b4f4d162.zip
s3-libsmb: Add a python wrapper
Please note that this is not finished and only for internal use. Signed-off-by: Stefan Metzmacher <metze@samba.org>
Diffstat (limited to 'source3/libsmb/pylibsmb.c')
-rw-r--r--source3/libsmb/pylibsmb.c671
1 files changed, 671 insertions, 0 deletions
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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <Python.h>
+#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 <pthread.h>
+
+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);
+}