2 * Unix SMB/CIFS implementation.
3 * libsmbconf - Samba configuration library - Python bindings
5 * Copyright (C) John Mulligan <phlogistonjohn@asynchrono.us> 2022
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/>.
21 #include "lib/replace/system/python.h"
23 #include "python/py3compat.h"
25 #include "lib/smbconf/smbconf.h"
26 #include "lib/smbconf/smbconf_txt.h"
27 #include "lib/smbconf/pysmbconf.h"
29 static PyObject
*PyExc_SMBConfError
;
31 static void py_raise_SMBConfError(sbcErr err
)
34 PyObject
*args
= NULL
;
37 * TODO: have the exception type accept arguments in new/init
39 args
= Py_BuildValue("(is)", err
, sbcErrorString(err
));
41 PyErr_Format(PyExc_SMBConfError
, "[%d]: %s", err
,
45 v
= PyObject_Call(PyExc_SMBConfError
, args
, NULL
);
51 * It's clearer to set an explicit error_code attribute for use in calling
52 * code to check what kind of SMBConfError was raised.
54 if (PyObject_SetAttrString(v
, "error_code", PyTuple_GetItem(args
, 0)) == -1) {
60 PyErr_SetObject((PyObject
*) Py_TYPE(v
), v
);
65 * py_from_smbconf_service returns a python tuple that is basically equivalent
66 * to the struct smbconf_service type content-wise.
68 static PyObject
*py_from_smbconf_service(struct smbconf_service
*svc
)
71 PyObject
*plist
= PyList_New(svc
->num_params
);
76 for (count
= 0; count
< svc
->num_params
; count
++) {
77 PyObject
*pt
= Py_BuildValue("(ss)",
78 svc
->param_names
[count
],
79 svc
->param_values
[count
]);
84 if (PyList_SetItem(plist
, count
, pt
) < 0) {
90 return Py_BuildValue("(sO)", svc
->name
, plist
);
93 static PyObject
*obj_new(PyTypeObject
* type
, PyObject
* args
, PyObject
* kwds
)
95 py_SMBConf_Object
*self
= (py_SMBConf_Object
*) type
->tp_alloc(type
, 0);
100 self
->mem_ctx
= talloc_new(NULL
);
101 if (self
->mem_ctx
== NULL
) {
106 return (PyObject
*) self
;
109 static void obj_dealloc(py_SMBConf_Object
* self
)
111 if (self
->conf_ctx
!= NULL
) {
112 smbconf_shutdown(self
->conf_ctx
);
114 talloc_free(self
->mem_ctx
);
115 Py_TYPE(self
)->tp_free((PyObject
*) self
);
118 static bool obj_ready(py_SMBConf_Object
* self
)
120 if (self
->conf_ctx
== NULL
) {
121 PyErr_Format(PyExc_RuntimeError
,
122 "attempt to use an uninitialized SMBConf object");
128 static PyObject
*obj_requires_messaging(py_SMBConf_Object
* self
,
129 PyObject
* Py_UNUSED(ignored
))
131 if (!obj_ready(self
)) {
134 if (smbconf_backend_requires_messaging(self
->conf_ctx
)) {
140 static PyObject
*obj_is_writable(py_SMBConf_Object
* self
,
141 PyObject
* Py_UNUSED(ignored
))
143 if (!obj_ready(self
)) {
146 if (smbconf_is_writeable(self
->conf_ctx
)) {
152 static PyObject
*obj_share_names(py_SMBConf_Object
* self
,
153 PyObject
* Py_UNUSED(ignored
))
158 char **share_names
= NULL
;
159 PyObject
*slist
= NULL
;
160 TALLOC_CTX
*mem_ctx
= NULL
;
162 if (!obj_ready(self
)) {
166 mem_ctx
= talloc_new(self
->mem_ctx
);
167 if (mem_ctx
== NULL
) {
173 smbconf_get_share_names(self
->conf_ctx
, mem_ctx
, &num_shares
,
175 if (err
!= SBC_ERR_OK
) {
176 talloc_free(mem_ctx
);
177 py_raise_SMBConfError(err
);
181 slist
= PyList_New(num_shares
);
183 talloc_free(mem_ctx
);
186 for (count
= 0; count
< num_shares
; count
++) {
187 PyObject
*ustr
= PyUnicode_FromString(share_names
[count
]);
190 talloc_free(mem_ctx
);
193 if (PyList_SetItem(slist
, count
, ustr
) < 0) {
196 talloc_free(mem_ctx
);
200 talloc_free(mem_ctx
);
204 static PyObject
*obj_get_share(py_SMBConf_Object
* self
, PyObject
* args
)
207 char *servicename
= NULL
;
208 struct smbconf_service
*svc
= NULL
;
209 PyObject
*plist
= NULL
;
210 TALLOC_CTX
*mem_ctx
= NULL
;
212 if (!PyArg_ParseTuple(args
, "s", &servicename
)) {
216 if (!obj_ready(self
)) {
220 mem_ctx
= talloc_new(self
->mem_ctx
);
221 if (mem_ctx
== NULL
) {
226 err
= smbconf_get_share(self
->conf_ctx
, mem_ctx
, servicename
, &svc
);
227 if (err
!= SBC_ERR_OK
) {
228 talloc_free(mem_ctx
);
229 py_raise_SMBConfError(err
);
233 * if py_from_smbconf_service returns NULL, then an exception should
234 * already be set. No special error handling needed.
236 plist
= py_from_smbconf_service(svc
);
237 talloc_free(mem_ctx
);
241 static PyObject
*obj_get_config(py_SMBConf_Object
* self
,
242 PyObject
* Py_UNUSED(ignored
))
245 PyObject
*svclist
= NULL
;
246 TALLOC_CTX
*mem_ctx
= NULL
;
249 struct smbconf_service
**svcs
= NULL
;
251 if (!obj_ready(self
)) {
255 mem_ctx
= talloc_new(self
->mem_ctx
);
256 if (mem_ctx
== NULL
) {
261 err
= smbconf_get_config(self
->conf_ctx
, mem_ctx
, &num_shares
, &svcs
);
262 if (err
!= SBC_ERR_OK
) {
263 talloc_free(mem_ctx
);
264 py_raise_SMBConfError(err
);
268 svclist
= PyList_New(num_shares
);
269 if (svclist
== NULL
) {
270 talloc_free(mem_ctx
);
273 for (count
= 0; count
< num_shares
; count
++) {
274 PyObject
*svcobj
= py_from_smbconf_service(svcs
[count
]);
275 if (svcobj
== NULL
) {
277 talloc_free(mem_ctx
);
280 if (PyList_SetItem(svclist
, count
, svcobj
) < 0) {
283 talloc_free(mem_ctx
);
288 talloc_free(mem_ctx
);
292 static PyObject
*obj_create_share(py_SMBConf_Object
* self
, PyObject
* args
)
295 char *servicename
= NULL
;
297 if (!PyArg_ParseTuple(args
, "s", &servicename
)) {
301 err
= smbconf_create_share(self
->conf_ctx
, servicename
);
302 if (err
!= SBC_ERR_OK
) {
303 py_raise_SMBConfError(err
);
309 static PyObject
*obj_drop(py_SMBConf_Object
* self
,
310 PyObject
* Py_UNUSED(ignored
))
314 err
= smbconf_drop(self
->conf_ctx
);
315 if (err
!= SBC_ERR_OK
) {
316 py_raise_SMBConfError(err
);
322 static PyObject
*obj_set_parameter(py_SMBConf_Object
* self
, PyObject
* args
)
325 char *servicename
= NULL
;
329 if (!PyArg_ParseTuple(args
, "sss", &servicename
, ¶m
, &val
)) {
333 err
= smbconf_set_parameter(self
->conf_ctx
, servicename
, param
, val
);
334 if (err
!= SBC_ERR_OK
) {
335 py_raise_SMBConfError(err
);
341 static PyObject
*obj_set_global_parameter(py_SMBConf_Object
* self
,
348 if (!PyArg_ParseTuple(args
, "ss", ¶m
, &val
)) {
352 err
= smbconf_set_global_parameter(self
->conf_ctx
, param
, val
);
353 if (err
!= SBC_ERR_OK
) {
354 py_raise_SMBConfError(err
);
360 static PyObject
*obj_delete_share(py_SMBConf_Object
* self
, PyObject
* args
)
363 char *servicename
= NULL
;
365 if (!PyArg_ParseTuple(args
, "s", &servicename
)) {
369 err
= smbconf_delete_share(self
->conf_ctx
, servicename
);
370 if (err
!= SBC_ERR_OK
) {
371 py_raise_SMBConfError(err
);
377 static char *py_get_kv_str(TALLOC_CTX
* mem_ctx
, PyObject
* obj
, Py_ssize_t idx
)
380 PyObject
*pystr
= PySequence_GetItem(obj
, idx
);
384 if (!PyUnicode_Check(pystr
)) {
385 PyErr_SetString(PyExc_TypeError
, "keys/values expect a str");
389 ss
= talloc_strdup(mem_ctx
, PyUnicode_AsUTF8(pystr
));
394 static PyObject
*obj_create_set_share(py_SMBConf_Object
* self
, PyObject
* args
)
397 char *servicename
= NULL
;
398 PyObject
*kvs
= NULL
;
399 Py_ssize_t size
, idx
;
400 struct smbconf_service
*tmp_service
= NULL
;
401 TALLOC_CTX
*tmp_ctx
= talloc_new(self
->mem_ctx
);
403 if (!PyArg_ParseTuple(args
, "sO", &servicename
, &kvs
)) {
404 talloc_free(tmp_ctx
);
408 if (PySequence_Check(kvs
) == 0) {
409 PyErr_SetString(PyExc_TypeError
,
410 "a sequence object is required");
411 talloc_free(tmp_ctx
);
415 size
= PySequence_Size(kvs
);
417 PyErr_SetString(PyExc_ValueError
, "failed to get size");
418 talloc_free(tmp_ctx
);
422 tmp_service
= talloc_zero(tmp_ctx
, struct smbconf_service
);
423 if (tmp_service
== NULL
) {
425 talloc_free(tmp_ctx
);
429 tmp_service
->name
= talloc_strdup(tmp_service
, servicename
);
430 if (tmp_service
->name
== NULL
) {
432 talloc_free(tmp_ctx
);
435 tmp_service
->num_params
= (uint32_t) size
;
436 tmp_service
->param_names
= talloc_array(tmp_ctx
, char *, size
);
437 if (tmp_service
->param_names
== NULL
) {
439 talloc_free(tmp_ctx
);
442 tmp_service
->param_values
= talloc_array(tmp_ctx
, char *, size
);
443 if (tmp_service
->param_values
== NULL
) {
445 talloc_free(tmp_ctx
);
449 for (idx
= 0; idx
< size
; idx
++) {
450 char *tmp_str
= NULL
;
451 PyObject
*tmp_pair
= PySequence_GetItem(kvs
, idx
);
452 if (tmp_pair
== NULL
) {
453 talloc_free(tmp_ctx
);
456 if (PySequence_Size(tmp_pair
) != 2) {
457 PyErr_SetString(PyExc_ValueError
,
458 "expecting two-item tuples");
460 talloc_free(tmp_ctx
);
465 tmp_str
= py_get_kv_str(tmp_ctx
, tmp_pair
, 0);
466 if (tmp_str
== NULL
) {
468 talloc_free(tmp_ctx
);
471 tmp_service
->param_names
[idx
] = tmp_str
;
474 tmp_str
= py_get_kv_str(tmp_ctx
, tmp_pair
, 1);
475 if (tmp_str
== NULL
) {
477 talloc_free(tmp_ctx
);
480 tmp_service
->param_values
[idx
] = tmp_str
;
485 err
= smbconf_create_set_share(self
->conf_ctx
, tmp_service
);
486 if (err
!= SBC_ERR_OK
) {
487 py_raise_SMBConfError(err
);
488 talloc_free(tmp_ctx
);
491 talloc_free(tmp_ctx
);
495 static PyObject
*obj_delete_parameter(py_SMBConf_Object
* self
, PyObject
* args
)
498 char *servicename
= NULL
;
499 char *param_name
= NULL
;
501 if (!PyArg_ParseTuple(args
, "ss", &servicename
, ¶m_name
)) {
505 err
= smbconf_delete_parameter(self
->conf_ctx
, servicename
, param_name
);
506 if (err
!= SBC_ERR_OK
) {
507 py_raise_SMBConfError(err
);
513 static PyObject
*obj_delete_global_parameter(py_SMBConf_Object
* self
,
517 char *param_name
= NULL
;
519 if (!PyArg_ParseTuple(args
, "s", ¶m_name
)) {
523 err
= smbconf_delete_global_parameter(self
->conf_ctx
, param_name
);
524 if (err
!= SBC_ERR_OK
) {
525 py_raise_SMBConfError(err
);
531 static PyObject
*obj_transaction_start(py_SMBConf_Object
* self
,
532 PyObject
* Py_UNUSED(ignored
))
534 sbcErr err
= smbconf_transaction_start(self
->conf_ctx
);
535 if (err
!= SBC_ERR_OK
) {
536 py_raise_SMBConfError(err
);
542 static PyObject
*obj_transaction_commit(py_SMBConf_Object
* self
,
543 PyObject
* Py_UNUSED(ignored
))
545 sbcErr err
= smbconf_transaction_commit(self
->conf_ctx
);
546 if (err
!= SBC_ERR_OK
) {
547 py_raise_SMBConfError(err
);
553 static PyObject
*obj_transaction_cancel(py_SMBConf_Object
* self
,
554 PyObject
* Py_UNUSED(ignored
))
556 sbcErr err
= smbconf_transaction_cancel(self
->conf_ctx
);
557 if (err
!= SBC_ERR_OK
) {
558 py_raise_SMBConfError(err
);
564 PyDoc_STRVAR(obj_requires_messaging_doc
,
565 "requires_messaging() -> bool\n"
567 "Returns true if the backend requires interprocess messaging.\n");
569 PyDoc_STRVAR(obj_is_writable_doc
,
570 "is_writeable() -> bool\n"
572 "Returns true if the SMBConf object's backend is writable.\n");
574 PyDoc_STRVAR(obj_share_names_doc
,
575 "share_names() -> list[str]\n"
577 "Return a list of the share names currently configured.\n"
578 "Includes the global section as a share name.\n");
580 PyDoc_STRVAR(obj_get_share_doc
,
581 "get_share() -> (str, list[(str, str)])\n"
583 "Given the name of a share, return a tuple of \n"
584 "(share_name, share_parms) where share_params is a list of\n"
585 "(param_name, param_value) tuples.\n"
586 "The term global can be specified to get global section parameters.\n");
588 PyDoc_STRVAR(obj_get_config_doc
,
589 "get_config() -> list[(str, list[(str, str)])]\n"
590 "Return a list of tuples for every section/share of the current\n"
591 "configuration. Each tuple in the list is the same as described\n"
592 "for get_share().\n");
594 PyDoc_STRVAR(obj_create_share_doc
,
595 "create_share(name: str) -> None\n"
596 "Create a new empty share in the configuration. The share\n"
597 "name must not exist or an error will be raised.\n");
599 PyDoc_STRVAR(obj_drop_doc
,
601 "Drop the entire configuration, resetting it to an empty state.\n");
603 PyDoc_STRVAR(obj_set_parameter_doc
,
604 "set_parameter(str, str, str) -> None\n"
605 "Set a configuration parameter. Specify service name, parameter name,\n"
606 "and parameter value.\n");
608 PyDoc_STRVAR(obj_set_global_parameter_doc
,
609 "set_global_parameter(str, str) -> None\n"
610 "Set a global configuration parameter. Specify the parameter name\n"
611 "and parameter value.\n");
613 PyDoc_STRVAR(obj_delete_share_doc
,
614 "delete_share(str) -> None\n"
615 "Delete a service from the configuration.\n");
617 PyDoc_STRVAR(obj_create_set_share_doc
,
618 "create_set_share(str, [(str, str)...]) -> None\n"
619 "Create and set the definition of a service.\n");
621 PyDoc_STRVAR(obj_delete_parameter_doc
,
622 "delete_parameter(str, str) -> None\n"
623 "Delete a single configuration parameter.\n");
625 PyDoc_STRVAR(obj_delete_global_parameter_doc
,
626 "delete_parameter(str, str) -> None\n"
627 "Delete a single global configuration parameter.\n");
629 PyDoc_STRVAR(obj_transaction_start_doc
,
630 "transaction_start() -> None\n"
631 "Start a transaction.\n"
632 "Transactions allow making compound sets of changes atomically.\n");
634 PyDoc_STRVAR(obj_transaction_commit_doc
,
635 "transaction_commit() -> None\n"
636 "Commit the transaction.\n");
638 PyDoc_STRVAR(obj_transaction_cancel_doc
,
639 "transaction_cancel() -> None\n"
640 "Cancel the transaction.\n");
642 static PyMethodDef py_smbconf_obj_methods
[] = {
643 { "requires_messaging", (PyCFunction
) obj_requires_messaging
,
644 METH_NOARGS
, obj_requires_messaging_doc
},
645 { "is_writeable", (PyCFunction
) obj_is_writable
, METH_NOARGS
,
646 obj_is_writable_doc
},
647 { "share_names", (PyCFunction
) obj_share_names
, METH_NOARGS
,
648 obj_share_names_doc
},
649 { "get_share", (PyCFunction
) obj_get_share
, METH_VARARGS
,
651 { "get_config", (PyCFunction
) obj_get_config
, METH_NOARGS
,
652 obj_get_config_doc
},
653 { "create_share", (PyCFunction
) obj_create_share
, METH_VARARGS
,
654 obj_create_share_doc
},
655 { "create_set_share", (PyCFunction
) obj_create_set_share
, METH_VARARGS
,
656 obj_create_set_share_doc
},
657 { "drop", (PyCFunction
) obj_drop
, METH_NOARGS
,
659 { "set_parameter", (PyCFunction
) obj_set_parameter
, METH_VARARGS
,
660 obj_set_parameter_doc
},
661 { "set_global_parameter", (PyCFunction
) obj_set_global_parameter
,
662 METH_VARARGS
, obj_set_global_parameter_doc
},
663 { "delete_share", (PyCFunction
) obj_delete_share
, METH_VARARGS
,
664 obj_delete_share_doc
},
665 { "delete_parameter", (PyCFunction
) obj_delete_parameter
, METH_VARARGS
,
666 obj_delete_parameter_doc
},
667 { "delete_global_parameter", (PyCFunction
) obj_delete_global_parameter
,
668 METH_VARARGS
, obj_delete_global_parameter_doc
},
669 { "transaction_start", (PyCFunction
) obj_transaction_start
, METH_NOARGS
,
670 obj_transaction_start_doc
},
671 { "transaction_commit", (PyCFunction
) obj_transaction_commit
,
672 METH_NOARGS
, obj_transaction_commit_doc
},
673 { "transaction_cancel", (PyCFunction
) obj_transaction_cancel
,
674 METH_NOARGS
, obj_transaction_cancel_doc
},
678 PyDoc_STRVAR(py_SMBConf_type_doc
,
679 "SMBConf objects provide uniform access to Samba configuration backends.\n"
681 "The SMBConf type should not be instantiated directly. Rather, use a\n"
682 "backend specific init function like init_txt.\n");
684 static PyTypeObject py_SMBConf_Type
= {
685 PyVarObject_HEAD_INIT(NULL
, 0)
686 .tp_name
= "smbconf.SMBConf",
687 .tp_doc
= py_SMBConf_type_doc
,
688 .tp_basicsize
= sizeof(py_SMBConf_Object
),
689 .tp_methods
= py_smbconf_obj_methods
,
691 .tp_dealloc
= (destructor
) obj_dealloc
,
694 static PyObject
*py_init_txt(PyObject
* module
, PyObject
* args
)
696 py_SMBConf_Object
*obj
;
699 struct smbconf_ctx
*conf_ctx
= NULL
;
701 if (!PyArg_ParseTuple(args
, "s", &path
)) {
705 obj
= (py_SMBConf_Object
*) obj_new(&py_SMBConf_Type
, NULL
, NULL
);
710 err
= smbconf_init_txt(obj
->mem_ctx
, &conf_ctx
, path
);
711 if (err
!= SBC_ERR_OK
) {
713 py_raise_SMBConfError(err
);
716 obj
->conf_ctx
= conf_ctx
;
717 return (PyObject
*) obj
;
720 static PyObject
*py_smbconf_error(PyObject
* module
, PyObject
* args
)
724 if (!PyArg_ParseTuple(args
, "i", &errcode
)) {
728 /* this always raises an exception. it doesn't return the exception. */
729 py_raise_SMBConfError(errcode
);
733 static PyMethodDef pysmbconf_methods
[] = {
734 { "init_txt", (PyCFunction
) py_init_txt
, METH_VARARGS
,
735 "Return an SMBConf object for the given text config file." },
736 { "_smbconf_error", (PyCFunction
) py_smbconf_error
, METH_VARARGS
,
737 "Raise an SMBConfError based on the given error code." },
741 PyDoc_STRVAR(py_smbconf_doc
,
742 "The smbconf module is a wrapper for Samba's smbconf library.\n"
743 "This library supports common functions to access the contents\n"
744 "of a configuration backend, such as the text-based smb.conf file\n"
745 "or the read-write registry backend.\n"
746 "The read-only functions on the SMBConf type function on both backend\n"
747 "types. Future, write based functions need a writable backend (registry).\n"
749 "Note that the registry backend will be provided by a different\n"
750 "library module from the source3 tree (implementation TBD).\n");
752 static struct PyModuleDef moduledef
= {
753 PyModuleDef_HEAD_INIT
,
755 .m_doc
= py_smbconf_doc
,
757 .m_methods
= pysmbconf_methods
,
760 MODULE_INIT_FUNC(smbconf
)
762 PyObject
*m
= PyModule_Create(&moduledef
);
767 if (PyType_Ready(&py_SMBConf_Type
) < 0) {
771 Py_INCREF(&py_SMBConf_Type
);
772 if (PyModule_AddObject(m
, "SMBConf", (PyObject
*) & py_SMBConf_Type
) <
774 Py_DECREF(&py_SMBConf_Type
);
780 PyErr_NewException(discard_const_p(char, "smbconf.SMBConfError"),
782 if (PyExc_SMBConfError
== NULL
) {
786 Py_INCREF(PyExc_SMBConfError
);
787 if (PyModule_AddObject(m
, "SMBConfError", PyExc_SMBConfError
) < 0) {
788 Py_DECREF(PyExc_SMBConfError
);
794 * ADD_FLAGS macro borrowed from source3/libsmb/pylibsmb.c
796 #define ADD_FLAGS(val) PyModule_AddObject(m, #val, PyLong_FromLong(val))
798 ADD_FLAGS(SBC_ERR_OK
);
799 ADD_FLAGS(SBC_ERR_NOT_IMPLEMENTED
);
800 ADD_FLAGS(SBC_ERR_NOT_SUPPORTED
);
801 ADD_FLAGS(SBC_ERR_UNKNOWN_FAILURE
);
802 ADD_FLAGS(SBC_ERR_NOMEM
);
803 ADD_FLAGS(SBC_ERR_INVALID_PARAM
);
804 ADD_FLAGS(SBC_ERR_BADFILE
);
805 ADD_FLAGS(SBC_ERR_NO_SUCH_SERVICE
);
806 ADD_FLAGS(SBC_ERR_IO_FAILURE
);
807 ADD_FLAGS(SBC_ERR_CAN_NOT_COMPLETE
);
808 ADD_FLAGS(SBC_ERR_NO_MORE_ITEMS
);
809 ADD_FLAGS(SBC_ERR_FILE_EXISTS
);
810 ADD_FLAGS(SBC_ERR_ACCESS_DENIED
);