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 "libcli/smb/smbXcli_base.h"
25 #include "libsmb/libsmb.h"
26 #include "libcli/security/security.h"
27 #include "system/select.h"
28 #include "source4/libcli/util/pyerrors.h"
29 #include "auth/credentials/pycredentials.h"
32 static PyTypeObject
*get_pytype(const char *module
, const char *type
)
37 mod
= PyImport_ImportModule(module
);
39 PyErr_Format(PyExc_RuntimeError
,
40 "Unable to import %s to check type %s",
44 result
= (PyTypeObject
*)PyObject_GetAttrString(mod
, type
);
47 PyErr_Format(PyExc_RuntimeError
,
48 "Unable to find type %s in module %s",
56 * We're using "const char * const *" for keywords,
57 * PyArg_ParseTupleAndKeywords expects a "char **". Confine the
58 * inevitable warnings to just one place.
60 static int ParseTupleAndKeywords(PyObject
*args
, PyObject
*kw
,
61 const char *format
, const char * const *keywords
,
64 char **_keywords
= discard_const_p(char *, keywords
);
67 va_start(a
, keywords
);
68 ret
= PyArg_VaParseTupleAndKeywords(args
, kw
, format
,
76 struct py_cli_oplock_break
{
83 struct cli_state
*cli
;
85 struct tevent_context
*ev
;
86 int (*req_wait_fn
)(struct tevent_context
*ev
,
87 struct tevent_req
*req
);
88 struct py_cli_thread
*thread_state
;
90 struct tevent_req
*oplock_waiter
;
91 struct py_cli_oplock_break
*oplock_breaks
;
92 struct py_tevent_cond
*oplock_cond
;
99 struct py_cli_thread
{
102 * Pipe to make the poll thread wake up in our destructor, so
103 * that we can exit and join the thread.
105 int shutdown_pipe
[2];
106 struct tevent_fd
*shutdown_fde
;
111 * Thread state to release the GIL during the poll(2) syscall
113 PyThreadState
*py_threadstate
;
116 static void *py_cli_state_poll_thread(void *private_data
)
118 struct py_cli_state
*self
= (struct py_cli_state
*)private_data
;
119 struct py_cli_thread
*t
= self
->thread_state
;
120 PyGILState_STATE gstate
;
122 gstate
= PyGILState_Ensure();
124 while (!t
->do_shutdown
) {
126 ret
= tevent_loop_once(self
->ev
);
129 PyGILState_Release(gstate
);
133 static void py_cli_state_trace_callback(enum tevent_trace_point point
,
136 struct py_cli_state
*self
= (struct py_cli_state
*)private_data
;
137 struct py_cli_thread
*t
= self
->thread_state
;
140 case TEVENT_TRACE_BEFORE_WAIT
:
141 assert(t
->py_threadstate
== NULL
);
142 t
->py_threadstate
= PyEval_SaveThread();
144 case TEVENT_TRACE_AFTER_WAIT
:
145 assert(t
->py_threadstate
!= NULL
);
146 PyEval_RestoreThread(t
->py_threadstate
);
147 t
->py_threadstate
= NULL
;
154 static void py_cli_state_shutdown_handler(struct tevent_context
*ev
,
155 struct tevent_fd
*fde
,
159 struct py_cli_state
*self
= (struct py_cli_state
*)private_data
;
160 struct py_cli_thread
*t
= self
->thread_state
;
162 if ((flags
& TEVENT_FD_READ
) == 0) {
165 TALLOC_FREE(t
->shutdown_fde
);
166 t
->do_shutdown
= true;
169 static int py_cli_thread_destructor(struct py_cli_thread
*t
)
177 * This will wake the poll thread from the poll(2)
179 written
= write(t
->shutdown_pipe
[1], &c
, 1);
180 } while ((written
== -1) && (errno
== EINTR
));
183 * Allow the poll thread to do its own cleanup under the GIL
185 Py_BEGIN_ALLOW_THREADS
186 ret
= pthread_join(t
->id
, NULL
);
190 if (t
->shutdown_pipe
[0] != -1) {
191 close(t
->shutdown_pipe
[0]);
192 t
->shutdown_pipe
[0] = -1;
194 if (t
->shutdown_pipe
[1] != -1) {
195 close(t
->shutdown_pipe
[1]);
196 t
->shutdown_pipe
[1] = -1;
201 static int py_tevent_cond_req_wait(struct tevent_context
*ev
,
202 struct tevent_req
*req
);
204 static bool py_cli_state_setup_mt_ev(struct py_cli_state
*self
)
206 struct py_cli_thread
*t
= NULL
;
209 self
->ev
= tevent_context_init_byname(NULL
, "poll_mt");
210 if (self
->ev
== NULL
) {
213 samba_tevent_set_debug(self
->ev
, "pylibsmb_tevent_mt");
214 tevent_set_trace_callback(self
->ev
, py_cli_state_trace_callback
, self
);
216 self
->req_wait_fn
= py_tevent_cond_req_wait
;
218 self
->thread_state
= talloc_zero(NULL
, struct py_cli_thread
);
219 if (self
->thread_state
== NULL
) {
222 t
= self
->thread_state
;
224 ret
= pipe(t
->shutdown_pipe
);
228 t
->shutdown_fde
= tevent_add_fd(
229 self
->ev
, self
->ev
, t
->shutdown_pipe
[0], TEVENT_FD_READ
,
230 py_cli_state_shutdown_handler
, self
);
231 if (t
->shutdown_fde
== NULL
) {
235 PyEval_InitThreads();
237 ret
= pthread_create(&t
->id
, NULL
, py_cli_state_poll_thread
, self
);
241 talloc_set_destructor(self
->thread_state
, py_cli_thread_destructor
);
246 TALLOC_FREE(t
->shutdown_fde
);
248 if (t
->shutdown_pipe
[0] != -1) {
249 close(t
->shutdown_pipe
[0]);
250 t
->shutdown_pipe
[0] = -1;
252 if (t
->shutdown_pipe
[1] != -1) {
253 close(t
->shutdown_pipe
[1]);
254 t
->shutdown_pipe
[1] = -1;
258 TALLOC_FREE(self
->thread_state
);
259 TALLOC_FREE(self
->ev
);
263 struct py_tevent_cond
{
264 pthread_mutex_t mutex
;
269 static void py_tevent_signalme(struct tevent_req
*req
);
271 static int py_tevent_cond_wait(struct py_tevent_cond
*cond
)
275 result
= pthread_mutex_init(&cond
->mutex
, NULL
);
279 result
= pthread_cond_init(&cond
->cond
, NULL
);
284 result
= pthread_mutex_lock(&cond
->mutex
);
289 cond
->is_done
= false;
291 while (!cond
->is_done
) {
293 Py_BEGIN_ALLOW_THREADS
294 result
= pthread_cond_wait(&cond
->cond
, &cond
->mutex
);
303 ret
= pthread_mutex_unlock(&cond
->mutex
);
306 ret
= pthread_cond_destroy(&cond
->cond
);
309 ret
= pthread_mutex_destroy(&cond
->mutex
);
315 static int py_tevent_cond_req_wait(struct tevent_context
*ev
,
316 struct tevent_req
*req
)
318 struct py_tevent_cond cond
;
319 tevent_req_set_callback(req
, py_tevent_signalme
, &cond
);
320 return py_tevent_cond_wait(&cond
);
323 static void py_tevent_cond_signal(struct py_tevent_cond
*cond
)
327 ret
= pthread_mutex_lock(&cond
->mutex
);
330 cond
->is_done
= true;
332 ret
= pthread_cond_signal(&cond
->cond
);
334 ret
= pthread_mutex_unlock(&cond
->mutex
);
338 static void py_tevent_signalme(struct tevent_req
*req
)
340 struct py_tevent_cond
*cond
= (struct py_tevent_cond
*)
341 tevent_req_callback_data_void(req
);
343 py_tevent_cond_signal(cond
);
348 static int py_tevent_req_wait(struct tevent_context
*ev
,
349 struct tevent_req
*req
);
351 static bool py_cli_state_setup_ev(struct py_cli_state
*self
)
353 self
->ev
= tevent_context_init(NULL
);
354 if (self
->ev
== NULL
) {
358 samba_tevent_set_debug(self
->ev
, "pylibsmb_tevent");
360 self
->req_wait_fn
= py_tevent_req_wait
;
365 static int py_tevent_req_wait(struct tevent_context
*ev
,
366 struct tevent_req
*req
)
368 while (tevent_req_is_in_progress(req
)) {
371 ret
= tevent_loop_once(ev
);
379 static bool py_tevent_req_wait_exc(struct py_cli_state
*self
,
380 struct tevent_req
*req
)
388 ret
= self
->req_wait_fn(self
->ev
, req
);
392 PyErr_SetFromErrno(PyExc_RuntimeError
);
398 static PyObject
*py_cli_state_new(PyTypeObject
*type
, PyObject
*args
,
401 struct py_cli_state
*self
;
403 self
= (struct py_cli_state
*)type
->tp_alloc(type
, 0);
408 self
->is_smb1
= false;
410 self
->thread_state
= NULL
;
411 self
->oplock_waiter
= NULL
;
412 self
->oplock_cond
= NULL
;
413 self
->oplock_breaks
= NULL
;
414 return (PyObject
*)self
;
417 static void py_cli_got_oplock_break(struct tevent_req
*req
);
419 static int py_cli_state_init(struct py_cli_state
*self
, PyObject
*args
,
424 PyObject
*creds
= NULL
;
425 struct cli_credentials
*cli_creds
;
426 PyObject
*py_multi_threaded
= Py_False
;
427 bool multi_threaded
= false;
428 PyObject
*py_sign
= Py_False
;
430 int signing_state
= SMB_SIGNING_DEFAULT
;
431 PyObject
*py_force_smb1
= Py_False
;
432 bool force_smb1
= false;
433 struct tevent_req
*req
;
437 static const char *kwlist
[] = {
438 "host", "share", "credentials",
439 "multi_threaded", "sign", "force_smb1",
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!OOO", kwlist
,
452 py_type_Credentials
, &creds
,
457 Py_DECREF(py_type_Credentials
);
463 multi_threaded
= PyObject_IsTrue(py_multi_threaded
);
464 sign
= PyObject_IsTrue(py_sign
);
465 force_smb1
= PyObject_IsTrue(py_force_smb1
);
468 signing_state
= SMB_SIGNING_REQUIRED
;
473 * As most of the cli_*_send() function
474 * don't support SMB2 (it's only plugged
475 * into the sync wrapper functions currently)
476 * we have a way to force SMB1.
478 flags
= CLI_FULL_CONNECTION_FORCE_SMB1
;
481 if (multi_threaded
) {
483 ret
= py_cli_state_setup_mt_ev(self
);
488 PyErr_SetString(PyExc_RuntimeError
,
489 "No PTHREAD support available");
493 PyErr_SetString(PyExc_RuntimeError
,
494 "multi_threaded is only possible on "
499 ret
= py_cli_state_setup_ev(self
);
506 cli_creds
= cli_credentials_init_anon(NULL
);
508 cli_creds
= PyCredentials_AsCliCredentials(creds
);
511 req
= cli_full_connection_creds_send(
512 NULL
, self
->ev
, "myname", host
, NULL
, 0, share
, "?????",
513 cli_creds
, flags
, signing_state
);
514 if (!py_tevent_req_wait_exc(self
, req
)) {
517 status
= cli_full_connection_creds_recv(req
, &self
->cli
);
520 if (!NT_STATUS_IS_OK(status
)) {
521 PyErr_SetNTSTATUS(status
);
525 if (smbXcli_conn_protocol(self
->cli
->conn
) < PROTOCOL_SMB2_02
) {
526 self
->is_smb1
= true;
530 * Oplocks require a multi threaded connection
532 if (self
->thread_state
== NULL
) {
536 self
->oplock_waiter
= cli_smb_oplock_break_waiter_send(
537 self
->ev
, self
->ev
, self
->cli
);
538 if (self
->oplock_waiter
== NULL
) {
542 tevent_req_set_callback(self
->oplock_waiter
, py_cli_got_oplock_break
,
547 static void py_cli_got_oplock_break(struct tevent_req
*req
)
549 struct py_cli_state
*self
= (struct py_cli_state
*)
550 tevent_req_callback_data_void(req
);
551 struct py_cli_oplock_break b
;
552 struct py_cli_oplock_break
*tmp
;
556 status
= cli_smb_oplock_break_waiter_recv(req
, &b
.fnum
, &b
.level
);
558 self
->oplock_waiter
= NULL
;
560 if (!NT_STATUS_IS_OK(status
)) {
564 num_breaks
= talloc_array_length(self
->oplock_breaks
);
565 tmp
= talloc_realloc(self
->ev
, self
->oplock_breaks
,
566 struct py_cli_oplock_break
, num_breaks
+1);
570 self
->oplock_breaks
= tmp
;
571 self
->oplock_breaks
[num_breaks
] = b
;
573 if (self
->oplock_cond
!= NULL
) {
574 py_tevent_cond_signal(self
->oplock_cond
);
577 self
->oplock_waiter
= cli_smb_oplock_break_waiter_send(
578 self
->ev
, self
->ev
, self
->cli
);
579 if (self
->oplock_waiter
== NULL
) {
582 tevent_req_set_callback(self
->oplock_waiter
, py_cli_got_oplock_break
,
586 static PyObject
*py_cli_get_oplock_break(struct py_cli_state
*self
,
589 size_t num_oplock_breaks
;
591 if (!PyArg_ParseTuple(args
, "")) {
595 if (self
->thread_state
== NULL
) {
596 PyErr_SetString(PyExc_RuntimeError
,
597 "get_oplock_break() only possible on "
598 "a multi_threaded connection");
602 if (self
->oplock_cond
!= NULL
) {
604 PyErr_SetFromErrno(PyExc_RuntimeError
);
608 num_oplock_breaks
= talloc_array_length(self
->oplock_breaks
);
610 if (num_oplock_breaks
== 0) {
611 struct py_tevent_cond cond
;
614 self
->oplock_cond
= &cond
;
615 ret
= py_tevent_cond_wait(&cond
);
616 self
->oplock_cond
= NULL
;
620 PyErr_SetFromErrno(PyExc_RuntimeError
);
625 num_oplock_breaks
= talloc_array_length(self
->oplock_breaks
);
626 if (num_oplock_breaks
> 0) {
629 result
= Py_BuildValue(
631 "fnum", self
->oplock_breaks
[0].fnum
,
632 "level", self
->oplock_breaks
[0].level
);
634 memmove(&self
->oplock_breaks
[0], &self
->oplock_breaks
[1],
635 sizeof(self
->oplock_breaks
[0]) *
636 (num_oplock_breaks
- 1));
637 self
->oplock_breaks
= talloc_realloc(
638 NULL
, self
->oplock_breaks
, struct py_cli_oplock_break
,
639 num_oplock_breaks
- 1);
646 static void py_cli_state_dealloc(struct py_cli_state
*self
)
648 TALLOC_FREE(self
->thread_state
);
649 TALLOC_FREE(self
->oplock_waiter
);
650 TALLOC_FREE(self
->ev
);
652 if (self
->cli
!= NULL
) {
653 cli_shutdown(self
->cli
);
656 Py_TYPE(self
)->tp_free((PyObject
*)self
);
659 static PyObject
*py_cli_create(struct py_cli_state
*self
, PyObject
*args
,
663 unsigned CreateFlags
= 0;
664 unsigned DesiredAccess
= FILE_GENERIC_READ
;
665 unsigned FileAttributes
= 0;
666 unsigned ShareAccess
= 0;
667 unsigned CreateDisposition
= FILE_OPEN
;
668 unsigned CreateOptions
= 0;
669 unsigned ImpersonationLevel
= SMB2_IMPERSONATION_IMPERSONATION
;
670 unsigned SecurityFlags
= 0;
672 struct tevent_req
*req
;
675 static const char *kwlist
[] = {
676 "Name", "CreateFlags", "DesiredAccess", "FileAttributes",
677 "ShareAccess", "CreateDisposition", "CreateOptions",
678 "ImpersonationLevel", "SecurityFlags", NULL
};
680 if (!ParseTupleAndKeywords(
681 args
, kwds
, "s|IIIIIIII", kwlist
,
682 &fname
, &CreateFlags
, &DesiredAccess
, &FileAttributes
,
683 &ShareAccess
, &CreateDisposition
, &CreateOptions
,
684 &ImpersonationLevel
, &SecurityFlags
)) {
688 req
= cli_ntcreate_send(NULL
, self
->ev
, self
->cli
, fname
, CreateFlags
,
689 DesiredAccess
, FileAttributes
, ShareAccess
,
690 CreateDisposition
, CreateOptions
,
691 ImpersonationLevel
, SecurityFlags
);
692 if (!py_tevent_req_wait_exc(self
, req
)) {
695 status
= cli_ntcreate_recv(req
, &fnum
, NULL
);
698 if (!NT_STATUS_IS_OK(status
)) {
699 PyErr_SetNTSTATUS(status
);
702 return Py_BuildValue("I", (unsigned)fnum
);
705 static PyObject
*py_cli_close(struct py_cli_state
*self
, PyObject
*args
)
707 struct tevent_req
*req
;
711 if (!PyArg_ParseTuple(args
, "i", &fnum
)) {
715 req
= cli_close_send(NULL
, self
->ev
, self
->cli
, fnum
);
716 if (!py_tevent_req_wait_exc(self
, req
)) {
719 status
= cli_close_recv(req
);
722 if (!NT_STATUS_IS_OK(status
)) {
723 PyErr_SetNTSTATUS(status
);
729 static PyObject
*py_cli_write(struct py_cli_state
*self
, PyObject
*args
,
736 unsigned long long offset
;
737 struct tevent_req
*req
;
741 static const char *kwlist
[] = {
742 "fnum", "buffer", "offset", "mode", NULL
};
744 if (!ParseTupleAndKeywords(
745 args
, kwds
, "I" PYARG_BYTES_LEN
"K|I", kwlist
,
746 &fnum
, &buf
, &buflen
, &offset
, &mode
)) {
750 req
= cli_write_send(NULL
, self
->ev
, self
->cli
, fnum
, mode
,
751 (uint8_t *)buf
, offset
, buflen
);
752 if (!py_tevent_req_wait_exc(self
, req
)) {
755 status
= cli_write_recv(req
, &written
);
758 if (!NT_STATUS_IS_OK(status
)) {
759 PyErr_SetNTSTATUS(status
);
762 return Py_BuildValue("K", (unsigned long long)written
);
765 static PyObject
*py_cli_read(struct py_cli_state
*self
, PyObject
*args
,
769 unsigned long long offset
;
771 struct tevent_req
*req
;
777 static const char *kwlist
[] = {
778 "fnum", "offset", "size", NULL
};
780 if (!ParseTupleAndKeywords(
781 args
, kwds
, "IKI", kwlist
, &fnum
, &offset
,
786 result
= PyBytes_FromStringAndSize(NULL
, size
);
787 if (result
== NULL
) {
790 buf
= PyBytes_AS_STRING(result
);
792 req
= cli_read_send(NULL
, self
->ev
, self
->cli
, fnum
,
794 if (!py_tevent_req_wait_exc(self
, req
)) {
798 status
= cli_read_recv(req
, &received
);
801 if (!NT_STATUS_IS_OK(status
)) {
803 PyErr_SetNTSTATUS(status
);
807 if (received
> size
) {
809 PyErr_Format(PyExc_IOError
,
810 "read invalid - got %zu requested %u",
815 if (received
< size
) {
816 if (_PyBytes_Resize(&result
, received
) < 0) {
824 static PyObject
*py_cli_ftruncate(struct py_cli_state
*self
, PyObject
*args
,
828 unsigned long long size
;
829 struct tevent_req
*req
;
832 static const char *kwlist
[] = {
833 "fnum", "size", NULL
};
835 if (!ParseTupleAndKeywords(
836 args
, kwds
, "IK", kwlist
, &fnum
, &size
)) {
840 req
= cli_ftruncate_send(NULL
, self
->ev
, self
->cli
, fnum
, size
);
841 if (!py_tevent_req_wait_exc(self
, req
)) {
844 status
= cli_ftruncate_recv(req
);
847 if (!NT_STATUS_IS_OK(status
)) {
848 PyErr_SetNTSTATUS(status
);
854 static PyObject
*py_cli_delete_on_close(struct py_cli_state
*self
,
859 struct tevent_req
*req
;
862 static const char *kwlist
[] = {
863 "fnum", "flag", NULL
};
865 if (!ParseTupleAndKeywords(
866 args
, kwds
, "II", kwlist
, &fnum
, &flag
)) {
870 req
= cli_nt_delete_on_close_send(NULL
, self
->ev
, self
->cli
, fnum
,
872 if (!py_tevent_req_wait_exc(self
, req
)) {
875 status
= cli_nt_delete_on_close_recv(req
);
878 if (!NT_STATUS_IS_OK(status
)) {
879 PyErr_SetNTSTATUS(status
);
885 static PyObject
*py_cli_list(struct py_cli_state
*self
,
891 FILE_ATTRIBUTE_DIRECTORY
|
892 FILE_ATTRIBUTE_SYSTEM
|
893 FILE_ATTRIBUTE_HIDDEN
;
894 unsigned info_level
= SMB_FIND_FILE_BOTH_DIRECTORY_INFO
;
895 struct tevent_req
*req
;
897 struct file_info
*finfos
;
898 size_t i
, num_finfos
;
901 const char *kwlist
[] = {
902 "mask", "attribute", "info_level", NULL
905 if (!ParseTupleAndKeywords(
906 args
, kwds
, "s|II", kwlist
,
907 &mask
, &attribute
, &info_level
)) {
911 req
= cli_list_send(NULL
, self
->ev
, self
->cli
, mask
, attribute
,
913 if (!py_tevent_req_wait_exc(self
, req
)) {
916 status
= cli_list_recv(req
, NULL
, &finfos
, &num_finfos
);
919 if (!NT_STATUS_IS_OK(status
)) {
920 PyErr_SetNTSTATUS(status
);
924 result
= Py_BuildValue("[]");
925 if (result
== NULL
) {
929 for (i
=0; i
<num_finfos
; i
++) {
930 struct file_info
*finfo
= &finfos
[i
];
934 file
= Py_BuildValue(
937 "mode", (int)finfo
->mode
);
943 ret
= PyList_Append(result
, file
);
953 static PyMethodDef py_cli_state_methods
[] = {
954 { "create", (PyCFunction
)py_cli_create
, METH_VARARGS
|METH_KEYWORDS
,
956 { "close", (PyCFunction
)py_cli_close
, METH_VARARGS
,
957 "Close a file handle" },
958 { "write", (PyCFunction
)py_cli_write
, METH_VARARGS
|METH_KEYWORDS
,
959 "Write to a file handle" },
960 { "read", (PyCFunction
)py_cli_read
, METH_VARARGS
|METH_KEYWORDS
,
961 "Read from a file handle" },
962 { "truncate", (PyCFunction
)py_cli_ftruncate
,
963 METH_VARARGS
|METH_KEYWORDS
,
965 { "delete_on_close", (PyCFunction
)py_cli_delete_on_close
,
966 METH_VARARGS
|METH_KEYWORDS
,
967 "Set/Reset the delete on close flag" },
968 { "readdir", (PyCFunction
)py_cli_list
,
969 METH_VARARGS
|METH_KEYWORDS
,
970 "List a directory" },
971 { "get_oplock_break", (PyCFunction
)py_cli_get_oplock_break
,
972 METH_VARARGS
, "Wait for an oplock break" },
973 { NULL
, NULL
, 0, NULL
}
976 static PyTypeObject py_cli_state_type
= {
977 PyVarObject_HEAD_INIT(NULL
, 0)
978 .tp_name
= "libsmb_samba_internal.Conn",
979 .tp_basicsize
= sizeof(struct py_cli_state
),
980 .tp_flags
= Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_BASETYPE
,
981 .tp_doc
= "libsmb connection",
982 .tp_new
= py_cli_state_new
,
983 .tp_init
= (initproc
)py_cli_state_init
,
984 .tp_dealloc
= (destructor
)py_cli_state_dealloc
,
985 .tp_methods
= py_cli_state_methods
,
988 static PyMethodDef py_libsmb_methods
[] = {
992 void initlibsmb_samba_internal(void);
994 static struct PyModuleDef moduledef
= {
995 PyModuleDef_HEAD_INIT
,
996 .m_name
= "libsmb_samba_internal",
997 .m_doc
= "libsmb wrapper",
999 .m_methods
= py_libsmb_methods
,
1002 MODULE_INIT_FUNC(libsmb_samba_internal
)
1006 talloc_stackframe();
1008 m
= PyModule_Create(&moduledef
);
1012 if (PyType_Ready(&py_cli_state_type
) < 0) {
1015 Py_INCREF(&py_cli_state_type
);
1016 PyModule_AddObject(m
, "Conn", (PyObject
*)&py_cli_state_type
);