2 * Unix SMB/CIFS implementation.
3 * Samba-internal work in progress Python binding for libsmbclient
5 * Copyright (C) Volker Lendecke 2012
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "python/py3compat.h"
24 #include "libsmb/libsmb.h"
25 #include "libcli/security/security.h"
26 #include "system/select.h"
27 #include "source4/libcli/util/pyerrors.h"
28 #include "auth/credentials/pycredentials.h"
31 static PyTypeObject *get_pytype(const char *module, const char *type)
36 mod = PyImport_ImportModule(module);
38 PyErr_Format(PyExc_RuntimeError,
39 "Unable to import %s to check type %s",
43 result = (PyTypeObject *)PyObject_GetAttrString(mod, type);
46 PyErr_Format(PyExc_RuntimeError,
47 "Unable to find type %s in module %s",
55 * We're using "const char * const *" for keywords,
56 * PyArg_ParseTupleAndKeywords expects a "char **". Confine the
57 * inevitable warnings to just one place.
59 static int ParseTupleAndKeywords(PyObject *args, PyObject *kw,
60 const char *format, const char * const *keywords,
63 char **_keywords = discard_const_p(char *, keywords);
66 va_start(a, keywords);
67 ret = PyArg_VaParseTupleAndKeywords(args, kw, format,
75 struct py_cli_oplock_break {
82 struct cli_state *cli;
83 struct tevent_context *ev;
84 struct py_cli_thread *thread_state;
86 struct tevent_req *oplock_waiter;
87 struct py_cli_oplock_break *oplock_breaks;
88 struct py_tevent_cond *oplock_cond;
95 struct py_cli_thread {
98 * Pipe to make the poll thread wake up in our destructor, so
99 * that we can exit and join the thread.
101 int shutdown_pipe[2];
102 struct tevent_fd *shutdown_fde;
107 * Thread state to release the GIL during the poll(2) syscall
109 PyThreadState *py_threadstate;
112 static void *py_cli_state_poll_thread(void *private_data)
114 struct py_cli_state *self = (struct py_cli_state *)private_data;
115 struct py_cli_thread *t = self->thread_state;
116 PyGILState_STATE gstate;
118 gstate = PyGILState_Ensure();
120 while (!t->do_shutdown) {
122 ret = tevent_loop_once(self->ev);
125 PyGILState_Release(gstate);
129 static void py_cli_state_trace_callback(enum tevent_trace_point point,
132 struct py_cli_state *self = (struct py_cli_state *)private_data;
133 struct py_cli_thread *t = self->thread_state;
136 case TEVENT_TRACE_BEFORE_WAIT:
137 assert(t->py_threadstate == NULL);
138 t->py_threadstate = PyEval_SaveThread();
140 case TEVENT_TRACE_AFTER_WAIT:
141 assert(t->py_threadstate != NULL);
142 PyEval_RestoreThread(t->py_threadstate);
143 t->py_threadstate = NULL;
150 static void py_cli_state_shutdown_handler(struct tevent_context *ev,
151 struct tevent_fd *fde,
155 struct py_cli_state *self = (struct py_cli_state *)private_data;
156 struct py_cli_thread *t = self->thread_state;
158 if ((flags & TEVENT_FD_READ) == 0) {
161 TALLOC_FREE(t->shutdown_fde);
162 t->do_shutdown = true;
165 static int py_cli_thread_destructor(struct py_cli_thread *t)
173 * This will wake the poll thread from the poll(2)
175 written = write(t->shutdown_pipe[1], &c, 1);
176 } while ((written == -1) && (errno == EINTR));
179 * Allow the poll thread to do its own cleanup under the GIL
181 Py_BEGIN_ALLOW_THREADS
182 ret = pthread_join(t->id, NULL);
186 if (t->shutdown_pipe[0] != -1) {
187 close(t->shutdown_pipe[0]);
188 t->shutdown_pipe[0] = -1;
190 if (t->shutdown_pipe[1] != -1) {
191 close(t->shutdown_pipe[1]);
192 t->shutdown_pipe[1] = -1;
197 static bool py_cli_state_setup_ev(struct py_cli_state *self)
199 struct py_cli_thread *t = NULL;
202 self->ev = tevent_context_init_byname(NULL, "poll_mt");
203 if (self->ev == NULL) {
206 samba_tevent_set_debug(self->ev, "pylibsmb_tevent_mt");
207 tevent_set_trace_callback(self->ev, py_cli_state_trace_callback, self);
209 self->thread_state = talloc_zero(NULL, struct py_cli_thread);
210 if (self->thread_state == NULL) {
213 t = self->thread_state;
215 ret = pipe(t->shutdown_pipe);
219 t->shutdown_fde = tevent_add_fd(
220 self->ev, self->ev, t->shutdown_pipe[0], TEVENT_FD_READ,
221 py_cli_state_shutdown_handler, self);
222 if (t->shutdown_fde == NULL) {
226 PyEval_InitThreads();
228 ret = pthread_create(&t->id, NULL, py_cli_state_poll_thread, self);
232 talloc_set_destructor(self->thread_state, py_cli_thread_destructor);
237 TALLOC_FREE(t->shutdown_fde);
239 if (t->shutdown_pipe[0] != -1) {
240 close(t->shutdown_pipe[0]);
241 t->shutdown_pipe[0] = -1;
243 if (t->shutdown_pipe[1] != -1) {
244 close(t->shutdown_pipe[1]);
245 t->shutdown_pipe[1] = -1;
249 TALLOC_FREE(self->thread_state);
250 TALLOC_FREE(self->ev);
254 struct py_tevent_cond {
255 pthread_mutex_t mutex;
260 static void py_tevent_signalme(struct tevent_req *req);
262 static int py_tevent_cond_wait(struct py_tevent_cond *cond)
266 result = pthread_mutex_init(&cond->mutex, NULL);
270 result = pthread_cond_init(&cond->cond, NULL);
275 result = pthread_mutex_lock(&cond->mutex);
280 cond->is_done = false;
282 while (!cond->is_done) {
284 Py_BEGIN_ALLOW_THREADS
285 result = pthread_cond_wait(&cond->cond, &cond->mutex);
294 ret = pthread_mutex_unlock(&cond->mutex);
297 ret = pthread_cond_destroy(&cond->cond);
300 ret = pthread_mutex_destroy(&cond->mutex);
306 static int py_tevent_req_wait(struct tevent_context *ev,
307 struct tevent_req *req)
309 struct py_tevent_cond cond;
310 tevent_req_set_callback(req, py_tevent_signalme, &cond);
311 return py_tevent_cond_wait(&cond);
314 static void py_tevent_cond_signal(struct py_tevent_cond *cond)
318 ret = pthread_mutex_lock(&cond->mutex);
321 cond->is_done = true;
323 ret = pthread_cond_signal(&cond->cond);
325 ret = pthread_mutex_unlock(&cond->mutex);
329 static void py_tevent_signalme(struct tevent_req *req)
331 struct py_tevent_cond *cond = (struct py_tevent_cond *)
332 tevent_req_callback_data_void(req);
334 py_tevent_cond_signal(cond);
339 static bool py_cli_state_setup_ev(struct py_cli_state *self)
341 self->ev = tevent_context_init(NULL);
342 if (self->ev == NULL) {
346 samba_tevent_set_debug(self->ev, "pylibsmb_tevent");
351 static int py_tevent_req_wait(struct tevent_context *ev,
352 struct tevent_req *req)
354 while (tevent_req_is_in_progress(req)) {
357 ret = tevent_loop_once(ev);
367 static bool py_tevent_req_wait_exc(struct tevent_context *ev,
368 struct tevent_req *req)
376 ret = py_tevent_req_wait(ev, req);
380 PyErr_SetFromErrno(PyExc_RuntimeError);
386 static PyObject *py_cli_state_new(PyTypeObject *type, PyObject *args,
389 struct py_cli_state *self;
391 self = (struct py_cli_state *)type->tp_alloc(type, 0);
397 self->thread_state = NULL;
398 self->oplock_waiter = NULL;
399 self->oplock_cond = NULL;
400 self->oplock_breaks = NULL;
401 return (PyObject *)self;
404 static void py_cli_got_oplock_break(struct tevent_req *req);
406 static int py_cli_state_init(struct py_cli_state *self, PyObject *args,
411 PyObject *creds = NULL;
412 struct cli_credentials *cli_creds;
413 struct tevent_req *req;
416 * For now we only support SMB1,
417 * as most of the cli_*_send() function
418 * don't support SMB2, it's only plugged
419 * into the sync wrapper functions currently.
421 int flags = CLI_FULL_CONNECTION_FORCE_SMB1;
423 static const char *kwlist[] = {
424 "host", "share", "credentials", NULL
427 PyTypeObject *py_type_Credentials = get_pytype(
428 "samba.credentials", "Credentials");
429 if (py_type_Credentials == NULL) {
433 ret = ParseTupleAndKeywords(
434 args, kwds, "ss|O!", kwlist,
435 &host, &share, py_type_Credentials, &creds);
437 Py_DECREF(py_type_Credentials);
443 if (!py_cli_state_setup_ev(self)) {
448 cli_creds = cli_credentials_init_anon(NULL);
450 cli_creds = PyCredentials_AsCliCredentials(creds);
453 req = cli_full_connection_creds_send(
454 NULL, self->ev, "myname", host, NULL, 0, share, "?????",
455 cli_creds, flags, SMB_SIGNING_DEFAULT);
456 if (!py_tevent_req_wait_exc(self->ev, req)) {
459 status = cli_full_connection_creds_recv(req, &self->cli);
462 if (!NT_STATUS_IS_OK(status)) {
463 PyErr_SetNTSTATUS(status);
467 self->oplock_waiter = cli_smb_oplock_break_waiter_send(
468 self->ev, self->ev, self->cli);
469 if (self->oplock_waiter == NULL) {
473 tevent_req_set_callback(self->oplock_waiter, py_cli_got_oplock_break,
478 static void py_cli_got_oplock_break(struct tevent_req *req)
480 struct py_cli_state *self = (struct py_cli_state *)
481 tevent_req_callback_data_void(req);
482 struct py_cli_oplock_break b;
483 struct py_cli_oplock_break *tmp;
487 status = cli_smb_oplock_break_waiter_recv(req, &b.fnum, &b.level);
489 self->oplock_waiter = NULL;
491 if (!NT_STATUS_IS_OK(status)) {
495 num_breaks = talloc_array_length(self->oplock_breaks);
496 tmp = talloc_realloc(self->ev, self->oplock_breaks,
497 struct py_cli_oplock_break, num_breaks+1);
501 self->oplock_breaks = tmp;
502 self->oplock_breaks[num_breaks] = b;
504 if (self->oplock_cond != NULL) {
505 py_tevent_cond_signal(self->oplock_cond);
508 self->oplock_waiter = cli_smb_oplock_break_waiter_send(
509 self->ev, self->ev, self->cli);
510 if (self->oplock_waiter == NULL) {
513 tevent_req_set_callback(self->oplock_waiter, py_cli_got_oplock_break,
517 static PyObject *py_cli_get_oplock_break(struct py_cli_state *self,
520 size_t num_oplock_breaks;
522 if (!PyArg_ParseTuple(args, "")) {
526 if (self->oplock_cond != NULL) {
528 PyErr_SetFromErrno(PyExc_RuntimeError);
532 num_oplock_breaks = talloc_array_length(self->oplock_breaks);
534 if (num_oplock_breaks == 0) {
535 struct py_tevent_cond cond;
538 self->oplock_cond = &cond;
539 ret = py_tevent_cond_wait(&cond);
540 self->oplock_cond = NULL;
544 PyErr_SetFromErrno(PyExc_RuntimeError);
549 num_oplock_breaks = talloc_array_length(self->oplock_breaks);
550 if (num_oplock_breaks > 0) {
553 result = Py_BuildValue(
555 "fnum", self->oplock_breaks[0].fnum,
556 "level", self->oplock_breaks[0].level);
558 memmove(&self->oplock_breaks[0], &self->oplock_breaks[1],
559 sizeof(self->oplock_breaks[0]) *
560 (num_oplock_breaks - 1));
561 self->oplock_breaks = talloc_realloc(
562 NULL, self->oplock_breaks, struct py_cli_oplock_break,
563 num_oplock_breaks - 1);
570 static void py_cli_state_dealloc(struct py_cli_state *self)
572 TALLOC_FREE(self->thread_state);
573 TALLOC_FREE(self->oplock_waiter);
574 TALLOC_FREE(self->ev);
576 if (self->cli != NULL) {
577 cli_shutdown(self->cli);
580 Py_TYPE(self)->tp_free((PyObject *)self);
583 static PyObject *py_cli_create(struct py_cli_state *self, PyObject *args,
587 unsigned CreateFlags = 0;
588 unsigned DesiredAccess = FILE_GENERIC_READ;
589 unsigned FileAttributes = 0;
590 unsigned ShareAccess = 0;
591 unsigned CreateDisposition = FILE_OPEN;
592 unsigned CreateOptions = 0;
593 unsigned SecurityFlags = 0;
595 struct tevent_req *req;
598 static const char *kwlist[] = {
599 "Name", "CreateFlags", "DesiredAccess", "FileAttributes",
600 "ShareAccess", "CreateDisposition", "CreateOptions",
601 "SecurityFlags", NULL };
603 if (!ParseTupleAndKeywords(
604 args, kwds, "s|IIIIIII", kwlist,
605 &fname, &CreateFlags, &DesiredAccess, &FileAttributes,
606 &ShareAccess, &CreateDisposition, &CreateOptions,
611 req = cli_ntcreate_send(NULL, self->ev, self->cli, fname, CreateFlags,
612 DesiredAccess, FileAttributes, ShareAccess,
613 CreateDisposition, CreateOptions,
615 if (!py_tevent_req_wait_exc(self->ev, req)) {
618 status = cli_ntcreate_recv(req, &fnum, NULL);
621 if (!NT_STATUS_IS_OK(status)) {
622 PyErr_SetNTSTATUS(status);
625 return Py_BuildValue("I", (unsigned)fnum);
628 static PyObject *py_cli_close(struct py_cli_state *self, PyObject *args)
630 struct tevent_req *req;
634 if (!PyArg_ParseTuple(args, "i", &fnum)) {
638 req = cli_close_send(NULL, self->ev, self->cli, fnum);
639 if (!py_tevent_req_wait_exc(self->ev, req)) {
642 status = cli_close_recv(req);
645 if (!NT_STATUS_IS_OK(status)) {
646 PyErr_SetNTSTATUS(status);
652 static PyObject *py_cli_write(struct py_cli_state *self, PyObject *args,
659 unsigned long long offset;
660 struct tevent_req *req;
664 static const char *kwlist[] = {
665 "fnum", "buffer", "offset", "mode", NULL };
667 if (!ParseTupleAndKeywords(
668 args, kwds, "Is#K|I", kwlist,
669 &fnum, &buf, &buflen, &offset, &mode)) {
673 req = cli_write_andx_send(NULL, self->ev, self->cli, fnum, mode,
674 (uint8_t *)buf, offset, buflen);
675 if (!py_tevent_req_wait_exc(self->ev, req)) {
678 status = cli_write_andx_recv(req, &written);
681 if (!NT_STATUS_IS_OK(status)) {
682 PyErr_SetNTSTATUS(status);
685 return Py_BuildValue("K", (unsigned long long)written);
688 static PyObject *py_cli_read(struct py_cli_state *self, PyObject *args,
692 unsigned long long offset;
694 struct tevent_req *req;
700 static const char *kwlist[] = {
701 "fnum", "offset", "size", NULL };
703 if (!ParseTupleAndKeywords(
704 args, kwds, "IKI", kwlist, &fnum, &offset,
709 req = cli_read_andx_send(NULL, self->ev, self->cli, fnum,
711 if (!py_tevent_req_wait_exc(self->ev, req)) {
714 status = cli_read_andx_recv(req, &buflen, &buf);
716 if (!NT_STATUS_IS_OK(status)) {
718 PyErr_SetNTSTATUS(status);
721 result = Py_BuildValue("s#", (char *)buf, (int)buflen);
726 static PyObject *py_cli_ftruncate(struct py_cli_state *self, PyObject *args,
730 unsigned long long size;
731 struct tevent_req *req;
734 static const char *kwlist[] = {
735 "fnum", "size", NULL };
737 if (!ParseTupleAndKeywords(
738 args, kwds, "IK", kwlist, &fnum, &size)) {
742 req = cli_ftruncate_send(NULL, self->ev, self->cli, fnum, size);
743 if (!py_tevent_req_wait_exc(self->ev, req)) {
746 status = cli_ftruncate_recv(req);
749 if (!NT_STATUS_IS_OK(status)) {
750 PyErr_SetNTSTATUS(status);
756 static PyObject *py_cli_delete_on_close(struct py_cli_state *self,
761 struct tevent_req *req;
764 static const char *kwlist[] = {
765 "fnum", "flag", NULL };
767 if (!ParseTupleAndKeywords(
768 args, kwds, "II", kwlist, &fnum, &flag)) {
772 req = cli_nt_delete_on_close_send(NULL, self->ev, self->cli, fnum,
774 if (!py_tevent_req_wait_exc(self->ev, req)) {
777 status = cli_nt_delete_on_close_recv(req);
780 if (!NT_STATUS_IS_OK(status)) {
781 PyErr_SetNTSTATUS(status);
787 static PyObject *py_cli_list(struct py_cli_state *self,
793 FILE_ATTRIBUTE_DIRECTORY |
794 FILE_ATTRIBUTE_SYSTEM |
795 FILE_ATTRIBUTE_HIDDEN;
796 unsigned info_level = SMB_FIND_FILE_BOTH_DIRECTORY_INFO;
797 struct tevent_req *req;
799 struct file_info *finfos;
800 size_t i, num_finfos;
803 const char *kwlist[] = {
804 "mask", "attribute", "info_level", NULL
807 if (!ParseTupleAndKeywords(
808 args, kwds, "s|II", kwlist,
809 &mask, &attribute, &info_level)) {
813 req = cli_list_send(NULL, self->ev, self->cli, mask, attribute,
815 if (!py_tevent_req_wait_exc(self->ev, req)) {
818 status = cli_list_recv(req, NULL, &finfos, &num_finfos);
821 if (!NT_STATUS_IS_OK(status)) {
822 PyErr_SetNTSTATUS(status);
826 result = Py_BuildValue("[]");
827 if (result == NULL) {
831 for (i=0; i<num_finfos; i++) {
832 struct file_info *finfo = &finfos[i];
836 file = Py_BuildValue(
839 "mode", (int)finfo->mode);
845 ret = PyList_Append(result, file);
855 static PyMethodDef py_cli_state_methods[] = {
856 { "create", (PyCFunction)py_cli_create, METH_VARARGS|METH_KEYWORDS,
858 { "close", (PyCFunction)py_cli_close, METH_VARARGS,
859 "Close a file handle" },
860 { "write", (PyCFunction)py_cli_write, METH_VARARGS|METH_KEYWORDS,
861 "Write to a file handle" },
862 { "read", (PyCFunction)py_cli_read, METH_VARARGS|METH_KEYWORDS,
863 "Read from a file handle" },
864 { "truncate", (PyCFunction)py_cli_ftruncate,
865 METH_VARARGS|METH_KEYWORDS,
867 { "delete_on_close", (PyCFunction)py_cli_delete_on_close,
868 METH_VARARGS|METH_KEYWORDS,
869 "Set/Reset the delete on close flag" },
870 { "readdir", (PyCFunction)py_cli_list,
871 METH_VARARGS|METH_KEYWORDS,
872 "List a directory" },
873 { "get_oplock_break", (PyCFunction)py_cli_get_oplock_break,
874 METH_VARARGS, "Wait for an oplock break" },
875 { NULL, NULL, 0, NULL }
878 static PyTypeObject py_cli_state_type = {
879 PyVarObject_HEAD_INIT(NULL, 0)
880 .tp_name = "libsmb_samba_internal.Conn",
881 .tp_basicsize = sizeof(struct py_cli_state),
882 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
883 .tp_doc = "libsmb connection",
884 .tp_new = py_cli_state_new,
885 .tp_init = (initproc)py_cli_state_init,
886 .tp_dealloc = (destructor)py_cli_state_dealloc,
887 .tp_methods = py_cli_state_methods,
890 static PyMethodDef py_libsmb_methods[] = {
894 void initlibsmb_samba_internal(void);
896 static struct PyModuleDef moduledef = {
897 PyModuleDef_HEAD_INIT,
898 .m_name = "libsmb_samba_internal",
899 .m_doc = "libsmb wrapper",
901 .m_methods = py_libsmb_methods,
904 MODULE_INIT_FUNC(libsmb_samba_internal)
910 m = PyModule_Create(&moduledef);
914 if (PyType_Ready(&py_cli_state_type) < 0) {
917 Py_INCREF(&py_cli_state_type);
918 PyModule_AddObject(m, "Conn", (PyObject *)&py_cli_state_type);