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 int (*req_wait_fn
)(struct tevent_context
*ev
,
85 struct tevent_req
*req
);
86 struct py_cli_thread
*thread_state
;
88 struct tevent_req
*oplock_waiter
;
89 struct py_cli_oplock_break
*oplock_breaks
;
90 struct py_tevent_cond
*oplock_cond
;
97 struct py_cli_thread
{
100 * Pipe to make the poll thread wake up in our destructor, so
101 * that we can exit and join the thread.
103 int shutdown_pipe
[2];
104 struct tevent_fd
*shutdown_fde
;
109 * Thread state to release the GIL during the poll(2) syscall
111 PyThreadState
*py_threadstate
;
114 static void *py_cli_state_poll_thread(void *private_data
)
116 struct py_cli_state
*self
= (struct py_cli_state
*)private_data
;
117 struct py_cli_thread
*t
= self
->thread_state
;
118 PyGILState_STATE gstate
;
120 gstate
= PyGILState_Ensure();
122 while (!t
->do_shutdown
) {
124 ret
= tevent_loop_once(self
->ev
);
127 PyGILState_Release(gstate
);
131 static void py_cli_state_trace_callback(enum tevent_trace_point point
,
134 struct py_cli_state
*self
= (struct py_cli_state
*)private_data
;
135 struct py_cli_thread
*t
= self
->thread_state
;
138 case TEVENT_TRACE_BEFORE_WAIT
:
139 assert(t
->py_threadstate
== NULL
);
140 t
->py_threadstate
= PyEval_SaveThread();
142 case TEVENT_TRACE_AFTER_WAIT
:
143 assert(t
->py_threadstate
!= NULL
);
144 PyEval_RestoreThread(t
->py_threadstate
);
145 t
->py_threadstate
= NULL
;
152 static void py_cli_state_shutdown_handler(struct tevent_context
*ev
,
153 struct tevent_fd
*fde
,
157 struct py_cli_state
*self
= (struct py_cli_state
*)private_data
;
158 struct py_cli_thread
*t
= self
->thread_state
;
160 if ((flags
& TEVENT_FD_READ
) == 0) {
163 TALLOC_FREE(t
->shutdown_fde
);
164 t
->do_shutdown
= true;
167 static int py_cli_thread_destructor(struct py_cli_thread
*t
)
175 * This will wake the poll thread from the poll(2)
177 written
= write(t
->shutdown_pipe
[1], &c
, 1);
178 } while ((written
== -1) && (errno
== EINTR
));
181 * Allow the poll thread to do its own cleanup under the GIL
183 Py_BEGIN_ALLOW_THREADS
184 ret
= pthread_join(t
->id
, NULL
);
188 if (t
->shutdown_pipe
[0] != -1) {
189 close(t
->shutdown_pipe
[0]);
190 t
->shutdown_pipe
[0] = -1;
192 if (t
->shutdown_pipe
[1] != -1) {
193 close(t
->shutdown_pipe
[1]);
194 t
->shutdown_pipe
[1] = -1;
199 static int py_tevent_cond_req_wait(struct tevent_context
*ev
,
200 struct tevent_req
*req
);
202 static bool py_cli_state_setup_mt_ev(struct py_cli_state
*self
)
204 struct py_cli_thread
*t
= NULL
;
207 self
->ev
= tevent_context_init_byname(NULL
, "poll_mt");
208 if (self
->ev
== NULL
) {
211 samba_tevent_set_debug(self
->ev
, "pylibsmb_tevent_mt");
212 tevent_set_trace_callback(self
->ev
, py_cli_state_trace_callback
, self
);
214 self
->req_wait_fn
= py_tevent_cond_req_wait
;
216 self
->thread_state
= talloc_zero(NULL
, struct py_cli_thread
);
217 if (self
->thread_state
== NULL
) {
220 t
= self
->thread_state
;
222 ret
= pipe(t
->shutdown_pipe
);
226 t
->shutdown_fde
= tevent_add_fd(
227 self
->ev
, self
->ev
, t
->shutdown_pipe
[0], TEVENT_FD_READ
,
228 py_cli_state_shutdown_handler
, self
);
229 if (t
->shutdown_fde
== NULL
) {
233 PyEval_InitThreads();
235 ret
= pthread_create(&t
->id
, NULL
, py_cli_state_poll_thread
, self
);
239 talloc_set_destructor(self
->thread_state
, py_cli_thread_destructor
);
244 TALLOC_FREE(t
->shutdown_fde
);
246 if (t
->shutdown_pipe
[0] != -1) {
247 close(t
->shutdown_pipe
[0]);
248 t
->shutdown_pipe
[0] = -1;
250 if (t
->shutdown_pipe
[1] != -1) {
251 close(t
->shutdown_pipe
[1]);
252 t
->shutdown_pipe
[1] = -1;
256 TALLOC_FREE(self
->thread_state
);
257 TALLOC_FREE(self
->ev
);
261 struct py_tevent_cond
{
262 pthread_mutex_t mutex
;
267 static void py_tevent_signalme(struct tevent_req
*req
);
269 static int py_tevent_cond_wait(struct py_tevent_cond
*cond
)
273 result
= pthread_mutex_init(&cond
->mutex
, NULL
);
277 result
= pthread_cond_init(&cond
->cond
, NULL
);
282 result
= pthread_mutex_lock(&cond
->mutex
);
287 cond
->is_done
= false;
289 while (!cond
->is_done
) {
291 Py_BEGIN_ALLOW_THREADS
292 result
= pthread_cond_wait(&cond
->cond
, &cond
->mutex
);
301 ret
= pthread_mutex_unlock(&cond
->mutex
);
304 ret
= pthread_cond_destroy(&cond
->cond
);
307 ret
= pthread_mutex_destroy(&cond
->mutex
);
313 static int py_tevent_cond_req_wait(struct tevent_context
*ev
,
314 struct tevent_req
*req
)
316 struct py_tevent_cond cond
;
317 tevent_req_set_callback(req
, py_tevent_signalme
, &cond
);
318 return py_tevent_cond_wait(&cond
);
321 static void py_tevent_cond_signal(struct py_tevent_cond
*cond
)
325 ret
= pthread_mutex_lock(&cond
->mutex
);
328 cond
->is_done
= true;
330 ret
= pthread_cond_signal(&cond
->cond
);
332 ret
= pthread_mutex_unlock(&cond
->mutex
);
336 static void py_tevent_signalme(struct tevent_req
*req
)
338 struct py_tevent_cond
*cond
= (struct py_tevent_cond
*)
339 tevent_req_callback_data_void(req
);
341 py_tevent_cond_signal(cond
);
346 static int py_tevent_req_wait(struct tevent_context
*ev
,
347 struct tevent_req
*req
);
349 static bool py_cli_state_setup_ev(struct py_cli_state
*self
)
351 self
->ev
= tevent_context_init(NULL
);
352 if (self
->ev
== NULL
) {
356 samba_tevent_set_debug(self
->ev
, "pylibsmb_tevent");
358 self
->req_wait_fn
= py_tevent_req_wait
;
363 static int py_tevent_req_wait(struct tevent_context
*ev
,
364 struct tevent_req
*req
)
366 while (tevent_req_is_in_progress(req
)) {
369 ret
= tevent_loop_once(ev
);
377 static bool py_tevent_req_wait_exc(struct py_cli_state
*self
,
378 struct tevent_req
*req
)
386 ret
= self
->req_wait_fn(self
->ev
, req
);
390 PyErr_SetFromErrno(PyExc_RuntimeError
);
396 static PyObject
*py_cli_state_new(PyTypeObject
*type
, PyObject
*args
,
399 struct py_cli_state
*self
;
401 self
= (struct py_cli_state
*)type
->tp_alloc(type
, 0);
407 self
->thread_state
= NULL
;
408 self
->oplock_waiter
= NULL
;
409 self
->oplock_cond
= NULL
;
410 self
->oplock_breaks
= NULL
;
411 return (PyObject
*)self
;
414 static void py_cli_got_oplock_break(struct tevent_req
*req
);
416 static int py_cli_state_init(struct py_cli_state
*self
, PyObject
*args
,
421 PyObject
*creds
= NULL
;
422 struct cli_credentials
*cli_creds
;
423 PyObject
*py_multi_threaded
= Py_False
;
424 bool multi_threaded
= false;
425 PyObject
*py_sign
= Py_False
;
427 int signing_state
= SMB_SIGNING_DEFAULT
;
428 struct tevent_req
*req
;
431 * For now we only support SMB1,
432 * as most of the cli_*_send() function
433 * don't support SMB2, it's only plugged
434 * into the sync wrapper functions currently.
436 int flags
= CLI_FULL_CONNECTION_FORCE_SMB1
;
438 static const char *kwlist
[] = {
439 "host", "share", "credentials",
440 "multi_threaded", "sign", NULL
443 PyTypeObject
*py_type_Credentials
= get_pytype(
444 "samba.credentials", "Credentials");
445 if (py_type_Credentials
== NULL
) {
449 ret
= ParseTupleAndKeywords(
450 args
, kwds
, "ss|O!OO", kwlist
,
452 py_type_Credentials
, &creds
,
456 Py_DECREF(py_type_Credentials
);
462 multi_threaded
= PyObject_IsTrue(py_multi_threaded
);
463 sign
= PyObject_IsTrue(py_sign
);
466 signing_state
= SMB_SIGNING_REQUIRED
;
469 if (multi_threaded
) {
471 ret
= py_cli_state_setup_mt_ev(self
);
476 PyErr_SetString(PyExc_RuntimeError
,
477 "No PTHREAD support available");
481 ret
= py_cli_state_setup_ev(self
);
488 cli_creds
= cli_credentials_init_anon(NULL
);
490 cli_creds
= PyCredentials_AsCliCredentials(creds
);
493 req
= cli_full_connection_creds_send(
494 NULL
, self
->ev
, "myname", host
, NULL
, 0, share
, "?????",
495 cli_creds
, flags
, signing_state
);
496 if (!py_tevent_req_wait_exc(self
, req
)) {
499 status
= cli_full_connection_creds_recv(req
, &self
->cli
);
502 if (!NT_STATUS_IS_OK(status
)) {
503 PyErr_SetNTSTATUS(status
);
507 self
->oplock_waiter
= cli_smb_oplock_break_waiter_send(
508 self
->ev
, self
->ev
, self
->cli
);
509 if (self
->oplock_waiter
== NULL
) {
513 tevent_req_set_callback(self
->oplock_waiter
, py_cli_got_oplock_break
,
518 static void py_cli_got_oplock_break(struct tevent_req
*req
)
520 struct py_cli_state
*self
= (struct py_cli_state
*)
521 tevent_req_callback_data_void(req
);
522 struct py_cli_oplock_break b
;
523 struct py_cli_oplock_break
*tmp
;
527 status
= cli_smb_oplock_break_waiter_recv(req
, &b
.fnum
, &b
.level
);
529 self
->oplock_waiter
= NULL
;
531 if (!NT_STATUS_IS_OK(status
)) {
535 num_breaks
= talloc_array_length(self
->oplock_breaks
);
536 tmp
= talloc_realloc(self
->ev
, self
->oplock_breaks
,
537 struct py_cli_oplock_break
, num_breaks
+1);
541 self
->oplock_breaks
= tmp
;
542 self
->oplock_breaks
[num_breaks
] = b
;
544 if (self
->oplock_cond
!= NULL
) {
545 py_tevent_cond_signal(self
->oplock_cond
);
548 self
->oplock_waiter
= cli_smb_oplock_break_waiter_send(
549 self
->ev
, self
->ev
, self
->cli
);
550 if (self
->oplock_waiter
== NULL
) {
553 tevent_req_set_callback(self
->oplock_waiter
, py_cli_got_oplock_break
,
557 static PyObject
*py_cli_get_oplock_break(struct py_cli_state
*self
,
560 size_t num_oplock_breaks
;
562 if (!PyArg_ParseTuple(args
, "")) {
566 if (self
->oplock_cond
!= NULL
) {
568 PyErr_SetFromErrno(PyExc_RuntimeError
);
572 num_oplock_breaks
= talloc_array_length(self
->oplock_breaks
);
574 if (num_oplock_breaks
== 0) {
575 struct py_tevent_cond cond
;
578 self
->oplock_cond
= &cond
;
579 ret
= py_tevent_cond_wait(&cond
);
580 self
->oplock_cond
= NULL
;
584 PyErr_SetFromErrno(PyExc_RuntimeError
);
589 num_oplock_breaks
= talloc_array_length(self
->oplock_breaks
);
590 if (num_oplock_breaks
> 0) {
593 result
= Py_BuildValue(
595 "fnum", self
->oplock_breaks
[0].fnum
,
596 "level", self
->oplock_breaks
[0].level
);
598 memmove(&self
->oplock_breaks
[0], &self
->oplock_breaks
[1],
599 sizeof(self
->oplock_breaks
[0]) *
600 (num_oplock_breaks
- 1));
601 self
->oplock_breaks
= talloc_realloc(
602 NULL
, self
->oplock_breaks
, struct py_cli_oplock_break
,
603 num_oplock_breaks
- 1);
610 static void py_cli_state_dealloc(struct py_cli_state
*self
)
612 TALLOC_FREE(self
->thread_state
);
613 TALLOC_FREE(self
->oplock_waiter
);
614 TALLOC_FREE(self
->ev
);
616 if (self
->cli
!= NULL
) {
617 cli_shutdown(self
->cli
);
620 Py_TYPE(self
)->tp_free((PyObject
*)self
);
623 static PyObject
*py_cli_create(struct py_cli_state
*self
, PyObject
*args
,
627 unsigned CreateFlags
= 0;
628 unsigned DesiredAccess
= FILE_GENERIC_READ
;
629 unsigned FileAttributes
= 0;
630 unsigned ShareAccess
= 0;
631 unsigned CreateDisposition
= FILE_OPEN
;
632 unsigned CreateOptions
= 0;
633 unsigned SecurityFlags
= 0;
635 struct tevent_req
*req
;
638 static const char *kwlist
[] = {
639 "Name", "CreateFlags", "DesiredAccess", "FileAttributes",
640 "ShareAccess", "CreateDisposition", "CreateOptions",
641 "SecurityFlags", NULL
};
643 if (!ParseTupleAndKeywords(
644 args
, kwds
, "s|IIIIIII", kwlist
,
645 &fname
, &CreateFlags
, &DesiredAccess
, &FileAttributes
,
646 &ShareAccess
, &CreateDisposition
, &CreateOptions
,
651 req
= cli_ntcreate_send(NULL
, self
->ev
, self
->cli
, fname
, CreateFlags
,
652 DesiredAccess
, FileAttributes
, ShareAccess
,
653 CreateDisposition
, CreateOptions
,
655 if (!py_tevent_req_wait_exc(self
, req
)) {
658 status
= cli_ntcreate_recv(req
, &fnum
, NULL
);
661 if (!NT_STATUS_IS_OK(status
)) {
662 PyErr_SetNTSTATUS(status
);
665 return Py_BuildValue("I", (unsigned)fnum
);
668 static PyObject
*py_cli_close(struct py_cli_state
*self
, PyObject
*args
)
670 struct tevent_req
*req
;
674 if (!PyArg_ParseTuple(args
, "i", &fnum
)) {
678 req
= cli_close_send(NULL
, self
->ev
, self
->cli
, fnum
);
679 if (!py_tevent_req_wait_exc(self
, req
)) {
682 status
= cli_close_recv(req
);
685 if (!NT_STATUS_IS_OK(status
)) {
686 PyErr_SetNTSTATUS(status
);
692 static PyObject
*py_cli_write(struct py_cli_state
*self
, PyObject
*args
,
699 unsigned long long offset
;
700 struct tevent_req
*req
;
704 static const char *kwlist
[] = {
705 "fnum", "buffer", "offset", "mode", NULL
};
707 if (!ParseTupleAndKeywords(
708 args
, kwds
, "Is#K|I", kwlist
,
709 &fnum
, &buf
, &buflen
, &offset
, &mode
)) {
713 req
= cli_write_andx_send(NULL
, self
->ev
, self
->cli
, fnum
, mode
,
714 (uint8_t *)buf
, offset
, buflen
);
715 if (!py_tevent_req_wait_exc(self
, req
)) {
718 status
= cli_write_andx_recv(req
, &written
);
721 if (!NT_STATUS_IS_OK(status
)) {
722 PyErr_SetNTSTATUS(status
);
725 return Py_BuildValue("K", (unsigned long long)written
);
728 static PyObject
*py_cli_read(struct py_cli_state
*self
, PyObject
*args
,
732 unsigned long long offset
;
734 struct tevent_req
*req
;
740 static const char *kwlist
[] = {
741 "fnum", "offset", "size", NULL
};
743 if (!ParseTupleAndKeywords(
744 args
, kwds
, "IKI", kwlist
, &fnum
, &offset
,
749 req
= cli_read_andx_send(NULL
, self
->ev
, self
->cli
, fnum
,
751 if (!py_tevent_req_wait_exc(self
, req
)) {
754 status
= cli_read_andx_recv(req
, &buflen
, &buf
);
756 if (!NT_STATUS_IS_OK(status
)) {
758 PyErr_SetNTSTATUS(status
);
761 result
= Py_BuildValue("s#", (char *)buf
, (int)buflen
);
766 static PyObject
*py_cli_ftruncate(struct py_cli_state
*self
, PyObject
*args
,
770 unsigned long long size
;
771 struct tevent_req
*req
;
774 static const char *kwlist
[] = {
775 "fnum", "size", NULL
};
777 if (!ParseTupleAndKeywords(
778 args
, kwds
, "IK", kwlist
, &fnum
, &size
)) {
782 req
= cli_ftruncate_send(NULL
, self
->ev
, self
->cli
, fnum
, size
);
783 if (!py_tevent_req_wait_exc(self
, req
)) {
786 status
= cli_ftruncate_recv(req
);
789 if (!NT_STATUS_IS_OK(status
)) {
790 PyErr_SetNTSTATUS(status
);
796 static PyObject
*py_cli_delete_on_close(struct py_cli_state
*self
,
801 struct tevent_req
*req
;
804 static const char *kwlist
[] = {
805 "fnum", "flag", NULL
};
807 if (!ParseTupleAndKeywords(
808 args
, kwds
, "II", kwlist
, &fnum
, &flag
)) {
812 req
= cli_nt_delete_on_close_send(NULL
, self
->ev
, self
->cli
, fnum
,
814 if (!py_tevent_req_wait_exc(self
, req
)) {
817 status
= cli_nt_delete_on_close_recv(req
);
820 if (!NT_STATUS_IS_OK(status
)) {
821 PyErr_SetNTSTATUS(status
);
827 static PyObject
*py_cli_list(struct py_cli_state
*self
,
833 FILE_ATTRIBUTE_DIRECTORY
|
834 FILE_ATTRIBUTE_SYSTEM
|
835 FILE_ATTRIBUTE_HIDDEN
;
836 unsigned info_level
= SMB_FIND_FILE_BOTH_DIRECTORY_INFO
;
837 struct tevent_req
*req
;
839 struct file_info
*finfos
;
840 size_t i
, num_finfos
;
843 const char *kwlist
[] = {
844 "mask", "attribute", "info_level", NULL
847 if (!ParseTupleAndKeywords(
848 args
, kwds
, "s|II", kwlist
,
849 &mask
, &attribute
, &info_level
)) {
853 req
= cli_list_send(NULL
, self
->ev
, self
->cli
, mask
, attribute
,
855 if (!py_tevent_req_wait_exc(self
, req
)) {
858 status
= cli_list_recv(req
, NULL
, &finfos
, &num_finfos
);
861 if (!NT_STATUS_IS_OK(status
)) {
862 PyErr_SetNTSTATUS(status
);
866 result
= Py_BuildValue("[]");
867 if (result
== NULL
) {
871 for (i
=0; i
<num_finfos
; i
++) {
872 struct file_info
*finfo
= &finfos
[i
];
876 file
= Py_BuildValue(
879 "mode", (int)finfo
->mode
);
885 ret
= PyList_Append(result
, file
);
895 static PyMethodDef py_cli_state_methods
[] = {
896 { "create", (PyCFunction
)py_cli_create
, METH_VARARGS
|METH_KEYWORDS
,
898 { "close", (PyCFunction
)py_cli_close
, METH_VARARGS
,
899 "Close a file handle" },
900 { "write", (PyCFunction
)py_cli_write
, METH_VARARGS
|METH_KEYWORDS
,
901 "Write to a file handle" },
902 { "read", (PyCFunction
)py_cli_read
, METH_VARARGS
|METH_KEYWORDS
,
903 "Read from a file handle" },
904 { "truncate", (PyCFunction
)py_cli_ftruncate
,
905 METH_VARARGS
|METH_KEYWORDS
,
907 { "delete_on_close", (PyCFunction
)py_cli_delete_on_close
,
908 METH_VARARGS
|METH_KEYWORDS
,
909 "Set/Reset the delete on close flag" },
910 { "readdir", (PyCFunction
)py_cli_list
,
911 METH_VARARGS
|METH_KEYWORDS
,
912 "List a directory" },
913 { "get_oplock_break", (PyCFunction
)py_cli_get_oplock_break
,
914 METH_VARARGS
, "Wait for an oplock break" },
915 { NULL
, NULL
, 0, NULL
}
918 static PyTypeObject py_cli_state_type
= {
919 PyVarObject_HEAD_INIT(NULL
, 0)
920 .tp_name
= "libsmb_samba_internal.Conn",
921 .tp_basicsize
= sizeof(struct py_cli_state
),
922 .tp_flags
= Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_BASETYPE
,
923 .tp_doc
= "libsmb connection",
924 .tp_new
= py_cli_state_new
,
925 .tp_init
= (initproc
)py_cli_state_init
,
926 .tp_dealloc
= (destructor
)py_cli_state_dealloc
,
927 .tp_methods
= py_cli_state_methods
,
930 static PyMethodDef py_libsmb_methods
[] = {
934 void initlibsmb_samba_internal(void);
936 static struct PyModuleDef moduledef
= {
937 PyModuleDef_HEAD_INIT
,
938 .m_name
= "libsmb_samba_internal",
939 .m_doc
= "libsmb wrapper",
941 .m_methods
= py_libsmb_methods
,
944 MODULE_INIT_FUNC(libsmb_samba_internal
)
950 m
= PyModule_Create(&moduledef
);
954 if (PyType_Ready(&py_cli_state_type
) < 0) {
957 Py_INCREF(&py_cli_state_type
);
958 PyModule_AddObject(m
, "Conn", (PyObject
*)&py_cli_state_type
);