[SCM] Samba Shared Repository - branch master updated

Günther Deschner gd at samba.org
Thu Apr 21 15:34:01 UTC 2022


The branch, master has been updated
       via  84480a1e21f python/samba/tests: add SMBConfTests suite
       via  d948cb1c6dd lib/smbconf: add an initial set of python bindings
      from  c285bcfbdad lib/cmdline: fix a typo

https://git.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit 84480a1e21f2523a8a1c74a29c90f6c1c7349edf
Author: John Mulligan <jmulligan at redhat.com>
Date:   Fri Apr 1 13:51:40 2022 -0400

    python/samba/tests: add SMBConfTests suite
    
    Add an initial suite of tests for the smbconf python bindings.
    Currently only simple read-only methods are available.
    
    Signed-off-by: John Mulligan <jmulligan at redhat.com>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz
    Reviewed-by: David Mulder <dmulder at suse.com>
    Reviewed-by: Guenther Deschner <gd at samba.org>
    
    Autobuild-User(master): Günther Deschner <gd at samba.org>
    Autobuild-Date(master): Thu Apr 21 15:33:38 UTC 2022 on sn-devel-184

commit d948cb1c6dde87d60792a8dd40c07c6e4f2aa359
Author: John Mulligan <jmulligan at redhat.com>
Date:   Thu Mar 31 16:54:49 2022 -0400

    lib/smbconf: add an initial set of python bindings
    
    The smbconf library provides a generic interface for Samba configuration
    backends. In order to access these backends, including the read-write
    registry backend, we add a new python binding for smbconf - the general
    interface library.
    
    This initial set of bindings covers some basic read-only calls.  This
    includes function calls for listing shares (config sections) and getting
    the parameters of the shares. The `init_txt` construction function must
    be used to get a new SMBConf object.  This is done so that other
    backends, specifically the registry backend from source3 can be used in
    the future. Those will provide their own construction funcs.
    
    Signed-off-by: John Mulligan <jmulligan at redhat.com>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz
    Reviewed-by: David Mulder <dmulder at suse.com>
    Reviewed-by: Guenther Deschner <gd at samba.org>

-----------------------------------------------------------------------

Summary of changes:
 lib/smbconf/pysmbconf.c       | 431 ++++++++++++++++++++++++++++++++++++++++++
 lib/smbconf/wscript_build     |   5 +
 python/samba/tests/smbconf.py | 102 ++++++++++
 selftest/tests.py             |   1 +
 4 files changed, 539 insertions(+)
 create mode 100644 lib/smbconf/pysmbconf.c
 create mode 100644 python/samba/tests/smbconf.py


Changeset truncated at 500 lines:

diff --git a/lib/smbconf/pysmbconf.c b/lib/smbconf/pysmbconf.c
new file mode 100644
index 00000000000..0a5c3a6980c
--- /dev/null
+++ b/lib/smbconf/pysmbconf.c
@@ -0,0 +1,431 @@
+/*
+ *  Unix SMB/CIFS implementation.
+ *  libsmbconf - Samba configuration library - Python bindings
+ *
+ *  Copyright (C) John Mulligan <phlogistonjohn at asynchrono.us> 2022
+ *
+ *  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 "python/py3compat.h"
+
+#include "lib/smbconf/smbconf.h"
+#include "lib/smbconf/smbconf_txt.h"
+
+static PyObject *PyExc_SMBConfError;
+
+typedef struct {
+	PyObject_HEAD
+
+	/* C values embedded in our python type */
+	TALLOC_CTX * mem_ctx;
+	struct smbconf_ctx *conf_ctx;
+} py_SMBConf_Object;
+
+static void py_raise_SMBConfError(sbcErr err)
+{
+	PyObject *v = NULL;
+	PyObject *args = NULL;
+
+	/*
+	 * TODO: have the exception type accept arguments in new/init
+	 * and make the error value accessible as a property
+	 */
+	args = Py_BuildValue("(is)", err, sbcErrorString(err));
+	if (args == NULL) {
+		PyErr_Format(PyExc_SMBConfError, "[%d]: %s", err,
+			     sbcErrorString(err));
+		return;
+	}
+	v = PyObject_Call(PyExc_SMBConfError, args, NULL);
+	Py_DECREF(args);
+	if (v != NULL) {
+		PyErr_SetObject((PyObject *) Py_TYPE(v), v);
+		Py_DECREF(v);
+	}
+}
+
+/*
+ * py_from_smbconf_service returns a python tuple that is basically equivalent
+ * to the struct smbconf_service type content-wise.
+ */
+static PyObject *py_from_smbconf_service(struct smbconf_service *svc)
+{
+	PyObject *plist = PyList_New(svc->num_params);
+	if (plist == NULL) {
+		return NULL;
+	}
+
+	for (uint32_t i = 0; i < svc->num_params; i++) {
+		PyObject *pt = Py_BuildValue("(ss)",
+					     svc->param_names[i],
+					     svc->param_values[i]);
+		if (pt == NULL) {
+			Py_CLEAR(plist);
+			return NULL;
+		}
+		if (PyList_SetItem(plist, i, pt) < 0) {
+			Py_CLEAR(pt);
+			Py_CLEAR(plist);
+			return NULL;
+		}
+	}
+	return Py_BuildValue("(sO)", svc->name, plist);
+}
+
+static PyObject *obj_new(PyTypeObject * type, PyObject * args, PyObject * kwds)
+{
+	py_SMBConf_Object *self = (py_SMBConf_Object *) type->tp_alloc(type, 0);
+	if (self == NULL) {
+		return NULL;
+	}
+
+	self->mem_ctx = talloc_new(NULL);
+	if (self->mem_ctx == NULL) {
+		Py_DECREF(self);
+		return NULL;
+	}
+
+	return (PyObject *) self;
+}
+
+static void obj_dealloc(py_SMBConf_Object * self)
+{
+	if (self->conf_ctx != NULL) {
+		smbconf_shutdown(self->conf_ctx);
+	}
+	talloc_free(self->mem_ctx);
+	Py_TYPE(self)->tp_free((PyObject *) self);
+}
+
+static bool obj_ready(py_SMBConf_Object * self)
+{
+	if (self->conf_ctx == NULL) {
+		PyErr_Format(PyExc_RuntimeError,
+			     "attempt to use an uninitialized SMBConf object");
+		return false;
+	}
+	return true;
+}
+
+static PyObject *obj_requires_messaging(py_SMBConf_Object * self,
+					PyObject * Py_UNUSED(ignored))
+{
+	if (!obj_ready(self)) {
+		return NULL;
+	}
+	if (smbconf_backend_requires_messaging(self->conf_ctx)) {
+		Py_RETURN_TRUE;
+	}
+	Py_RETURN_FALSE;
+}
+
+static PyObject *obj_is_writable(py_SMBConf_Object * self,
+				 PyObject * Py_UNUSED(ignored))
+{
+	if (!obj_ready(self)) {
+		return NULL;
+	}
+	if (smbconf_is_writeable(self->conf_ctx)) {
+		Py_RETURN_TRUE;
+	}
+	Py_RETURN_FALSE;
+}
+
+static PyObject *obj_share_names(py_SMBConf_Object * self,
+				 PyObject * Py_UNUSED(ignored))
+{
+	sbcErr err;
+	uint32_t num_shares;
+	char **share_names = NULL;
+	PyObject *slist = NULL;
+	TALLOC_CTX *mem_ctx = NULL;
+
+	if (!obj_ready(self)) {
+		return NULL;
+	}
+
+	mem_ctx = talloc_new(self->mem_ctx);
+	if (mem_ctx == NULL) {
+		PyErr_NoMemory();
+		return NULL;
+	}
+
+	err =
+	    smbconf_get_share_names(self->conf_ctx, mem_ctx, &num_shares,
+				    &share_names);
+	if (err != SBC_ERR_OK) {
+		talloc_free(mem_ctx);
+		py_raise_SMBConfError(err);
+		return NULL;
+	}
+
+	slist = PyList_New(num_shares);
+	if (slist == NULL) {
+		talloc_free(mem_ctx);
+		return NULL;
+	}
+	for (uint32_t i = 0; i < num_shares; i++) {
+		PyObject *ustr = PyUnicode_FromString(share_names[i]);
+		if (ustr == NULL) {
+			Py_CLEAR(slist);
+			talloc_free(mem_ctx);
+			return NULL;
+		}
+		if (PyList_SetItem(slist, i, ustr) < 0) {
+			Py_CLEAR(ustr);
+			Py_CLEAR(slist);
+			talloc_free(mem_ctx);
+			return NULL;
+		}
+	}
+	talloc_free(mem_ctx);
+	return slist;
+}
+
+static PyObject *obj_get_share(py_SMBConf_Object * self, PyObject * args)
+{
+	sbcErr err;
+	char *servicename = NULL;
+	struct smbconf_service *svc = NULL;
+	PyObject *plist = NULL;
+	TALLOC_CTX *mem_ctx = NULL;
+
+	if (!PyArg_ParseTuple(args, "s", &servicename)) {
+		return NULL;
+	}
+
+	if (!obj_ready(self)) {
+		return NULL;
+	}
+
+	mem_ctx = talloc_new(self->mem_ctx);
+	if (mem_ctx == NULL) {
+		PyErr_NoMemory();
+		return NULL;
+	}
+
+	err = smbconf_get_share(self->conf_ctx, mem_ctx, servicename, &svc);
+	if (err != SBC_ERR_OK) {
+		talloc_free(mem_ctx);
+		py_raise_SMBConfError(err);
+		return NULL;
+	}
+	/*
+	 * if py_from_smbconf_service returns NULL, then an exception should
+	 * already be set. No special error handling needed.
+	 */
+	plist = py_from_smbconf_service(svc);
+	talloc_free(mem_ctx);
+	return plist;
+}
+
+static PyObject *obj_get_config(py_SMBConf_Object * self,
+				PyObject * Py_UNUSED(ignored))
+{
+	sbcErr err;
+	PyObject *svclist = NULL;
+	TALLOC_CTX *mem_ctx = NULL;
+	uint32_t num_shares;
+	struct smbconf_service **svcs = NULL;
+
+	if (!obj_ready(self)) {
+		return NULL;
+	}
+
+	mem_ctx = talloc_new(self->mem_ctx);
+	if (mem_ctx == NULL) {
+		PyErr_NoMemory();
+		return NULL;
+	}
+
+	err = smbconf_get_config(self->conf_ctx, mem_ctx, &num_shares, &svcs);
+	if (err != SBC_ERR_OK) {
+		talloc_free(mem_ctx);
+		py_raise_SMBConfError(err);
+		return NULL;
+	}
+
+	svclist = PyList_New(num_shares);
+	if (svclist == NULL) {
+		talloc_free(mem_ctx);
+		return NULL;
+	}
+	for (uint32_t i = 0; i < num_shares; i++) {
+		PyObject *svcobj = py_from_smbconf_service(svcs[i]);
+		if (svcobj == NULL) {
+			Py_CLEAR(svclist);
+			talloc_free(mem_ctx);
+			return NULL;
+		}
+		if (PyList_SetItem(svclist, i, svcobj) < 0) {
+			Py_CLEAR(svcobj);
+			Py_CLEAR(svclist);
+			talloc_free(mem_ctx);
+			return NULL;
+		}
+	}
+
+	talloc_free(mem_ctx);
+	return svclist;
+}
+
+PyDoc_STRVAR(obj_requires_messaging_doc,
+"requires_messaging() -> bool\n"
+"\n"
+"Returns true if the backend requires interprocess messaging.\n");
+
+PyDoc_STRVAR(obj_is_writable_doc,
+"is_writeable() -> bool\n"
+"\n"
+"Returns true if the SMBConf object's backend is writable.\n");
+
+PyDoc_STRVAR(obj_share_names_doc,
+"share_names() -> list[str]\n"
+"\n"
+"Return a list of the share names currently configured.\n"
+"Includes the global section as a share name.\n");
+
+PyDoc_STRVAR(obj_get_share_doc,
+"get_share() -> (str, list[(str, str)])\n"
+"\n"
+"Given the name of a share, return a tuple of \n"
+"(share_name, share_parms) where share_params is a list of\n"
+"(param_name, param_value) tuples.\n"
+"The term global can be specified to get global section parameters.\n");
+
+PyDoc_STRVAR(obj_get_config_doc,
+"get_config() -> list[(str, list[(str, str)])]\n"
+"Return a list of tuples for every section/share of the current\n"
+"configuration. Each tuple in the list is the same as described\n"
+"for get_share().\n");
+
+static PyMethodDef py_smbconf_obj_methods[] = {
+	{ "requires_messaging", (PyCFunction) obj_requires_messaging,
+	 METH_NOARGS, obj_requires_messaging_doc },
+	{ "is_writeable", (PyCFunction) obj_is_writable, METH_NOARGS,
+	 obj_is_writable_doc },
+	{ "share_names", (PyCFunction) obj_share_names, METH_NOARGS,
+	 obj_share_names_doc },
+	{ "get_share", (PyCFunction) obj_get_share, METH_VARARGS,
+	 obj_get_share_doc },
+	{ "get_config", (PyCFunction) obj_get_config, METH_NOARGS,
+	 obj_get_config_doc },
+	{ 0 },
+};
+
+PyDoc_STRVAR(py_SMBConf_type_doc,
+"SMBConf objects provide uniform access to Samba configuration backends.\n"
+"\n"
+"The SMBConf type should not be instantiated directly. Rather, use a\n"
+"backend specific init function like init_txt.\n");
+
+static PyTypeObject py_SMBConf_Type = {
+	PyVarObject_HEAD_INIT(NULL, 0)
+	.tp_name = "smbconf.SMBConf",
+	.tp_doc = py_SMBConf_type_doc,
+	.tp_basicsize = sizeof(py_SMBConf_Object),
+	.tp_methods = py_smbconf_obj_methods,
+	.tp_new = obj_new,
+	.tp_dealloc = (destructor) obj_dealloc,
+};
+
+static PyObject *py_init_txt(PyObject * module, PyObject * args)
+{
+	py_SMBConf_Object *obj;
+	sbcErr err;
+	char *path = NULL;
+	struct smbconf_ctx *conf_ctx = NULL;
+
+	if (!PyArg_ParseTuple(args, "s", &path)) {
+		return NULL;
+	}
+
+	obj = (py_SMBConf_Object *) obj_new(&py_SMBConf_Type, NULL, NULL);
+	if (obj == NULL) {
+		return NULL;
+	}
+
+	err = smbconf_init_txt(obj->mem_ctx, &conf_ctx, path);
+	if (err != SBC_ERR_OK) {
+		Py_DECREF(obj);
+		py_raise_SMBConfError(err);
+		return NULL;
+	}
+	obj->conf_ctx = conf_ctx;
+	return (PyObject *) obj;
+}
+
+static PyMethodDef pysmbconf_methods[] = {
+	{ "init_txt", (PyCFunction) py_init_txt, METH_VARARGS,
+	 "Return an SMBConf object for the given text config file." },
+	{ 0 },
+};
+
+PyDoc_STRVAR(py_smbconf_doc,
+"The smbconf module is a wrapper for Samba's smbconf library.\n"
+"This library supports common functions to access the contents\n"
+"of a configuration backend, such as the text-based smb.conf file\n"
+"or the read-write registry backend.\n"
+"The read-only functions on the SMBConf type function on both backend\n"
+"types. Future, write based functions need a writable backend (registry).\n"
+"\n"
+"Note that the registry backend will be provided by a different\n"
+"library module from the source3 tree (implemenation TBD).\n");
+
+static struct PyModuleDef moduledef = {
+	PyModuleDef_HEAD_INIT,
+	.m_name = "smbconf",
+	.m_doc = py_smbconf_doc,
+	.m_size = -1,
+	.m_methods = pysmbconf_methods,
+};
+
+MODULE_INIT_FUNC(smbconf)
+{
+	PyObject *m = PyModule_Create(&moduledef);
+	if (m == NULL) {
+		return NULL;
+	}
+
+	if (PyType_Ready(&py_SMBConf_Type) < 0) {
+		Py_DECREF(m);
+		return NULL;
+	}
+	Py_INCREF(&py_SMBConf_Type);
+	if (PyModule_AddObject(m, "SMBConf", (PyObject *) & py_SMBConf_Type) <
+	    0) {
+		Py_DECREF(&py_SMBConf_Type);
+		Py_DECREF(m);
+		return NULL;
+	}
+
+	PyExc_SMBConfError =
+	    PyErr_NewException(discard_const_p(char, "smbconf.SMBConfError"),
+			       NULL, NULL);
+	if (PyExc_SMBConfError == NULL) {
+		Py_DECREF(m);
+		return NULL;
+	}
+	Py_INCREF(PyExc_SMBConfError);
+	if (PyModule_AddObject(m, "SMBConfError", PyExc_SMBConfError) < 0) {
+		Py_DECREF(PyExc_SMBConfError);
+		Py_DECREF(m);
+		return NULL;
+	}
+
+	return m;
+}
diff --git a/lib/smbconf/wscript_build b/lib/smbconf/wscript_build
index 4f9930a1ae0..97d6b18d30e 100644
--- a/lib/smbconf/wscript_build
+++ b/lib/smbconf/wscript_build
@@ -3,3 +3,8 @@ bld.SAMBA_SUBSYSTEM('LIBSMBCONF',
                     deps='talloc sendfile'
                     )
 
+bld.SAMBA3_PYTHON('pysmbconf',
+                  source='pysmbconf.c',
+                  public_deps=' '.join(['samba-util', 'tdb', 'talloc', 'smbconf']),
+                  realname='samba/smbconf.so'
+                  )
diff --git a/python/samba/tests/smbconf.py b/python/samba/tests/smbconf.py
new file mode 100644
index 00000000000..41d9889cf02
--- /dev/null
+++ b/python/samba/tests/smbconf.py
@@ -0,0 +1,102 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer at samba.org> 2007
+# Copyright (C) John Mulligan <phlogistonjohn at asynchrono.us> 2022
+#
+# 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/>.
+#
+
+"""
+Tests for samba.smbconf module
+"""
+
+import samba.tests
+
+
+class SMBConfTests(samba.tests.TestCase):
+    _smbconf = None
+
+    @property
+    def smbconf(self):
+        """Property to access module under test without
+        importing it at test module load-time.
+        """
+        if self._smbconf is not None:
+            return self._smbconf
+
+        import samba.smbconf
+
+        self._smbconf = samba.smbconf
+        return self._smbconf
+
+    @property
+    def example_conf_default(self):
+        return "./testdata/samba3/smb.conf"


-- 
Samba Shared Repository



More information about the samba-cvs mailing list